Bug 807168 - Make JSTracer into a real C++ class and kill the macro accessors; r=jonco,mccr8
authorTerrence Cole <terrence@mozilla.com>
Fri, 11 Apr 2014 15:19:05 -0700
changeset 198866 cf31dec6639a580e726fa70829e8d05f3dfcd805
parent 198865 b91748a1b6daeb6489769ddd370406dedf284954
child 198867 c26b2de39a80bb5be69481136dadb2691f37ee74
push id486
push userasasaki@mozilla.com
push dateMon, 14 Jul 2014 18:39:42 +0000
treeherdermozilla-release@d33428174ff1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco, mccr8
bugs807168
milestone31.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 807168 - Make JSTracer into a real C++ class and kill the macro accessors; r=jonco,mccr8
content/base/src/nsCCUncollectableMarker.h
content/xul/content/src/nsXULElement.h
content/xul/document/src/XULDocument.h
content/xul/document/src/nsXULPrototypeCache.cpp
content/xul/document/src/nsXULPrototypeDocument.h
dom/base/nsWrapperCache.h
dom/base/nsWrapperCacheInlines.h
dom/bindings/TypedArray.h
js/public/Tracer.h
js/public/TracingAPI.h
js/src/builtin/MapObject.cpp
js/src/builtin/TestingFunctions.cpp
js/src/gc/Iteration.cpp
js/src/gc/Marking.cpp
js/src/gc/Nursery.cpp
js/src/gc/RootMarking.cpp
js/src/gc/StoreBuffer.cpp
js/src/gc/StoreBuffer.h
js/src/gc/Tracer.cpp
js/src/gc/Tracer.h
js/src/gc/Verifier.cpp
js/src/jit/Ion.cpp
js/src/jit/IonFrames.cpp
js/src/jsapi-tests/testIntTypesABI.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsatom.cpp
js/src/jscompartment.cpp
js/src/jsfriendapi.cpp
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinfer.cpp
js/src/jsobj.cpp
js/src/jsproxy.cpp
js/src/jspubtd.h
js/src/jsscript.cpp
js/src/jsweakmap.h
js/src/moz.build
js/src/shell/jsheaptools.cpp
js/src/vm/ArrayBufferObject.cpp
js/src/vm/Debugger.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/ScopeObject.cpp
js/xpconnect/src/XPCVariant.cpp
js/xpconnect/src/XPCWrappedJS.cpp
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
xpcom/base/CycleCollectedJSRuntime.cpp
xpcom/glue/tests/gtest/TestGCPostBarriers.cpp
--- a/content/base/src/nsCCUncollectableMarker.h
+++ b/content/base/src/nsCCUncollectableMarker.h
@@ -1,20 +1,19 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsCCUncollectableMarker_h_
 #define nsCCUncollectableMarker_h_
 
-#include "nsIObserver.h"
+#include "js/TracingAPI.h"
 #include "mozilla/Attributes.h"
-
-struct JSTracer;
+#include "nsIObserver.h"
 
 class nsCCUncollectableMarker MOZ_FINAL : public nsIObserver
 {
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   /**
    * Inits a global nsCCUncollectableMarker. Should only be called once.
--- a/content/xul/content/src/nsXULElement.h
+++ b/content/xul/content/src/nsXULElement.h
@@ -7,17 +7,17 @@
 
   The base XUL element class and associates.
 
 */
 
 #ifndef nsXULElement_h__
 #define nsXULElement_h__
 
-#include "js/Tracer.h"
+#include "js/TracingAPI.h"
 #include "mozilla/Attributes.h"
 #include "nsIDOMEvent.h"
 #include "nsIServiceManager.h"
 #include "nsIAtom.h"
 #include "nsINodeInfo.h"
 #include "nsIControllers.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMXULElement.h"
--- a/content/xul/document/src/XULDocument.h
+++ b/content/xul/document/src/XULDocument.h
@@ -20,16 +20,17 @@
 #include "nsIURI.h"
 #include "nsIXULDocument.h"
 #include "nsScriptLoader.h"
 #include "nsIStreamListener.h"
 #include "nsICSSLoaderObserver.h"
 
 #include "mozilla/Attributes.h"
 
+#include "js/TracingAPI.h"
 #include "js/TypeDecls.h"
 
 class nsIRDFResource;
 class nsIRDFService;
 class nsPIWindowRoot;
 #if 0 // XXXbe save me, scc (need NSCAP_FORWARD_DECL(nsXULPrototypeScript))
 class nsIObjectInputStream;
 class nsIObjectOutputStream;
@@ -37,17 +38,16 @@ class nsIXULPrototypeScript;
 #else
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsXULElement.h"
 #endif
 #include "nsURIHashKey.h"
 #include "nsInterfaceHashtable.h"
 
-struct JSTracer;
 struct PRLogModuleInfo;
 
 class nsRefMapEntry : public nsStringHashKey
 {
 public:
   nsRefMapEntry(const nsAString& aKey) :
     nsStringHashKey(&aKey)
   {
--- a/content/xul/document/src/nsXULPrototypeCache.cpp
+++ b/content/xul/document/src/nsXULPrototypeCache.cpp
@@ -17,17 +17,17 @@
 #include "nsIObjectOutputStream.h"
 #include "nsIObserverService.h"
 #include "nsIStringStream.h"
 #include "nsIStorageStream.h"
 
 #include "nsNetUtil.h"
 #include "nsAppDirectoryServiceDefs.h"
 
-#include "js/Tracer.h"
+#include "js/TracingAPI.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/scache/StartupCache.h"
 #include "mozilla/scache/StartupCacheUtils.h"
 #include "mozilla/Telemetry.h"
 
 using namespace mozilla;
 using namespace mozilla::scache;
--- a/content/xul/document/src/nsXULPrototypeDocument.h
+++ b/content/xul/document/src/nsXULPrototypeDocument.h
@@ -1,32 +1,32 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsXULPrototypeDocument_h__
 #define nsXULPrototypeDocument_h__
 
+#include "js/TracingAPI.h"
 #include "mozilla/Attributes.h"
 #include "nsAutoPtr.h"
 #include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "nsISerializable.h"
 #include "nsCycleCollectionParticipant.h"
 
 class nsIAtom;
 class nsIPrincipal;
 class nsIURI;
 class nsNodeInfoManager;
 class nsXULPrototypeElement;
 class nsXULPrototypePI;
 class nsXULPDGlobalObject;
-struct JSTracer;
 
 namespace mozilla {
 namespace dom {
 class XULDocument;
 } // namespace dom
 } // namespace mozilla
 
 /**
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -6,17 +6,17 @@
 #ifndef nsWrapperCache_h___
 #define nsWrapperCache_h___
 
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Assertions.h"
 #include "js/Id.h"          // must come before js/RootingAPI.h
 #include "js/Value.h"       // must come before js/RootingAPI.h
 #include "js/RootingAPI.h"
-#include "js/Tracer.h"
+#include "js/TracingAPI.h"
 
 class XPCWrappedNativeScope;
 
 #define NS_WRAPPERCACHE_IID \
 { 0x6f3179a1, 0x36f7, 0x4a5c, \
   { 0x8c, 0xf1, 0xad, 0xc8, 0x7c, 0xde, 0x3e, 0x87 } }
 
 /**
--- a/dom/base/nsWrapperCacheInlines.h
+++ b/dom/base/nsWrapperCacheInlines.h
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsWrapperCacheInline_h___
 #define nsWrapperCacheInline_h___
 
 #include "nsWrapperCache.h"
 #include "js/GCAPI.h"
-#include "js/Tracer.h"
+#include "js/TracingAPI.h"
 
 inline JSObject*
 nsWrapperCache::GetWrapper() const
 {
     JSObject* obj = GetWrapperPreserveColor();
     if (obj) {
       JS::ExposeObjectToActiveJS(obj);
     }
--- a/dom/bindings/TypedArray.h
+++ b/dom/bindings/TypedArray.h
@@ -5,17 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_TypedArray_h
 #define mozilla_dom_TypedArray_h
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/RootingAPI.h"
-#include "js/Tracer.h"
+#include "js/TracingAPI.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
 /*
rename from js/public/Tracer.h
rename to js/public/TracingAPI.h
--- a/js/public/Tracer.h
+++ b/js/public/TracingAPI.h
@@ -1,22 +1,22 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef js_Tracer_h
-#define js_Tracer_h
+#ifndef js_TracingAPI_h
+#define js_TracingAPI_h
 
 #include "mozilla/NullPtr.h"
- 
+
 #include "jspubtd.h"
 
-struct JSTracer;
+class JS_PUBLIC_API(JSTracer);
 
 namespace JS {
 template <typename T> class Heap;
 template <typename T> class TenuredHeap;
 }
 
 // Tracer callback, called for each traceable thing directly referenced by a
 // particular object or runtime structure. It is the callback responsibility
@@ -41,85 +41,112 @@ typedef void
 (* JSTraceNamePrinter)(JSTracer *trc, char *buf, size_t bufsize);
 
 enum WeakMapTraceKind {
     DoNotTraceWeakMaps = 0,
     TraceWeakMapValues = 1,
     TraceWeakMapKeysValues = 2
 };
 
-struct JSTracer {
-    JSRuntime           *runtime;
+class JS_PUBLIC_API(JSTracer)
+{
+  public:
+    JSTracer(JSRuntime *rt, JSTraceCallback traceCallback,
+             WeakMapTraceKind weakTraceKind = TraceWeakMapValues);
+
+    // Set debugging information about a reference to a traceable thing to prepare
+    // for the following call to JS_CallTracer.
+    //
+    // When printer is null, arg must be const char * or char * C string naming
+    // the reference and index must be either (size_t)-1 indicating that the name
+    // alone describes the reference or it must be an index into some array vector
+    // that stores the reference.
+    //
+    // When printer callback is not null, the arg and index arguments are
+    // available to the callback as debugPrintArg_ and debugPrintIndex_ fields
+    // of JSTracer.
+    //
+    // The storage for name or callback's arguments needs to live only until
+    // the following call to JS_CallTracer returns.
+    void setTracingDetails(JSTraceNamePrinter printer, const void *arg, size_t index) {
+        debugPrinter_ = printer;
+        debugPrintArg_ = arg;
+        debugPrintIndex_ = index;
+    }
+
+    void setTracingIndex(const char *name, size_t index) {
+        setTracingDetails(nullptr, (void *)name, index);
+    }
+
+    void setTracingName(const char *name) {
+        setTracingDetails(nullptr, (void *)name, size_t(-1));
+    }
+
+    // Remove the currently set tracing details.
+    void clearTracingDetails() {
+        debugPrinter_ = nullptr;
+        debugPrintArg_ = nullptr;
+    }
+
+    // Return true if tracing details are currently set.
+    bool hasTracingDetails() const;
+
+    // Get the string set with the most recent call to setTracingName or return
+    // fallback if a name printer function has been installed.
+    const char *tracingName(const char *fallback) const;
+
+    // Build a description of this edge in the heap graph. This call may invoke
+    // the debug printer, which may inspect arbitrary areas of the heap.
+    const char *getTracingEdgeName(char *buffer, size_t bufferSize);
+
+    // Access the currently active tracing details.
+    JSTraceNamePrinter debugPrinter() const;
+    const void *debugPrintArg() const;
+    size_t debugPrintIndex() const;
+
+    // Return the runtime set on the tracer.
+    JSRuntime *runtime() const { return runtime_; }
+
+    // Return the weak map tracing behavior set on this tracer.
+    WeakMapTraceKind eagerlyTraceWeakMaps() const { return eagerlyTraceWeakMaps_; }
+
+    // Update the trace callback.
+    void setTraceCallback(JSTraceCallback traceCallback);
+
+#ifdef JS_GC_ZEAL
+    // Sets the "real" location for a marked reference, when passing the address
+    // directly is not feasable. This address is used for matching against the
+    // store buffer when verifying the correctness of the entrees there.
+    //
+    // This is currently complicated by our need to nest calls for Values
+    // stored as keys in hash tables.
+    void setTracingLocation(void *location);
+    void unsetTracingLocation();
+    void **tracingLocation(void **thingp);
+#else
+    void setTracingLocation(void *location) {}
+    void unsetTracingLocation() {}
+    void **tracingLocation(void **thingp) { return nullptr; }
+#endif
+
+    // We expose |callback| directly so that IS_GC_MARKING_TRACER can compare
+    // it to GCMarker::GrayCallback.
     JSTraceCallback     callback;
-    JSTraceNamePrinter  debugPrinter;
-    const void          *debugPrintArg;
-    size_t              debugPrintIndex;
-    WeakMapTraceKind    eagerlyTraceWeakMaps;
+
+  private:
+    JSRuntime           *runtime_;
+    JSTraceNamePrinter  debugPrinter_;
+    const void          *debugPrintArg_;
+    size_t              debugPrintIndex_;
+    WeakMapTraceKind    eagerlyTraceWeakMaps_;
 #ifdef JS_GC_ZEAL
-    void                *realLocation;
+    void                *realLocation_;
 #endif
 };
 
-// Set debugging information about a reference to a traceable thing to prepare
-// for the following call to JS_CallTracer.
-//
-// When printer is null, arg must be const char * or char * C string naming
-// the reference and index must be either (size_t)-1 indicating that the name
-// alone describes the reference or it must be an index into some array vector
-// that stores the reference.
-//
-// When printer callback is not null, the arg and index arguments are
-// available to the callback as debugPrintArg and debugPrintIndex fields
-// of JSTracer.
-//
-// The storage for name or callback's arguments needs to live only until
-// the following call to JS_CallTracer returns.
-//
-# define JS_SET_TRACING_DETAILS(trc, printer, arg, index)                     \
-    JS_BEGIN_MACRO                                                            \
-        (trc)->debugPrinter = (printer);                                      \
-        (trc)->debugPrintArg = (arg);                                         \
-        (trc)->debugPrintIndex = (index);                                     \
-    JS_END_MACRO
-
-// Sets the real location for a marked reference, when passing the address
-// directly is not feasable.
-//
-// FIXME: This is currently overcomplicated by our need to nest calls for Values
-// stored as keys in hash tables, but will get simplified once we can rekey
-// in-place.
-//
-#ifdef JS_GC_ZEAL
-# define JS_SET_TRACING_LOCATION(trc, location)                               \
-    JS_BEGIN_MACRO                                                            \
-        if (!(trc)->realLocation || !(location))                              \
-            (trc)->realLocation = (location);                                 \
-    JS_END_MACRO
-# define JS_UNSET_TRACING_LOCATION(trc)                                       \
-    JS_BEGIN_MACRO                                                            \
-        (trc)->realLocation = nullptr;                                        \
-    JS_END_MACRO
-#else
-# define JS_SET_TRACING_LOCATION(trc, location)                               \
-    JS_BEGIN_MACRO                                                            \
-    JS_END_MACRO
-# define JS_UNSET_TRACING_LOCATION(trc)                                       \
-    JS_BEGIN_MACRO                                                            \
-    JS_END_MACRO
-#endif
-
-// Convenience macro to describe the argument of JS_CallTracer using C string
-// and index.
-# define JS_SET_TRACING_INDEX(trc, name, index)                               \
-    JS_SET_TRACING_DETAILS(trc, nullptr, name, index)
-
-// Convenience macro to describe the argument of JS_CallTracer using C string.
-# define JS_SET_TRACING_NAME(trc, name)                                       \
-    JS_SET_TRACING_DETAILS(trc, nullptr, name, (size_t)-1)
-
 // The JS_Call*Tracer family of functions traces the given GC thing reference.
 // This performs the tracing action configured on the given JSTracer:
 // typically calling the JSTracer::callback or marking the thing as live.
 //
 // The argument to JS_Call*Tracer is an in-out param: when the function
 // returns, the garbage collector might have moved the GC thing. In this case,
 // the reference passed to JS_Call*Tracer will be updated to the object's new
 // location. Callers of this method are responsible for updating any state
@@ -160,37 +187,30 @@ JS_CallHeapScriptTracer(JSTracer *trc, J
 extern JS_PUBLIC_API(void)
 JS_CallHeapFunctionTracer(JSTracer *trc, JS::Heap<JSFunction *> *funp, const char *name);
 
 template <typename HashSetEnum>
 inline void
 JS_CallHashSetObjectTracer(JSTracer *trc, HashSetEnum &e, JSObject *const &key, const char *name)
 {
     JSObject *updated = key;
-    JS_SET_TRACING_LOCATION(trc, reinterpret_cast<void *>(&const_cast<JSObject *&>(key)));
+    trc->setTracingLocation(reinterpret_cast<void *>(&const_cast<JSObject *&>(key)));
     JS_CallObjectTracer(trc, &updated, name);
     if (updated != key)
         e.rekeyFront(key, updated);
 }
 
 // Trace an object that is known to always be tenured.  No post barriers are
 // required in this case.
 extern JS_PUBLIC_API(void)
 JS_CallTenuredObjectTracer(JSTracer *trc, JS::TenuredHeap<JSObject *> *objp, const char *name);
 
-// API for JSTraceCallback implementations.
-extern JS_PUBLIC_API(void)
-JS_TracerInit(JSTracer *trc, JSRuntime *rt, JSTraceCallback callback);
-
 extern JS_PUBLIC_API(void)
 JS_TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind);
 
 extern JS_PUBLIC_API(void)
 JS_TraceRuntime(JSTracer *trc);
 
 extern JS_PUBLIC_API(void)
 JS_GetTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc,
                      void *thing, JSGCTraceKind kind, bool includeDetails);
 
-extern JS_PUBLIC_API(const char *)
-JS_GetTraceEdgeName(JSTracer *trc, char *buffer, int bufferSize);
-
-#endif /* js_Tracer_h */
+#endif /* js_TracingAPI_h */
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -824,17 +824,17 @@ HashableValue::operator==(const Hashable
 #endif
     return b;
 }
 
 HashableValue
 HashableValue::mark(JSTracer *trc) const
 {
     HashableValue hv(*this);
-    JS_SET_TRACING_LOCATION(trc, (void *)this);
+    trc->setTracingLocation((void *)this);
     gc::MarkValue(trc, &hv.value, "key");
     return hv;
 }
 
 
 /*** MapIterator *********************************************************************************/
 
 namespace {
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -679,30 +679,34 @@ NondeterministicGetWeakMapKeys(JSContext
 struct JSCountHeapNode {
     void                *thing;
     JSGCTraceKind       kind;
     JSCountHeapNode     *next;
 };
 
 typedef HashSet<void *, PointerHasher<void *, 3>, SystemAllocPolicy> VisitedSet;
 
-typedef struct JSCountHeapTracer {
+class CountHeapTracer
+{
+  public:
+    CountHeapTracer(JSRuntime *rt, JSTraceCallback callback) : base(rt, callback) {}
+
     JSTracer            base;
     VisitedSet          visited;
     JSCountHeapNode     *traceList;
     JSCountHeapNode     *recycleList;
     bool                ok;
-} JSCountHeapTracer;
+};
 
 static void
 CountHeapNotify(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
     JS_ASSERT(trc->callback == CountHeapNotify);
 
-    JSCountHeapTracer *countTracer = (JSCountHeapTracer *)trc;
+    CountHeapTracer *countTracer = (CountHeapTracer *)trc;
     void *thing = *thingp;
 
     if (!countTracer->ok)
         return;
 
     VisitedSet::AddPtr p = countTracer->visited.lookupForAdd(thing);
     if (p)
         return;
@@ -788,18 +792,17 @@ CountHeap(JSContext *cx, unsigned argc, 
                     if (!!bytes)
                         JS_ReportError(cx, "trace kind name '%s' is unknown", bytes.ptr());
                     return false;
                 }
             }
         }
     }
 
-    JSCountHeapTracer countTracer;
-    JS_TracerInit(&countTracer.base, JS_GetRuntime(cx), CountHeapNotify);
+    CountHeapTracer countTracer(JS_GetRuntime(cx), CountHeapNotify);
     if (!countTracer.visited.init()) {
         JS_ReportOutOfMemory(cx);
         return false;
     }
     countTracer.ok = true;
     countTracer.traceList = nullptr;
     countTracer.recycleList = nullptr;
 
--- a/js/src/gc/Iteration.cpp
+++ b/js/src/gc/Iteration.cpp
@@ -17,18 +17,18 @@
 using namespace js;
 using namespace js::gc;
 
 void
 js::TraceRuntime(JSTracer *trc)
 {
     JS_ASSERT(!IS_GC_MARKING_TRACER(trc));
 
-    MinorGC(trc->runtime, JS::gcreason::EVICT_NURSERY);
-    AutoPrepareForTracing prep(trc->runtime, WithAtoms);
+    MinorGC(trc->runtime(), JS::gcreason::EVICT_NURSERY);
+    AutoPrepareForTracing prep(trc->runtime(), WithAtoms);
     MarkRuntime(trc);
 }
 
 static void
 IterateCompartmentsArenasCells(JSRuntime *rt, Zone *zone, void *data,
                                JSIterateCompartmentCallback compartmentCallback,
                                IterateArenaCallback arenaCallback,
                                IterateCellCallback cellCallback)
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -148,31 +148,31 @@ template<typename T>
 static inline void
 CheckMarkedThing(JSTracer *trc, T *thing)
 {
 #ifdef DEBUG
     JS_ASSERT(trc);
     JS_ASSERT(thing);
 
     /* This function uses data that's not available in the nursery. */
-    if (IsInsideNursery(trc->runtime, thing))
+    if (IsInsideNursery(trc->runtime(), thing))
         return;
 
     /*
      * Permanent atoms are not associated with this runtime, but will be ignored
      * during marking.
      */
     if (ThingIsPermanentAtom(thing))
         return;
 
     JS_ASSERT(thing->zone());
-    JS_ASSERT(thing->zone()->runtimeFromMainThread() == trc->runtime);
-    JS_ASSERT(trc->debugPrinter || trc->debugPrintArg);
+    JS_ASSERT(thing->zone()->runtimeFromMainThread() == trc->runtime());
+    JS_ASSERT(trc->hasTracingDetails());
 
-    DebugOnly<JSRuntime *> rt = trc->runtime;
+    DebugOnly<JSRuntime *> rt = trc->runtime();
 
     JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc) && rt->gcManipulatingDeadZones,
                  !thing->zone()->scheduledForDestruction);
 
     JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
     JS_ASSERT_IF(thing->zone()->requireGCTracer(),
                  IS_GC_MARKING_TRACER(trc));
@@ -212,17 +212,17 @@ MarkInternal(JSTracer *trc, T **thingp)
 
     if (!trc->callback) {
         /*
          * We may mark a Nursery thing outside the context of the
          * MinorCollectionTracer because of a pre-barrier. The pre-barrier is
          * not needed in this case because we perform a minor collection before
          * each incremental slice.
          */
-        if (IsInsideNursery(trc->runtime, thing))
+        if (IsInsideNursery(trc->runtime(), thing))
             return;
 
         /*
          * Don't mark permanent atoms, as they may be associated with another
          * runtime. Note that PushMarkStack() also checks this, but the tests
          * and maybeAlive write below should only be done on the main thread.
          */
         if (ThingIsPermanentAtom(thing))
@@ -234,103 +234,101 @@ MarkInternal(JSTracer *trc, T **thingp)
          */
         if (!thing->zone()->isGCMarking())
             return;
 
         PushMarkStack(AsGCMarker(trc), thing);
         thing->zone()->maybeAlive = true;
     } else {
         trc->callback(trc, (void **)thingp, MapTypeToTraceKind<T>::kind);
-        JS_UNSET_TRACING_LOCATION(trc);
+        trc->unsetTracingLocation();
     }
 
-    trc->debugPrinter = nullptr;
-    trc->debugPrintArg = nullptr;
+    trc->clearTracingDetails();
 }
 
 #define JS_ROOT_MARKING_ASSERT(trc)                                     \
     JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc),                             \
-                 trc->runtime->gcIncrementalState == NO_INCREMENTAL ||  \
-                 trc->runtime->gcIncrementalState == MARK_ROOTS);
+                 trc->runtime()->gcIncrementalState == NO_INCREMENTAL ||  \
+                 trc->runtime()->gcIncrementalState == MARK_ROOTS);
 
 namespace js {
 namespace gc {
 
 template <typename T>
 void
 MarkUnbarriered(JSTracer *trc, T **thingp, const char *name)
 {
-    JS_SET_TRACING_NAME(trc, name);
+    trc->setTracingName(name);
     MarkInternal(trc, thingp);
 }
 
 template <typename T>
 static void
 Mark(JSTracer *trc, BarrieredPtr<T> *thing, const char *name)
 {
-    JS_SET_TRACING_NAME(trc, name);
+    trc->setTracingName(name);
     MarkInternal(trc, thing->unsafeGet());
 }
 
 void
 MarkPermanentAtom(JSTracer *trc, JSAtom *atom, const char *name)
 {
-    JS_SET_TRACING_NAME(trc, name);
+    trc->setTracingName(name);
 
     JS_ASSERT(atom->isPermanent());
 
     CheckMarkedThing(trc, atom);
 
     if (!trc->callback) {
         // Atoms do not refer to other GC things so don't need to go on the mark stack.
         // Additionally, PushMarkStack will ignore permanent atoms.
         atom->markIfUnmarked();
     } else {
         void *thing = atom;
         trc->callback(trc, &thing, JSTRACE_STRING);
         JS_ASSERT(thing == atom);
-        JS_UNSET_TRACING_LOCATION(trc);
+        trc->unsetTracingLocation();
     }
 
-    trc->debugPrinter = nullptr;
-    trc->debugPrintArg = nullptr;
+    trc->clearTracingDetails();
 }
 
 } /* namespace gc */
 } /* namespace js */
 
 template <typename T>
 static void
 MarkRoot(JSTracer *trc, T **thingp, const char *name)
 {
     JS_ROOT_MARKING_ASSERT(trc);
-    JS_SET_TRACING_NAME(trc, name);
+    trc->setTracingName(name);
     MarkInternal(trc, thingp);
 }
 
 template <typename T>
 static void
 MarkRange(JSTracer *trc, size_t len, HeapPtr<T> *vec, const char *name)
 {
     for (size_t i = 0; i < len; ++i) {
         if (vec[i].get()) {
-            JS_SET_TRACING_INDEX(trc, name, i);
+            trc->setTracingIndex(name, i);
             MarkInternal(trc, vec[i].unsafeGet());
         }
     }
 }
 
 template <typename T>
 static void
 MarkRootRange(JSTracer *trc, size_t len, T **vec, const char *name)
 {
     JS_ROOT_MARKING_ASSERT(trc);
     for (size_t i = 0; i < len; ++i) {
         if (vec[i]) {
-            JS_SET_TRACING_INDEX(trc, name, i);
+            trc->setTracingIndex(name, i);
             MarkInternal(trc, &vec[i]);
         }
     }
 }
 
 namespace js {
 namespace gc {
 
@@ -532,17 +530,17 @@ gc::MarkKind(JSTracer *trc, void **thing
         MarkInternal(trc, reinterpret_cast<jit::JitCode **>(thingp));
         break;
     }
 }
 
 static void
 MarkGCThingInternal(JSTracer *trc, void **thingp, const char *name)
 {
-    JS_SET_TRACING_NAME(trc, name);
+    trc->setTracingName(name);
     JS_ASSERT(thingp);
     if (!*thingp)
         return;
     MarkKind(trc, thingp, GetGCThingTraceKind(*thingp));
 }
 
 void
 gc::MarkGCThingRoot(JSTracer *trc, void **thingp, const char *name)
@@ -559,137 +557,137 @@ gc::MarkGCThingUnbarriered(JSTracer *trc
 
 /*** ID Marking ***/
 
 static inline void
 MarkIdInternal(JSTracer *trc, jsid *id)
 {
     if (JSID_IS_STRING(*id)) {
         JSString *str = JSID_TO_STRING(*id);
-        JS_SET_TRACING_LOCATION(trc, (void *)id);
+        trc->setTracingLocation((void *)id);
         MarkInternal(trc, &str);
         *id = NON_INTEGER_ATOM_TO_JSID(reinterpret_cast<JSAtom *>(str));
     } else if (MOZ_UNLIKELY(JSID_IS_OBJECT(*id))) {
         JSObject *obj = JSID_TO_OBJECT(*id);
-        JS_SET_TRACING_LOCATION(trc, (void *)id);
+        trc->setTracingLocation((void *)id);
         MarkInternal(trc, &obj);
         *id = OBJECT_TO_JSID(obj);
     } else {
         /* Unset realLocation manually if we do not call MarkInternal. */
-        JS_UNSET_TRACING_LOCATION(trc);
+        trc->unsetTracingLocation();
     }
 }
 
 void
 gc::MarkId(JSTracer *trc, BarrieredId *id, const char *name)
 {
-    JS_SET_TRACING_NAME(trc, name);
+    trc->setTracingName(name);
     MarkIdInternal(trc, id->unsafeGet());
 }
 
 void
 gc::MarkIdRoot(JSTracer *trc, jsid *id, const char *name)
 {
     JS_ROOT_MARKING_ASSERT(trc);
-    JS_SET_TRACING_NAME(trc, name);
+    trc->setTracingName(name);
     MarkIdInternal(trc, id);
 }
 
 void
 gc::MarkIdUnbarriered(JSTracer *trc, jsid *id, const char *name)
 {
-    JS_SET_TRACING_NAME(trc, name);
+    trc->setTracingName(name);
     MarkIdInternal(trc, id);
 }
 
 void
 gc::MarkIdRange(JSTracer *trc, size_t len, HeapId *vec, const char *name)
 {
     for (size_t i = 0; i < len; ++i) {
-        JS_SET_TRACING_INDEX(trc, name, i);
+        trc->setTracingIndex(name, i);
         MarkIdInternal(trc, vec[i].unsafeGet());
     }
 }
 
 void
 gc::MarkIdRootRange(JSTracer *trc, size_t len, jsid *vec, const char *name)
 {
     JS_ROOT_MARKING_ASSERT(trc);
     for (size_t i = 0; i < len; ++i) {
-        JS_SET_TRACING_INDEX(trc, name, i);
+        trc->setTracingIndex(name, i);
         MarkIdInternal(trc, &vec[i]);
     }
 }
 
 /*** Value Marking ***/
 
 static inline void
 MarkValueInternal(JSTracer *trc, Value *v)
 {
     if (v->isMarkable()) {
         JS_ASSERT(v->toGCThing());
         void *thing = v->toGCThing();
-        JS_SET_TRACING_LOCATION(trc, (void *)v);
+        trc->setTracingLocation((void *)v);
         MarkKind(trc, &thing, v->gcKind());
         if (v->isString())
             v->setString((JSString *)thing);
         else
             v->setObjectOrNull((JSObject *)thing);
     } else {
         /* Unset realLocation manually if we do not call MarkInternal. */
-        JS_UNSET_TRACING_LOCATION(trc);
+        trc->unsetTracingLocation();
     }
 }
 
 void
 gc::MarkValue(JSTracer *trc, BarrieredValue *v, const char *name)
 {
-    JS_SET_TRACING_NAME(trc, name);
+    trc->setTracingName(name);
     MarkValueInternal(trc, v->unsafeGet());
 }
 
 void
 gc::MarkValueRoot(JSTracer *trc, Value *v, const char *name)
 {
     JS_ROOT_MARKING_ASSERT(trc);
-    JS_SET_TRACING_NAME(trc, name);
+    trc->setTracingName(name);
     MarkValueInternal(trc, v);
 }
 
 void
 gc::MarkTypeRoot(JSTracer *trc, types::Type *v, const char *name)
 {
     JS_ROOT_MARKING_ASSERT(trc);
-    JS_SET_TRACING_NAME(trc, name);
+    trc->setTracingName(name);
     if (v->isSingleObject()) {
         JSObject *obj = v->singleObject();
         MarkInternal(trc, &obj);
         *v = types::Type::ObjectType(obj);
     } else if (v->isTypeObject()) {
         types::TypeObject *typeObj = v->typeObject();
         MarkInternal(trc, &typeObj);
         *v = types::Type::ObjectType(typeObj);
     }
 }
 
 void
 gc::MarkValueRange(JSTracer *trc, size_t len, BarrieredValue *vec, const char *name)
 {
     for (size_t i = 0; i < len; ++i) {
-        JS_SET_TRACING_INDEX(trc, name, i);
+        trc->setTracingIndex(name, i);
         MarkValueInternal(trc, vec[i].unsafeGet());
     }
 }
 
 void
 gc::MarkValueRootRange(JSTracer *trc, size_t len, Value *vec, const char *name)
 {
     JS_ROOT_MARKING_ASSERT(trc);
     for (size_t i = 0; i < len; ++i) {
-        JS_SET_TRACING_INDEX(trc, name, i);
+        trc->setTracingIndex(name, i);
         MarkValueInternal(trc, &vec[i]);
     }
 }
 
 bool
 gc::IsValueMarked(Value *v)
 {
     JS_ASSERT(v->isMarkable());
@@ -729,65 +727,65 @@ bool
 gc::IsSlotMarked(HeapSlot *s)
 {
     return IsMarked(s);
 }
 
 void
 gc::MarkSlot(JSTracer *trc, HeapSlot *s, const char *name)
 {
-    JS_SET_TRACING_NAME(trc, name);
+    trc->setTracingName(name);
     MarkValueInternal(trc, s->unsafeGet());
 }
 
 void
 gc::MarkArraySlots(JSTracer *trc, size_t len, HeapSlot *vec, const char *name)
 {
     for (size_t i = 0; i < len; ++i) {
-        JS_SET_TRACING_INDEX(trc, name, i);
+        trc->setTracingIndex(name, i);
         MarkValueInternal(trc, vec[i].unsafeGet());
     }
 }
 
 void
 gc::MarkObjectSlots(JSTracer *trc, JSObject *obj, uint32_t start, uint32_t nslots)
 {
     JS_ASSERT(obj->isNative());
     for (uint32_t i = start; i < (start + nslots); ++i) {
-        JS_SET_TRACING_DETAILS(trc, js_GetObjectSlotName, obj, i);
+        trc->setTracingDetails(js_GetObjectSlotName, obj, i);
         MarkValueInternal(trc, obj->nativeGetSlotRef(i).unsafeGet());
     }
 }
 
 static bool
 ShouldMarkCrossCompartment(JSTracer *trc, JSObject *src, Cell *cell)
 {
     if (!IS_GC_MARKING_TRACER(trc))
         return true;
 
     uint32_t color = AsGCMarker(trc)->getMarkColor();
     JS_ASSERT(color == BLACK || color == GRAY);
 
-    if (IsInsideNursery(trc->runtime, cell)) {
+    if (IsInsideNursery(trc->runtime(), cell)) {
         JS_ASSERT(color == BLACK);
         return false;
     }
 
     JS::Zone *zone = cell->tenuredZone();
     if (color == BLACK) {
         /*
          * Having black->gray edges violates our promise to the cycle
          * collector. This can happen if we're collecting a compartment and it
          * has an edge to an uncollected compartment: it's possible that the
          * source and destination of the cross-compartment edge should be gray,
          * but the source was marked black by the conservative scanner.
          */
         if (cell->isMarked(GRAY)) {
             JS_ASSERT(!zone->isCollecting());
-            trc->runtime->gcFoundBlackGrayEdges = true;
+            trc->runtime()->gcFoundBlackGrayEdges = true;
         }
         return zone->isGCMarking();
     } else {
         if (zone->isGCMarkingBlack()) {
             /*
              * The destination compartment is being not being marked gray now,
              * but it will be later, so record the cell so it can be marked gray
              * at the appropriate time.
@@ -822,24 +820,24 @@ gc::MarkCrossCompartmentSlot(JSTracer *t
         MarkSlot(trc, dst, name);
 }
 
 /*** Special Marking ***/
 
 void
 gc::MarkObject(JSTracer *trc, HeapPtr<GlobalObject, JSScript *> *thingp, const char *name)
 {
-    JS_SET_TRACING_NAME(trc, name);
+    trc->setTracingName(name);
     MarkInternal(trc, thingp->unsafeGet());
 }
 
 void
 gc::MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name)
 {
-    JS_SET_TRACING_NAME(trc, name);
+    trc->setTracingName(name);
     MarkValueInternal(trc, v);
 }
 
 bool
 gc::IsCellMarked(Cell **thingp)
 {
     return IsMarked<Cell>(thingp);
 }
@@ -857,122 +855,122 @@ gc::IsCellAboutToBeFinalized(Cell **thin
 
 #define JS_COMPARTMENT_ASSERT_STR(rt, thing)                            \
     JS_ASSERT((thing)->zone()->isGCMarking() ||                         \
               (rt)->isAtomsZone((thing)->zone()));
 
 static void
 PushMarkStack(GCMarker *gcmarker, ObjectImpl *thing)
 {
-    JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
-    JS_ASSERT(!IsInsideNursery(gcmarker->runtime, thing));
+    JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
+    JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
 
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         gcmarker->pushObject(thing);
 }
 
 /*
  * PushMarkStack for BaseShape unpacks its children directly onto the mark
  * stack. For a pre-barrier between incremental slices, this may result in
  * objects in the nursery getting pushed onto the mark stack. It is safe to
  * ignore these objects because they will be marked by the matching
  * post-barrier during the minor GC at the start of each incremental slice.
  */
 static void
 MaybePushMarkStackBetweenSlices(GCMarker *gcmarker, JSObject *thing)
 {
-    JSRuntime *rt = gcmarker->runtime;
+    JSRuntime *rt = gcmarker->runtime();
     JS_COMPARTMENT_ASSERT(rt, thing);
     JS_ASSERT_IF(rt->isHeapBusy(), !IsInsideNursery(rt, thing));
 
     if (!IsInsideNursery(rt, thing) && thing->markIfUnmarked(gcmarker->getMarkColor()))
         gcmarker->pushObject(thing);
 }
 
 static void
 PushMarkStack(GCMarker *gcmarker, JSFunction *thing)
 {
-    JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
-    JS_ASSERT(!IsInsideNursery(gcmarker->runtime, thing));
+    JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
+    JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
 
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         gcmarker->pushObject(thing);
 }
 
 static void
 PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing)
 {
-    JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
-    JS_ASSERT(!IsInsideNursery(gcmarker->runtime, thing));
+    JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
+    JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
 
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         gcmarker->pushType(thing);
 }
 
 static void
 PushMarkStack(GCMarker *gcmarker, JSScript *thing)
 {
-    JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
-    JS_ASSERT(!IsInsideNursery(gcmarker->runtime, thing));
+    JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
+    JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
 
     /*
      * We mark scripts directly rather than pushing on the stack as they can
      * refer to other scripts only indirectly (like via nested functions) and
      * we cannot get to deep recursion.
      */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         MarkChildren(gcmarker, thing);
 }
 
 static void
 PushMarkStack(GCMarker *gcmarker, LazyScript *thing)
 {
-    JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
-    JS_ASSERT(!IsInsideNursery(gcmarker->runtime, thing));
+    JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
+    JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
 
     /*
      * We mark lazy scripts directly rather than pushing on the stack as they
      * only refer to normal scripts and to strings, and cannot recurse.
      */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         MarkChildren(gcmarker, thing);
 }
 
 static void
 ScanShape(GCMarker *gcmarker, Shape *shape);
 
 static void
 PushMarkStack(GCMarker *gcmarker, Shape *thing)
 {
-    JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
-    JS_ASSERT(!IsInsideNursery(gcmarker->runtime, thing));
+    JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
+    JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
 
     /* We mark shapes directly rather than pushing on the stack. */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         ScanShape(gcmarker, thing);
 }
 
 static void
 PushMarkStack(GCMarker *gcmarker, jit::JitCode *thing)
 {
-    JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
-    JS_ASSERT(!IsInsideNursery(gcmarker->runtime, thing));
+    JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
+    JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
 
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         gcmarker->pushJitCode(thing);
 }
 
 static inline void
 ScanBaseShape(GCMarker *gcmarker, BaseShape *base);
 
 static void
 PushMarkStack(GCMarker *gcmarker, BaseShape *thing)
 {
-    JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
-    JS_ASSERT(!IsInsideNursery(gcmarker->runtime, thing));
+    JS_COMPARTMENT_ASSERT(gcmarker->runtime(), thing);
+    JS_ASSERT(!IsInsideNursery(gcmarker->runtime(), thing));
 
     /* We mark base shapes directly rather than pushing on the stack. */
     if (thing->markIfUnmarked(gcmarker->getMarkColor()))
         ScanBaseShape(gcmarker, thing);
 }
 
 static void
 ScanShape(GCMarker *gcmarker, Shape *shape)
@@ -1023,30 +1021,30 @@ ScanBaseShape(GCMarker *gcmarker, BaseSh
         JS_ASSERT(base->compartment() == unowned->compartment());
         unowned->markIfUnmarked(gcmarker->getMarkColor());
     }
 }
 
 static inline void
 ScanLinearString(GCMarker *gcmarker, JSLinearString *str)
 {
-    JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime, str);
+    JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime(), str);
     JS_ASSERT(str->isMarked());
 
     /*
      * Add extra asserts to confirm the static type to detect incorrect string
      * mutations.
      */
     JS_ASSERT(str->JSString::isLinear());
     while (str->hasBase()) {
         str = str->base();
         JS_ASSERT(str->JSString::isLinear());
         if (str->isPermanentAtom())
             break;
-        JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime, str);
+        JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime(), str);
         if (!str->markIfUnmarked())
             break;
     }
 }
 
 /*
  * The function tries to scan the whole rope tree using the marking stack as
  * temporary storage. If that becomes full, the unscanned ropes are added to
@@ -1059,17 +1057,17 @@ ScanLinearString(GCMarker *gcmarker, JSL
 static void
 ScanRope(GCMarker *gcmarker, JSRope *rope)
 {
     ptrdiff_t savedPos = gcmarker->stack.position();
     JS_DIAGNOSTICS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING);
     for (;;) {
         JS_DIAGNOSTICS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING);
         JS_DIAGNOSTICS_ASSERT(rope->JSString::isRope());
-        JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime, rope);
+        JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime(), rope);
         JS_ASSERT(rope->isMarked());
         JSRope *next = nullptr;
 
         JSString *right = rope->rightChild();
         if (!right->isPermanentAtom() && right->markIfUnmarked()) {
             if (right->isLinear())
                 ScanLinearString(gcmarker, &right->asLinear());
             else
@@ -1113,17 +1111,17 @@ ScanString(GCMarker *gcmarker, JSString 
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSString *str)
 {
     // Permanent atoms might not be associated with this runtime.
     if (str->isPermanentAtom())
         return;
 
-    JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime, str);
+    JS_COMPARTMENT_ASSERT_STR(gcmarker->runtime(), str);
 
     /*
      * As string can only refer to other strings we fully scan its GC graph
      * using the explicit stack when navigating the rope tree to avoid
      * dealing with strings on the stack in drainMarkStack.
      */
     if (str->markIfUnmarked())
         ScanString(gcmarker, str);
@@ -1485,51 +1483,51 @@ GCMarker::processMarkStackTop(SliceBudge
         JS_ASSERT((addr3 - addr2) % sizeof(Value) == 0);
         vp = reinterpret_cast<HeapSlot *>(addr2);
         end = reinterpret_cast<HeapSlot *>(addr3);
         goto scan_value_array;
     }
 
     if (tag == ObjectTag) {
         obj = reinterpret_cast<JSObject *>(addr);
-        JS_COMPARTMENT_ASSERT(runtime, obj);
+        JS_COMPARTMENT_ASSERT(runtime(), obj);
         goto scan_obj;
     }
 
     processMarkStackOther(tag, addr);
     return;
 
   scan_value_array:
     JS_ASSERT(vp <= end);
     while (vp != end) {
         const Value &v = *vp++;
         if (v.isString()) {
             JSString *str = v.toString();
             if (!str->isPermanentAtom()) {
-                JS_COMPARTMENT_ASSERT_STR(runtime, str);
-                JS_ASSERT(runtime->isAtomsZone(str->zone()) || str->zone() == obj->zone());
+                JS_COMPARTMENT_ASSERT_STR(runtime(), str);
+                JS_ASSERT(runtime()->isAtomsZone(str->zone()) || str->zone() == obj->zone());
                 if (str->markIfUnmarked())
                     ScanString(this, str);
             }
         } else if (v.isObject()) {
             JSObject *obj2 = &v.toObject();
-            JS_COMPARTMENT_ASSERT(runtime, obj2);
+            JS_COMPARTMENT_ASSERT(runtime(), obj2);
             JS_ASSERT(obj->compartment() == obj2->compartment());
             if (obj2->markIfUnmarked(getMarkColor())) {
                 pushValueArray(obj, vp, end);
                 obj = obj2;
                 goto scan_obj;
             }
         }
     }
     return;
 
   scan_obj:
     {
-        JS_COMPARTMENT_ASSERT(runtime, obj);
+        JS_COMPARTMENT_ASSERT(runtime(), obj);
 
         budget.step();
         if (budget.isOverBudget()) {
             pushObject(obj);
             return;
         }
 
         types::TypeObject *type = obj->typeFromGC();
@@ -1539,18 +1537,18 @@ GCMarker::processMarkStackTop(SliceBudge
         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 &&
+            JS_ASSERT_IF(runtime()->gcMode() == JSGC_MODE_INCREMENTAL &&
+                         runtime()->gcIncrementalEnabled &&
                          !(clasp->trace == JS_GlobalObjectTraceHook &&
                            (!obj->compartment()->options().getTrace() ||
                             !obj->isOwnGlobal())),
                          clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS);
             clasp->trace(this, obj);
         }
 
         if (!shape->isNative())
@@ -1581,17 +1579,17 @@ GCMarker::processMarkStackTop(SliceBudge
         goto scan_value_array;
     }
 }
 
 bool
 GCMarker::drainMarkStack(SliceBudget &budget)
 {
 #ifdef DEBUG
-    JSRuntime *rt = runtime;
+    JSRuntime *rt = runtime();
 
     struct AutoCheckCompartment {
         JSRuntime *runtime;
         AutoCheckCompartment(JSRuntime *rt) : runtime(rt) {
             JS_ASSERT(!rt->gcStrictCompartmentChecking);
             runtime->gcStrictCompartmentChecking = true;
         }
         ~AutoCheckCompartment() { runtime->gcStrictCompartmentChecking = false; }
@@ -1676,28 +1674,28 @@ UnmarkGrayChildren(JSTracer *trc, void *
 
 struct UnmarkGrayTracer : public JSTracer
 {
     /*
      * We set eagerlyTraceWeakMaps to false because the cycle collector will fix
      * up any color mismatches involving weakmaps when it runs.
      */
     UnmarkGrayTracer(JSRuntime *rt)
-      : tracingShape(false), previousShape(nullptr), unmarkedAny(false)
-    {
-        JS_TracerInit(this, rt, UnmarkGrayChildren);
-        eagerlyTraceWeakMaps = DoNotTraceWeakMaps;
-    }
+      : JSTracer(rt, UnmarkGrayChildren, DoNotTraceWeakMaps),
+        tracingShape(false),
+        previousShape(nullptr),
+        unmarkedAny(false)
+    {}
 
     UnmarkGrayTracer(JSTracer *trc, bool tracingShape)
-      : tracingShape(tracingShape), previousShape(nullptr), unmarkedAny(false)
-    {
-        JS_TracerInit(this, trc->runtime, UnmarkGrayChildren);
-        eagerlyTraceWeakMaps = DoNotTraceWeakMaps;
-    }
+      : JSTracer(trc->runtime(), UnmarkGrayChildren, DoNotTraceWeakMaps),
+        tracingShape(tracingShape),
+        previousShape(nullptr),
+        unmarkedAny(false)
+    {}
 
     /* True iff we are tracing the immediate children of a shape. */
     bool tracingShape;
 
     /* If tracingShape, shape child or nullptr. Otherwise, nullptr. */
     void *previousShape;
 
     /* Whether we unmarked anything. */
@@ -1734,27 +1732,27 @@ struct UnmarkGrayTracer : public JSTrace
  *   of the containers, we must add unmark-graying read barriers to these
  *   containers.
  */
 static void
 UnmarkGrayChildren(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
     void *thing = *thingp;
     int stackDummy;
-    if (!JS_CHECK_STACK_SIZE(trc->runtime->mainThread.nativeStackLimit[StackForSystemCode], &stackDummy)) {
+    if (!JS_CHECK_STACK_SIZE(trc->runtime()->mainThread.nativeStackLimit[StackForSystemCode], &stackDummy)) {
         /*
          * If we run out of stack, we take a more drastic measure: require that
          * we GC again before the next CC.
          */
-        trc->runtime->gcGrayBitsValid = false;
+        trc->runtime()->gcGrayBitsValid = false;
         return;
     }
 
     UnmarkGrayTracer *tracer = static_cast<UnmarkGrayTracer *>(trc);
-    if (!IsInsideNursery(trc->runtime, thing)) {
+    if (!IsInsideNursery(trc->runtime(), thing)) {
         if (!JS::GCThingIsMarkedGray(thing))
             return;
 
         UnmarkGrayGCThing(thing);
         tracer->unmarkedAny = true;
     }
 
     /*
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -294,27 +294,25 @@ class MinorCollectionTracer : public JST
     /* Insert the given relocation entry into the list of things to visit. */
     MOZ_ALWAYS_INLINE void insertIntoFixupList(RelocationOverlay *entry) {
         *tail = entry;
         tail = &entry->next_;
         *tail = nullptr;
     }
 
     MinorCollectionTracer(JSRuntime *rt, Nursery *nursery)
-      : JSTracer(),
+      : JSTracer(rt, Nursery::MinorGCCallback, TraceWeakMapKeysValues),
         nursery(nursery),
         session(rt, MinorCollecting),
         tenuredSize(0),
         head(nullptr),
         tail(&head),
         savedRuntimeNeedBarrier(rt->needsBarrier()),
         disableStrictProxyChecking(rt)
     {
-        JS_TracerInit(this, rt, Nursery::MinorGCCallback);
-        eagerlyTraceWeakMaps = TraceWeakMapKeysValues;
         rt->gcNumber++;
 
         /*
          * We disable the runtime needsBarrier() check so that pre-barriers do
          * not fire on objects that have been relocated. The pre-barrier's
          * call to obj->zone() will try to look through shape_, which is now
          * the relocation magic and will crash. However, zone->needsBarrier()
          * must still be set correctly so that allocations we make in minor
@@ -332,18 +330,18 @@ class MinorCollectionTracer : public JST
                 if (!ArrayBufferObject::saveArrayBufferList(c, liveArrayBuffers))
                     CrashAtUnhandlableOOM("OOM while saving live array buffers");
                 ArrayBufferObject::resetArrayBufferList(c);
             }
         }
     }
 
     ~MinorCollectionTracer() {
-        runtime->setNeedsBarrier(savedRuntimeNeedBarrier);
-        if (runtime->gcIncrementalState != NO_INCREMENTAL)
+        runtime()->setNeedsBarrier(savedRuntimeNeedBarrier);
+        if (runtime()->gcIncrementalState != NO_INCREMENTAL)
             ArrayBufferObject::restoreArrayBufferLists(liveArrayBuffers);
     }
 };
 
 } /* namespace gc */
 } /* namespace js */
 
 static AllocKind
@@ -531,17 +529,17 @@ js::Nursery::markSlot(MinorCollectionTra
     JSObject *tenured = static_cast<JSObject*>(moveToTenured(trc, obj));
     slotp->unsafeGet()->setObject(*tenured);
 }
 
 void *
 js::Nursery::moveToTenured(MinorCollectionTracer *trc, JSObject *src)
 {
     Zone *zone = src->zone();
-    AllocKind dstKind = GetObjectAllocKindForCopy(trc->runtime, src);
+    AllocKind dstKind = GetObjectAllocKindForCopy(trc->runtime(), src);
     JSObject *dst = static_cast<JSObject *>(allocateFromTenured(zone, dstKind));
     if (!dst)
         CrashAtUnhandlableOOM("Failed to allocate object while tenuring.");
 
     trc->tenuredSize += moveObjectToTenured(dst, src, dstKind);
 
     RelocationOverlay *overlay = reinterpret_cast<RelocationOverlay *>(src);
     overlay->forwardTo(dst);
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -83,20 +83,20 @@ MarkExactStackRootList(JSTracer *trc, Ro
         rooter = rooter->previous();
     }
 }
 
 static void
 MarkExactStackRoots(JSTracer *trc)
 {
     for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
-        for (ContextIter cx(trc->runtime); !cx.done(); cx.next())
+        for (ContextIter cx(trc->runtime()); !cx.done(); cx.next())
             MarkExactStackRootList(trc, cx->thingGCRooters[i], ThingRootKind(i));
 
-        MarkExactStackRootList(trc, trc->runtime->mainThread.thingGCRooters[i], ThingRootKind(i));
+        MarkExactStackRootList(trc, trc->runtime()->mainThread.thingGCRooters[i], ThingRootKind(i));
     }
 }
 #endif /* JSGC_USE_EXACT_ROOTING */
 
 enum ConservativeGCTest
 {
     CGCT_VALID,
     CGCT_LOWBITSET, /* excluded because one of the low bits was set */
@@ -192,17 +192,17 @@ IsAddressableGCThing(JSRuntime *rt, uint
  */
 static inline ConservativeGCTest
 MarkIfGCThingWord(JSTracer *trc, uintptr_t w)
 {
     void *thing;
     ArenaHeader *aheader;
     AllocKind thingKind;
     ConservativeGCTest status =
-        IsAddressableGCThing(trc->runtime, w, IS_GC_MARKING_TRACER(trc),
+        IsAddressableGCThing(trc->runtime(), w, IS_GC_MARKING_TRACER(trc),
                              &thingKind, &aheader, &thing);
     if (status != CGCT_VALID)
         return status;
 
     /*
      * Check if the thing is free. We must use the list of free spans as at
      * this point we no longer have the mark bits from the previous GC run and
      * we must account for newly allocated things.
@@ -210,26 +210,26 @@ MarkIfGCThingWord(JSTracer *trc, uintptr
     if (InFreeList(aheader, thing))
         return CGCT_NOTLIVE;
 
     JSGCTraceKind traceKind = MapAllocToTraceKind(thingKind);
 #ifdef DEBUG
     const char pattern[] = "machine_stack %p";
     char nameBuf[sizeof(pattern) - 2 + sizeof(thing) * 2];
     JS_snprintf(nameBuf, sizeof(nameBuf), pattern, thing);
-    JS_SET_TRACING_NAME(trc, nameBuf);
+    trc->setTracingName(nameBuf);
 #endif
-    JS_SET_TRACING_LOCATION(trc, (void *)w);
+    trc->setTracingLocation((void *)w);
     void *tmp = thing;
     MarkKind(trc, &tmp, traceKind);
     JS_ASSERT(tmp == thing);
 
 #ifdef DEBUG
-    if (trc->runtime->gcIncrementalState == MARK_ROOTS)
-        trc->runtime->mainThread.gcSavedRoots.append(
+    if (trc->runtime()->gcIncrementalState == MARK_ROOTS)
+        trc->runtime()->mainThread.gcSavedRoots.append(
             PerThreadData::SavedGCRoot(thing, traceKind));
 #endif
 
     return CGCT_VALID;
 }
 
 #ifndef JSGC_USE_EXACT_ROOTING
 static void
@@ -281,25 +281,25 @@ MarkRangeConservativelyAndSkipIon(JSTrac
 
     // Mark everything after the most recent Ion activation.
     MarkRangeConservatively(trc, i, end);
 }
 
 static MOZ_NEVER_INLINE void
 MarkConservativeStackRoots(JSTracer *trc, bool useSavedRoots)
 {
-    JSRuntime *rt = trc->runtime;
+    JSRuntime *rt = trc->runtime();
 
 #ifdef DEBUG
     if (useSavedRoots) {
         for (PerThreadData::SavedGCRoot *root = rt->mainThread.gcSavedRoots.begin();
              root != rt->mainThread.gcSavedRoots.end();
              root++)
         {
-            JS_SET_TRACING_NAME(trc, "cstack");
+            trc->setTracingName("cstack");
             MarkKind(trc, &root->thing, root->kind);
         }
         return;
     }
 
     if (rt->gcIncrementalState == MARK_ROOTS)
         rt->mainThread.gcSavedRoots.clearAndFree();
 #endif
@@ -464,17 +464,17 @@ AutoGCRooter::trace(JSTracer *trc)
         MarkScriptRootRange(trc, vector.length(), vector.begin(), "js::AutoScriptVector.vector");
         return;
       }
 
       case OBJOBJHASHMAP: {
         AutoObjectObjectHashMap::HashMapImpl &map = static_cast<AutoObjectObjectHashMap *>(this)->map;
         for (AutoObjectObjectHashMap::Enum e(map); !e.empty(); e.popFront()) {
             MarkObjectRoot(trc, &e.front().value(), "AutoObjectObjectHashMap value");
-            JS_SET_TRACING_LOCATION(trc, (void *)&e.front().key());
+            trc->setTracingLocation((void *)&e.front().key());
             JSObject *key = e.front().key();
             MarkObjectRoot(trc, &key, "AutoObjectObjectHashMap key");
             if (key != e.front().key())
                 e.rekeyFront(key);
         }
         return;
       }
 
@@ -557,26 +557,26 @@ AutoGCRooter::trace(JSTracer *trc)
     JS_ASSERT(tag_ >= 0);
     if (Value *vp = static_cast<AutoArrayRooter *>(this)->array)
         MarkValueRootRange(trc, tag_, vp, "JS::AutoArrayRooter.array");
 }
 
 /* static */ void
 AutoGCRooter::traceAll(JSTracer *trc)
 {
-    for (ContextIter cx(trc->runtime); !cx.done(); cx.next()) {
+    for (ContextIter cx(trc->runtime()); !cx.done(); cx.next()) {
         for (js::AutoGCRooter *gcr = cx->autoGCRooters; gcr; gcr = gcr->down)
             gcr->trace(trc);
     }
 }
 
 /* static */ void
 AutoGCRooter::traceAllWrappers(JSTracer *trc)
 {
-    for (ContextIter cx(trc->runtime); !cx.done(); cx.next()) {
+    for (ContextIter cx(trc->runtime()); !cx.done(); cx.next()) {
         for (js::AutoGCRooter *gcr = cx->autoGCRooters; gcr; gcr = gcr->down) {
             if (gcr->tag_ == WRAPVECTOR || gcr->tag_ == WRAPPER)
                 gcr->trace(trc);
         }
     }
 }
 
 void
@@ -640,17 +640,17 @@ struct PersistentRootedMarker
     }
 };
 }
 }
 
 void
 js::gc::MarkPersistentRootedChains(JSTracer *trc)
 {
-    JSRuntime *rt = trc->runtime;
+    JSRuntime *rt = trc->runtime();
 
     // Mark the PersistentRooted chains of types that may be null.
     PersistentRootedMarker<JSFunction*>::markChainIfNotNull<MarkObjectRoot>(
         trc, rt->functionPersistentRooteds, "PersistentRooted<JSFunction *>");
     PersistentRootedMarker<JSObject*>::markChainIfNotNull<MarkObjectRoot>(
         trc, rt->objectPersistentRooteds, "PersistentRooted<JSObject *>");
     PersistentRootedMarker<JSScript*>::markChainIfNotNull<MarkScriptRoot>(
         trc, rt->scriptPersistentRooteds, "PersistentRooted<JSScript *>");
@@ -662,17 +662,17 @@ js::gc::MarkPersistentRootedChains(JSTra
                                                         "PersistentRooted<jsid>");
     PersistentRootedMarker<Value>::markChain<MarkValueRoot>(trc, rt->valuePersistentRooteds,
                                                             "PersistentRooted<Value>");
 }
 
 void
 js::gc::MarkRuntime(JSTracer *trc, bool useSavedRoots)
 {
-    JSRuntime *rt = trc->runtime;
+    JSRuntime *rt = trc->runtime();
     JS_ASSERT(trc->callback != GCMarker::GrayCallback);
 
     JS_ASSERT(!rt->mainThread.suppressGC);
 
     if (IS_GC_MARKING_TRACER(trc)) {
         for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
             if (!c->zone()->isCollecting())
                 c->markCrossCompartmentWrappers(trc);
@@ -713,17 +713,17 @@ js::gc::MarkRuntime(JSTracer *trc, bool 
     MarkPersistentRootedChains(trc);
 
     if (rt->scriptAndCountsVector) {
         ScriptAndCountsVector &vec = *rt->scriptAndCountsVector;
         for (size_t i = 0; i < vec.length(); i++)
             MarkScriptRoot(trc, &vec[i].script, "scriptAndCountsVector");
     }
 
-    if (!rt->isBeingDestroyed() && !trc->runtime->isHeapMinorCollecting()) {
+    if (!rt->isBeingDestroyed() && !trc->runtime()->isHeapMinorCollecting()) {
         if (!IS_GC_MARKING_TRACER(trc) || rt->atomsCompartment()->zone()->isCollecting()) {
             MarkPermanentAtoms(trc);
             MarkAtoms(trc);
 #ifdef JS_ION
             jit::JitRuntime::Mark(trc);
 #endif
         }
     }
@@ -744,17 +744,17 @@ js::gc::MarkRuntime(JSTracer *trc, bool 
                     JS_ASSERT(script == i.get<JSScript>());
                 }
             }
         }
     }
 
     /* We can't use GCCompartmentsIter if we're called from TraceRuntime. */
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
-        if (trc->runtime->isHeapMinorCollecting())
+        if (trc->runtime()->isHeapMinorCollecting())
             c->globalWriteBarriered = false;
 
         if (IS_GC_MARKING_TRACER(trc) && !c->zone()->isCollecting())
             continue;
 
         /* During a GC, these are treated as weak pointers. */
         if (!IS_GC_MARKING_TRACER(trc)) {
             if (c->watchpointMap)
@@ -799,14 +799,14 @@ js::gc::MarkRuntime(JSTracer *trc, bool 
                 (*op)(trc, rt->gcGrayRootTracer.data);
         }
     }
 }
 
 void
 js::gc::BufferGrayRoots(GCMarker *gcmarker)
 {
-    JSRuntime *rt = gcmarker->runtime;
+    JSRuntime *rt = gcmarker->runtime();
     gcmarker->startBufferingGrayRoots();
     if (JSTraceDataOp op = rt->gcGrayRootTracer.op)
         (*op)(gcmarker, rt->gcGrayRootTracer.data);
     gcmarker->endBufferingGrayRoots();
 }
--- a/js/src/gc/StoreBuffer.cpp
+++ b/js/src/gc/StoreBuffer.cpp
@@ -21,17 +21,17 @@ using mozilla::ReentrancyGuard;
 
 /*** Edges ***/
 
 void
 StoreBuffer::SlotsEdge::mark(JSTracer *trc)
 {
     JSObject *obj = object();
 
-    if (trc->runtime->gcNursery.isInside(obj))
+    if (trc->runtime()->gcNursery.isInside(obj))
         return;
 
     if (!obj->isNative()) {
         const Class *clasp = obj->getClass();
         if (clasp)
             clasp->trace(trc, obj);
         return;
     }
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -15,18 +15,18 @@
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/ReentrancyGuard.h"
 
 #include "jsalloc.h"
 
 #include "ds/LifoAlloc.h"
 #include "gc/Nursery.h"
+#include "gc/Tracer.h"
 #include "js/MemoryMetrics.h"
-#include "js/Tracer.h"
 
 namespace js {
 
 void
 CrashAtUnhandlableOOM(const char *reason);
 
 namespace gc {
 
@@ -56,17 +56,17 @@ class HashKeyRef : public BufferableRef
   public:
     HashKeyRef(Map *m, const Key &k) : map(m), key(k) {}
 
     void mark(JSTracer *trc) {
         Key prior = key;
         typename Map::Ptr p = map->lookup(key);
         if (!p)
             return;
-        JS_SET_TRACING_LOCATION(trc, (void*)&*p);
+        trc->setTracingLocation(&*p);
         Mark(trc, &key, "HashKeyRef");
         map->rekeyIfMoved(prior, key);
     }
 };
 
 typedef HashSet<void *, PointerHasher<void *, 3>, SystemAllocPolicy> EdgeSet;
 
 /* The size of a single block of store buffer storage space. */
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "js/Tracer.h"
+#include "gc/Tracer.h"
 
 #include "jsapi.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsprf.h"
 #include "jsscript.h"
 #include "NamespaceImports.h"
 
@@ -86,38 +86,32 @@ JS_CallHeapFunctionTracer(JSTracer *trc,
 
 JS_PUBLIC_API(void)
 JS_CallTenuredObjectTracer(JSTracer *trc, JS::TenuredHeap<JSObject *> *objp, const char *name)
 {
     JSObject *obj = objp->getPtr();
     if (!obj)
         return;
 
-    JS_SET_TRACING_LOCATION(trc, (void*)objp);
+    trc->setTracingLocation((void*)objp);
     MarkObjectUnbarriered(trc, &obj, name);
 
     objp->setPtr(obj);
 }
 
 JS_PUBLIC_API(void)
-JS_TracerInit(JSTracer *trc, JSRuntime *rt, JSTraceCallback callback)
-{
-    InitTracer(trc, rt, callback);
-}
-
-JS_PUBLIC_API(void)
 JS_TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind)
 {
     js::TraceChildren(trc, thing, kind);
 }
 
 JS_PUBLIC_API(void)
 JS_TraceRuntime(JSTracer *trc)
 {
-    AssertHeapIsIdle(trc->runtime);
+    AssertHeapIsIdle(trc->runtime());
     TraceRuntime(trc);
 }
 
 static size_t
 CountDecimalDigits(size_t num)
 {
     size_t numDigits = 0;
     do {
@@ -240,25 +234,95 @@ JS_GetTraceThingInfo(char *buf, size_t b
           case JSTRACE_BASE_SHAPE:
           case JSTRACE_TYPE_OBJECT:
             break;
         }
     }
     buf[bufsize - 1] = '\0';
 }
 
-extern JS_PUBLIC_API(const char *)
-JS_GetTraceEdgeName(JSTracer *trc, char *buffer, int bufferSize)
+JSTracer::JSTracer(JSRuntime *rt, JSTraceCallback traceCallback,
+                   WeakMapTraceKind weakTraceKind /* = TraceWeakMapValues */)
+  : callback(traceCallback)
+  , runtime_(rt)
+  , debugPrinter_(nullptr)
+  , debugPrintArg_(nullptr)
+  , debugPrintIndex_(size_t(-1))
+  , eagerlyTraceWeakMaps_(weakTraceKind)
+#ifdef JS_GC_ZEAL
+  , realLocation_(nullptr)
+#endif
+{
+}
+
+bool
+JSTracer::hasTracingDetails() const
 {
-    if (trc->debugPrinter) {
-        trc->debugPrinter(trc, buffer, bufferSize);
+    return debugPrinter_ || debugPrintArg_;
+}
+
+const char *
+JSTracer::tracingName(const char *fallback) const
+{
+    JS_ASSERT(hasTracingDetails());
+    return debugPrinter_ ? fallback : (const char *)debugPrintArg_;
+}
+
+const char *
+JSTracer::getTracingEdgeName(char *buffer, size_t bufferSize)
+{
+    if (debugPrinter_) {
+        debugPrinter_(this, buffer, bufferSize);
+        return buffer;
+    }
+    if (debugPrintIndex_ != size_t(-1)) {
+        JS_snprintf(buffer, bufferSize, "%s[%lu]",
+                    (const char *)debugPrintArg_,
+                    debugPrintIndex_);
         return buffer;
     }
-    if (trc->debugPrintIndex != (size_t) - 1) {
-        JS_snprintf(buffer, bufferSize, "%s[%lu]",
-                    (const char *)trc->debugPrintArg,
-                    trc->debugPrintIndex);
-        return buffer;
-    }
-    return (const char*)trc->debugPrintArg;
+    return (const char*)debugPrintArg_;
+}
+
+JSTraceNamePrinter
+JSTracer::debugPrinter() const
+{
+    return debugPrinter_;
+}
+
+const void *
+JSTracer::debugPrintArg() const
+{
+    return debugPrintArg_;
+}
+
+size_t
+JSTracer::debugPrintIndex() const
+{
+    return debugPrintIndex_;
 }
 
+void
+JSTracer::setTraceCallback(JSTraceCallback traceCallback)
+{
+    callback = traceCallback;
+}
 
+#ifdef JS_GC_ZEAL
+void
+JSTracer::setTracingLocation(void *location)
+{
+    if (!realLocation_ || !location)
+        realLocation_ = location;
+}
+
+void
+JSTracer::unsetTracingLocation()
+{
+    realLocation_ = nullptr;
+}
+
+void **
+JSTracer::tracingLocation(void **thingp)
+{
+    return realLocation_ ? (void **)realLocation_ : thingp;
+}
+#endif
new file mode 100644
--- /dev/null
+++ b/js/src/gc/Tracer.h
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_Tracer_h
+#define js_Tracer_h
+
+#include "js/TracingAPI.h"
+
+#endif /* js_Tracer_h */
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -54,17 +54,17 @@ using namespace mozilla;
  *   generational pointers we find is a subset of the pointers recorded in our
  *   StoreBuffer.
  */
 
 struct EdgeValue
 {
     void *thing;
     JSGCTraceKind kind;
-    char *label;
+    const char *label;
 };
 
 struct VerifyNode
 {
     void *thing;
     JSGCTraceKind kind;
     uint32_t count;
     EdgeValue edges[1];
@@ -97,46 +97,48 @@ struct VerifyPreTracer : JSTracer
 
     /* This graph represents the initial GC "snapshot". */
     VerifyNode *curnode;
     VerifyNode *root;
     char *edgeptr;
     char *term;
     NodeMap nodemap;
 
-    VerifyPreTracer(JSRuntime *rt) : noggc(rt), root(nullptr) {}
+    VerifyPreTracer(JSRuntime *rt, JSTraceCallback callback)
+      : JSTracer(rt, callback), noggc(rt), number(rt->gcNumber), count(0), root(nullptr)
+    {}
 
     ~VerifyPreTracer() {
         js_free(root);
     }
 };
 
 /*
  * This function builds up the heap snapshot by adding edges to the current
  * node.
  */
 static void
 AccumulateEdge(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
 {
     VerifyPreTracer *trc = (VerifyPreTracer *)jstrc;
 
-    JS_ASSERT(!IsInsideNursery(trc->runtime, *(uintptr_t **)thingp));
+    JS_ASSERT(!IsInsideNursery(trc->runtime(), *(uintptr_t **)thingp));
 
     trc->edgeptr += sizeof(EdgeValue);
     if (trc->edgeptr >= trc->term) {
         trc->edgeptr = trc->term;
         return;
     }
 
     VerifyNode *node = trc->curnode;
     uint32_t i = node->count;
 
     node->edges[i].thing = *thingp;
     node->edges[i].kind = kind;
-    node->edges[i].label = trc->debugPrinter ? nullptr : (char *)trc->debugPrintArg;
+    node->edges[i].label = trc->tracingName("<unknown>");
     node->count++;
 }
 
 static VerifyNode *
 MakeNode(VerifyPreTracer *trc, void *thing, JSGCTraceKind kind)
 {
     NodeMap::AddPtr p = trc->nodemap.lookupForAdd(thing);
     if (!p) {
@@ -186,25 +188,27 @@ gc::StartVerifyPreBarriers(JSRuntime *rt
     AutoPrepareForTracing prep(rt, WithAtoms);
 
     if (!IsIncrementalGCSafe(rt))
         return;
 
     for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront())
         r.front()->bitmap.clear();
 
-    VerifyPreTracer *trc = js_new<VerifyPreTracer>(rt);
+    rt->gcNumber++;
+
+    VerifyPreTracer *trc = js_new<VerifyPreTracer>(rt, nullptr);
     if (!trc)
         return;
 
-    rt->gcNumber++;
-    trc->number = rt->gcNumber;
-    trc->count = 0;
-
-    JS_TracerInit(trc, rt, AccumulateEdge);
+    /*
+     * Passing a function pointer directly to js_new trips a compiler bug in
+     * MSVC. Work around by filling the pointer after allocating with nullptr.
+     */
+    trc->setTraceCallback(AccumulateEdge);
 
     const size_t size = 64 * 1024 * 1024;
     trc->root = (VerifyNode *)js_malloc(size);
     if (!trc->root)
         goto oom;
     trc->edgeptr = (char *)trc->root;
     trc->term = trc->edgeptr + size;
 
@@ -300,17 +304,17 @@ AssertMarkedOrAllocated(const EdgeValue 
     if (!edge.thing || IsMarkedOrAllocated(static_cast<Cell *>(edge.thing)))
         return;
 
     // Permanent atoms aren't marked during graph traversal.
     if (edge.kind == JSTRACE_STRING && static_cast<JSString *>(edge.thing)->isPermanentAtom())
         return;
 
     char msgbuf[1024];
-    const char *label = edge.label ? edge.label : "<unknown>";
+    const char *label = edge.label;
 
     JS_snprintf(msgbuf, sizeof(msgbuf), "[barrier verifier] Unmarked edge: %s", label);
     MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
     MOZ_CRASH();
 }
 
 void
 gc::EndVerifyPreBarriers(JSRuntime *rt)
@@ -342,17 +346,17 @@ gc::EndVerifyPreBarriers(JSRuntime *rt)
      */
     JS_ASSERT(trc->number == rt->gcNumber);
     rt->gcNumber++;
 
     rt->gcVerifyPreData = nullptr;
     rt->gcIncrementalState = NO_INCREMENTAL;
 
     if (!compartmentCreated && IsIncrementalGCSafe(rt)) {
-        JS_TracerInit(trc, rt, CheckEdge);
+        trc->setTraceCallback(CheckEdge);
 
         /* Start after the roots. */
         VerifyNode *node = NextNode(trc->root);
         while ((char *)node < trc->edgeptr) {
             trc->curnode = node;
             JS_TraceChildren(trc, node->thing, node->kind);
 
             if (node->count <= MAX_VERIFIER_EDGES) {
@@ -367,26 +371,31 @@ gc::EndVerifyPreBarriers(JSRuntime *rt)
     rt->gcMarker.reset();
     rt->gcMarker.stop();
 
     js_delete(trc);
 }
 
 /*** Post-Barrier Verifyier ***/
 
-struct VerifyPostTracer : JSTracer {
+struct VerifyPostTracer : JSTracer
+{
     /* The gcNumber when the verification began. */
     uint64_t number;
 
     /* This counts up to gcZealFrequency to decide whether to verify. */
     int count;
 
     /* The set of edges in the StoreBuffer at the end of verification. */
     typedef HashSet<void **, PointerHasher<void **, 3>, SystemAllocPolicy> EdgeSet;
     EdgeSet *edges;
+
+    VerifyPostTracer(JSRuntime *rt, JSTraceCallback callback)
+      : JSTracer(rt, callback), number(rt->gcNumber), count(0)
+    {}
 };
 
 /*
  * The post-barrier verifier runs the full store buffer and a fake nursery when
  * running and when it stops, walks the full heap to ensure that all the
  * important edges were inserted into the storebuffer.
  */
 void
@@ -396,48 +405,47 @@ gc::StartVerifyPostBarriers(JSRuntime *r
     if (rt->gcVerifyPostData ||
         rt->gcIncrementalState != NO_INCREMENTAL)
     {
         return;
     }
 
     MinorGC(rt, JS::gcreason::EVICT_NURSERY);
 
-    VerifyPostTracer *trc = js_new<VerifyPostTracer>();
+    rt->gcNumber++;
+
+    VerifyPostTracer *trc = js_new<VerifyPostTracer>(rt, nullptr);
     if (!trc)
         return;
 
     rt->gcVerifyPostData = trc;
-    rt->gcNumber++;
-    trc->number = rt->gcNumber;
-    trc->count = 0;
 #endif
 }
 
 #ifdef JSGC_GENERATIONAL
 void
 PostVerifierCollectStoreBufferEdges(JSTracer *jstrc, void **thingp, JSGCTraceKind kind)
 {
     VerifyPostTracer *trc = (VerifyPostTracer *)jstrc;
 
     /* The nursery only stores objects. */
     if (kind != JSTRACE_OBJECT)
         return;
 
     /* The store buffer may store extra, non-cross-generational edges. */
     JSObject *dst = *reinterpret_cast<JSObject **>(thingp);
-    if (trc->runtime->gcNursery.isInside(thingp) || !trc->runtime->gcNursery.isInside(dst))
+    if (trc->runtime()->gcNursery.isInside(thingp) || !trc->runtime()->gcNursery.isInside(dst))
         return;
 
     /*
      * Values will be unpacked to the stack before getting here. However, the
      * only things that enter this callback are marked by the store buffer. The
      * store buffer ensures that the real tracing location is set correctly.
      */
-    void **loc = trc->realLocation != nullptr ? (void **)trc->realLocation : thingp;
+    void **loc = trc->tracingLocation(thingp);
 
     trc->edges->put(loc);
 }
 
 static void
 AssertStoreBufferContainsEdge(VerifyPostTracer::EdgeSet *edges, void **loc, JSObject *dst)
 {
     if (edges->has(loc))
@@ -455,51 +463,51 @@ PostVerifierVisitEdge(JSTracer *jstrc, v
 {
     VerifyPostTracer *trc = (VerifyPostTracer *)jstrc;
 
     /* The nursery only stores objects. */
     if (kind != JSTRACE_OBJECT)
         return;
 
     /* Filter out non cross-generational edges. */
-    JS_ASSERT(!trc->runtime->gcNursery.isInside(thingp));
+    JS_ASSERT(!trc->runtime()->gcNursery.isInside(thingp));
     JSObject *dst = *reinterpret_cast<JSObject **>(thingp);
-    if (!trc->runtime->gcNursery.isInside(dst))
+    if (!trc->runtime()->gcNursery.isInside(dst))
         return;
 
     /*
      * Values will be unpacked to the stack before getting here. However, the
      * only things that enter this callback are marked by the JS_TraceChildren
      * below. Since ObjectImpl::markChildren handles this, the real trace
      * location will be set correctly in these cases.
      */
-    void **loc = trc->realLocation != nullptr ? (void **)trc->realLocation : thingp;
+    void **loc = trc->tracingLocation(thingp);
 
     AssertStoreBufferContainsEdge(trc->edges, loc, dst);
 }
 #endif
 
 void
 js::gc::EndVerifyPostBarriers(JSRuntime *rt)
 {
 #ifdef JSGC_GENERATIONAL
     VerifyPostTracer::EdgeSet edges;
     AutoPrepareForTracing prep(rt, SkipAtoms);
 
     VerifyPostTracer *trc = (VerifyPostTracer *)rt->gcVerifyPostData;
 
     /* Visit every entry in the store buffer and put the edges in a hash set. */
-    JS_TracerInit(trc, rt, PostVerifierCollectStoreBufferEdges);
+    trc->setTraceCallback(PostVerifierCollectStoreBufferEdges);
     if (!edges.init())
         goto oom;
     trc->edges = &edges;
     rt->gcStoreBuffer.markAll(trc);
 
     /* Walk the heap to find any edges not the the |edges| set. */
-    JS_TracerInit(trc, rt, PostVerifierVisitEdge);
+    trc->setTraceCallback(PostVerifierVisitEdge);
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
         for (size_t kind = 0; kind < FINALIZE_LIMIT; ++kind) {
             for (CellIterUnderGC cells(zone, AllocKind(kind)); !cells.done(); cells.next()) {
                 Cell *src = cells.getCell();
                 JS_TraceChildren(trc, src, MapAllocToTraceKind(AllocKind(kind)));
             }
         }
     }
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -560,36 +560,36 @@ FinishAllOffThreadCompilations(JSCompart
         }
     }
 #endif
 }
 
 /* static */ void
 JitRuntime::Mark(JSTracer *trc)
 {
-    JS_ASSERT(!trc->runtime->isHeapMinorCollecting());
-    Zone *zone = trc->runtime->atomsCompartment()->zone();
+    JS_ASSERT(!trc->runtime()->isHeapMinorCollecting());
+    Zone *zone = trc->runtime()->atomsCompartment()->zone();
     for (gc::CellIterUnderGC i(zone, gc::FINALIZE_JITCODE); !i.done(); i.next()) {
         JitCode *code = i.get<JitCode>();
         MarkJitCodeRoot(trc, &code, "wrapper");
     }
 }
 
 void
 JitCompartment::mark(JSTracer *trc, JSCompartment *compartment)
 {
     // Cancel any active or pending off thread compilations. Note that the
     // MIR graph does not hold any nursery pointers, so there's no need to
     // do this for minor GCs.
-    JS_ASSERT(!trc->runtime->isHeapMinorCollecting());
+    JS_ASSERT(!trc->runtime()->isHeapMinorCollecting());
     CancelOffThreadIonCompile(compartment, nullptr);
     FinishAllOffThreadCompilations(compartment);
 
     // Free temporary OSR buffer.
-    trc->runtime->jitRuntime()->freeOsrTempData();
+    trc->runtime()->jitRuntime()->freeOsrTempData();
 
     // Mark scripts with parallel IonScripts if we should preserve them.
     if (activeParallelEntryScripts_) {
         for (ScriptSet::Enum e(*activeParallelEntryScripts_); !e.empty(); e.popFront()) {
             JSScript *script = e.front();
 
             // If the script has since been invalidated or was attached by an
             // off-thread worker too late (i.e., the ForkJoin finished with
@@ -602,17 +602,17 @@ JitCompartment::mark(JSTracer *trc, JSCo
             }
 
             // Check and increment the age. If the script is below the max
             // age, mark it.
             //
             // Subtlety: We depend on the tracing of the parallel IonScript's
             // callTargetEntries to propagate the parallel age to the entire
             // call graph.
-            if (ShouldPreserveParallelJITCode(trc->runtime, script, /* increase = */ true)) {
+            if (ShouldPreserveParallelJITCode(trc->runtime(), script, /* increase = */ true)) {
                 MarkScript(trc, const_cast<EncapsulatedPtrScript *>(&e.front()), "par-script");
                 MOZ_ASSERT(script == e.front());
             }
         }
     }
 }
 
 void
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -916,31 +916,31 @@ UpdateIonJSFrameForMinorGC(JSTracer *trc
     const SafepointIndex *si = ionScript->getSafepointIndex(frame.returnAddressToFp());
     SafepointReader safepoint(ionScript, si);
 
     GeneralRegisterSet slotsRegs = safepoint.slotsOrElementsSpills();
     uintptr_t *spill = frame.spillBase();
     for (GeneralRegisterBackwardIterator iter(safepoint.allGprSpills()); iter.more(); iter++) {
         --spill;
         if (slotsRegs.has(*iter))
-            trc->runtime->gcNursery.forwardBufferPointer(reinterpret_cast<HeapSlot **>(spill));
+            trc->runtime()->gcNursery.forwardBufferPointer(reinterpret_cast<HeapSlot **>(spill));
     }
 
     // Skip to the right place in the safepoint
     uint32_t slot;
     while (safepoint.getGcSlot(&slot));
     while (safepoint.getValueSlot(&slot));
 #ifdef JS_NUNBOX32
     LAllocation type, payload;
     while (safepoint.getNunboxSlot(&type, &payload));
 #endif
 
     while (safepoint.getSlotsOrElementsSlot(&slot)) {
         HeapSlot **slots = reinterpret_cast<HeapSlot **>(layout->slotRef(slot));
-        trc->runtime->gcNursery.forwardBufferPointer(slots);
+        trc->runtime()->gcNursery.forwardBufferPointer(slots);
     }
 }
 #endif
 
 static void
 MarkBaselineStubFrame(JSTracer *trc, const IonFrameIterator &frame)
 {
     // Mark the ICStub pointer stored in the stub frame. This is necessary
@@ -1190,17 +1190,17 @@ MarkJitActivations(JSRuntime *rt, JSTrac
     for (JitActivationIterator activations(rt); !activations.done(); ++activations)
         MarkJitActivation(trc, activations);
 }
 
 #ifdef JSGC_GENERATIONAL
 void
 UpdateJitActivationsForMinorGC(JSRuntime *rt, JSTracer *trc)
 {
-    JS_ASSERT(trc->runtime->isHeapMinorCollecting());
+    JS_ASSERT(trc->runtime()->isHeapMinorCollecting());
     for (JitActivationIterator activations(rt); !activations.done(); ++activations) {
         for (IonFrameIterator frames(activations); !frames.done(); ++frames) {
             if (frames.type() == JitFrame_IonJS)
                 UpdateIonJSFrameForMinorGC(trc, frames);
         }
     }
 }
 #endif
--- a/js/src/jsapi-tests/testIntTypesABI.cpp
+++ b/js/src/jsapi-tests/testIntTypesABI.cpp
@@ -27,17 +27,17 @@
 #include "js/MemoryMetrics.h"
 #include "js/OldDebugAPI.h"
 #include "js/ProfilingStack.h"
 #include "js/PropertyKey.h"
 #include "js/RequiredDefines.h"
 #include "js/RootingAPI.h"
 #include "js/SliceBudget.h"
 #include "js/StructuredClone.h"
-#include "js/Tracer.h"
+#include "js/TracingAPI.h"
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 #include "js/Value.h"
 #include "js/Vector.h"
 #include "js/WeakMapPtr.h"
 #include "jsapi-tests/tests.h"
 
 /*
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1640,34 +1640,40 @@ struct JSHeapDumpNode {
     JSHeapDumpNode  *parent;        /* node with the thing that refer to thing
                                        from this node */
     char            edgeName[1];    /* name of the edge from parent->thing
                                        into thing */
 };
 
 typedef HashSet<void *, PointerHasher<void *, 3>, SystemAllocPolicy> VisitedSet;
 
-typedef struct JSDumpingTracer {
+class DumpingTracer
+{
+  public:
+    DumpingTracer(JSRuntime *rt, JSTraceCallback callback)
+      : base(rt, callback)
+    {}
+
     JSTracer            base;
     VisitedSet          visited;
     bool                ok;
     void                *startThing;
     void                *thingToFind;
     void                *thingToIgnore;
     JSHeapDumpNode      *parentNode;
     JSHeapDumpNode      **lastNodep;
     char                buffer[200];
-} JSDumpingTracer;
+};
 
 static void
 DumpNotify(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
     JS_ASSERT(trc->callback == DumpNotify);
 
-    JSDumpingTracer *dtrc = (JSDumpingTracer *)trc;
+    DumpingTracer *dtrc = (DumpingTracer *)trc;
     void *thing = *thingp;
 
     if (!dtrc->ok || thing == dtrc->thingToIgnore)
         return;
 
     /*
      * Check if we have already seen thing unless it is thingToFind to include
      * it to the graph each time we reach it and print all live things that
@@ -1689,17 +1695,17 @@ DumpNotify(JSTracer *trc, void **thingp,
         if (p)
             return;
         if (!dtrc->visited.add(p, thing)) {
             dtrc->ok = false;
             return;
         }
     }
 
-    const char *edgeName = JS_GetTraceEdgeName(&dtrc->base, dtrc->buffer, sizeof(dtrc->buffer));
+    const char *edgeName = dtrc->base.getTracingEdgeName(dtrc->buffer, sizeof(dtrc->buffer));
     size_t edgeNameSize = strlen(edgeName) + 1;
     size_t bytes = offsetof(JSHeapDumpNode, edgeName) + edgeNameSize;
     JSHeapDumpNode *node = (JSHeapDumpNode *) js_malloc(bytes);
     if (!node) {
         dtrc->ok = false;
         return;
     }
 
@@ -1711,17 +1717,17 @@ DumpNotify(JSTracer *trc, void **thingp,
 
     JS_ASSERT(!*dtrc->lastNodep);
     *dtrc->lastNodep = node;
     dtrc->lastNodep = &node->next;
 }
 
 /* Dump node and the chain that leads to thing it contains. */
 static bool
-DumpNode(JSDumpingTracer *dtrc, FILE* fp, JSHeapDumpNode *node)
+DumpNode(DumpingTracer *dtrc, FILE* fp, JSHeapDumpNode *node)
 {
     JSHeapDumpNode *prev, *following;
     size_t chainLimit;
     enum { MAX_PARENTS_TO_PRINT = 10 };
 
     JS_GetTraceThingInfo(dtrc->buffer, sizeof dtrc->buffer,
                          &dtrc->base, node->thing, node->kind, true);
     if (fprintf(fp, "%p %-22s via ", node->thing, dtrc->buffer) < 0)
@@ -1781,20 +1787,19 @@ DumpNode(JSDumpingTracer *dtrc, FILE* fp
 
 JS_PUBLIC_API(bool)
 JS_DumpHeap(JSRuntime *rt, FILE *fp, void* startThing, JSGCTraceKind startKind,
             void *thingToFind, size_t maxDepth, void *thingToIgnore)
 {
     if (maxDepth == 0)
         return true;
 
-    JSDumpingTracer dtrc;
+    DumpingTracer dtrc(rt, DumpNotify);
     if (!dtrc.visited.init())
         return false;
-    JS_TracerInit(&dtrc.base, rt, DumpNotify);
     dtrc.ok = true;
     dtrc.startThing = startThing;
     dtrc.thingToFind = thingToFind;
     dtrc.thingToIgnore = thingToIgnore;
     dtrc.parentNode = nullptr;
     JSHeapDumpNode *node = nullptr;
     dtrc.lastNodep = &node;
     if (!startThing) {
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -22,24 +22,23 @@
 #include "jspubtd.h"
 
 #include "js/CallArgs.h"
 #include "js/Class.h"
 #include "js/HashTable.h"
 #include "js/Id.h"
 #include "js/Principals.h"
 #include "js/RootingAPI.h"
+#include "js/TracingAPI.h"
 #include "js/Utility.h"
 #include "js/Value.h"
 #include "js/Vector.h"
 
 /************************************************************************/
 
-struct JSTracer;
-
 namespace JS {
 
 class Latin1CharsZ;
 class TwoByteChars;
 
 #if defined JS_THREADSAFE && defined JS_DEBUG
 
 class JS_PUBLIC_API(AutoCheckRequestDepth)
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -184,34 +184,34 @@ JSRuntime::finishAtoms()
     commonNames = nullptr;
     permanentAtoms = nullptr;
     emptyString = nullptr;
 }
 
 void
 js::MarkAtoms(JSTracer *trc)
 {
-    JSRuntime *rt = trc->runtime;
+    JSRuntime *rt = trc->runtime();
     for (AtomSet::Enum e(rt->atoms()); !e.empty(); e.popFront()) {
         const AtomStateEntry &entry = e.front();
         if (!entry.isTagged())
             continue;
 
         JSAtom *atom = entry.asPtr();
         bool tagged = entry.isTagged();
         MarkStringRoot(trc, &atom, "interned_atom");
         if (entry.asPtr() != atom)
             e.rekeyFront(AtomHasher::Lookup(atom), AtomStateEntry(atom, tagged));
     }
 }
 
 void
 js::MarkPermanentAtoms(JSTracer *trc)
 {
-    JSRuntime *rt = trc->runtime;
+    JSRuntime *rt = trc->runtime();
 
     // Permanent atoms only need to be marked in the runtime which owns them.
     if (rt->parentRuntime)
         return;
 
     // Static strings are not included in the permanent atoms table.
     if (rt->staticStrings)
         rt->staticStrings->trace(trc);
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -523,17 +523,17 @@ 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());
+    JS_ASSERT(!trc->runtime()->isHeapMinorCollecting());
 
 #ifdef JS_ION
     if (jitCompartment_)
         jitCompartment_->mark(trc, this);
 #endif
 
     /*
      * If a compartment is on-stack, we mark its global so that
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -715,116 +715,114 @@ js_DumpObject(JSObject *obj)
         fprintf(stderr, "NULL\n");
         return;
     }
     obj->dump();
 }
 
 #endif
 
-struct JSDumpHeapTracer : public JSTracer
+struct DumpHeapTracer : public JSTracer
 {
     FILE   *output;
 
-    JSDumpHeapTracer(FILE *fp)
-      : output(fp)
+    DumpHeapTracer(FILE *fp, JSRuntime *rt, JSTraceCallback callback,
+                   WeakMapTraceKind weakTraceKind)
+      : JSTracer(rt, callback, weakTraceKind), output(fp)
     {}
 };
 
 static char
 MarkDescriptor(void *thing)
 {
     gc::Cell *cell = static_cast<gc::Cell*>(thing);
     if (cell->isMarked(gc::BLACK))
         return cell->isMarked(gc::GRAY) ? 'G' : 'B';
     else
         return cell->isMarked(gc::GRAY) ? 'X' : 'W';
 }
 
 static void
 DumpHeapVisitZone(JSRuntime *rt, void *data, Zone *zone)
 {
-    JSDumpHeapTracer *dtrc = static_cast<JSDumpHeapTracer *>(data);
+    DumpHeapTracer *dtrc = static_cast<DumpHeapTracer *>(data);
     fprintf(dtrc->output, "# zone %p\n", (void *)zone);
 }
 
 static void
 DumpHeapVisitCompartment(JSRuntime *rt, void *data, JSCompartment *comp)
 {
     char name[1024];
     if (rt->compartmentNameCallback)
         (*rt->compartmentNameCallback)(rt, comp, name, sizeof(name));
     else
         strcpy(name, "<unknown>");
 
-    JSDumpHeapTracer *dtrc = static_cast<JSDumpHeapTracer *>(data);
+    DumpHeapTracer *dtrc = static_cast<DumpHeapTracer *>(data);
     fprintf(dtrc->output, "# compartment %s [in zone %p]\n", name, (void *)comp->zone());
 }
 
 static void
 DumpHeapVisitArena(JSRuntime *rt, void *data, gc::Arena *arena,
                    JSGCTraceKind traceKind, size_t thingSize)
 {
-    JSDumpHeapTracer *dtrc = static_cast<JSDumpHeapTracer *>(data);
+    DumpHeapTracer *dtrc = static_cast<DumpHeapTracer *>(data);
     fprintf(dtrc->output, "# arena allockind=%u size=%u\n",
             unsigned(arena->aheader.getAllocKind()), unsigned(thingSize));
 }
 
 static void
 DumpHeapVisitCell(JSRuntime *rt, void *data, void *thing,
                   JSGCTraceKind traceKind, size_t thingSize)
 {
-    JSDumpHeapTracer *dtrc = static_cast<JSDumpHeapTracer *>(data);
+    DumpHeapTracer *dtrc = static_cast<DumpHeapTracer *>(data);
     char cellDesc[1024 * 32];
     JS_GetTraceThingInfo(cellDesc, sizeof(cellDesc), dtrc, thing, traceKind, true);
     fprintf(dtrc->output, "%p %c %s\n", thing, MarkDescriptor(thing), cellDesc);
     JS_TraceChildren(dtrc, thing, traceKind);
 }
 
 static void
 DumpHeapVisitChild(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
-    if (gc::IsInsideNursery(trc->runtime, *thingp))
+    if (gc::IsInsideNursery(trc->runtime(), *thingp))
         return;
 
-    JSDumpHeapTracer *dtrc = static_cast<JSDumpHeapTracer *>(trc);
+    DumpHeapTracer *dtrc = static_cast<DumpHeapTracer *>(trc);
     char buffer[1024];
     fprintf(dtrc->output, "> %p %c %s\n", *thingp, MarkDescriptor(*thingp),
-            JS_GetTraceEdgeName(dtrc, buffer, sizeof(buffer)));
+            dtrc->getTracingEdgeName(buffer, sizeof(buffer)));
 }
 
 static void
 DumpHeapVisitRoot(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
-    if (gc::IsInsideNursery(trc->runtime, *thingp))
+    if (gc::IsInsideNursery(trc->runtime(), *thingp))
         return;
 
-    JSDumpHeapTracer *dtrc = static_cast<JSDumpHeapTracer *>(trc);
+    DumpHeapTracer *dtrc = static_cast<DumpHeapTracer *>(trc);
     char buffer[1024];
     fprintf(dtrc->output, "%p %c %s\n", *thingp, MarkDescriptor(*thingp),
-            JS_GetTraceEdgeName(dtrc, buffer, sizeof(buffer)));
+            dtrc->getTracingEdgeName(buffer, sizeof(buffer)));
 }
 
 void
 js::DumpHeapComplete(JSRuntime *rt, FILE *fp, js::DumpHeapNurseryBehaviour nurseryBehaviour)
 {
-    JSDumpHeapTracer dtrc(fp);
-
 #ifdef JSGC_GENERATIONAL
     if (nurseryBehaviour == js::CollectNurseryBeforeDump)
         MinorGC(rt, JS::gcreason::API);
 #endif
 
-    JS_TracerInit(&dtrc, rt, DumpHeapVisitRoot);
-    dtrc.eagerlyTraceWeakMaps = TraceWeakMapKeysValues;
+    DumpHeapTracer dtrc(fp, rt, DumpHeapVisitRoot, TraceWeakMapKeysValues);
     TraceRuntime(&dtrc);
 
     fprintf(dtrc.output, "==========\n");
 
-    JS_TracerInit(&dtrc, rt, DumpHeapVisitChild);
+    dtrc.setTraceCallback(DumpHeapVisitChild);
     IterateZonesCompartmentsArenasCells(rt, &dtrc,
                                         DumpHeapVisitZone,
                                         DumpHeapVisitCompartment,
                                         DumpHeapVisitArena,
                                         DumpHeapVisitCell);
 
     fflush(dtrc.output);
 }
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1802,30 +1802,16 @@ template void *
 ArenaLists::refillFreeList<CanGC>(ThreadSafeContext *cx, AllocKind thingKind);
 
 JSGCTraceKind
 js_GetGCThingTraceKind(void *thing)
 {
     return GetGCThingTraceKind(thing);
 }
 
-void
-js::InitTracer(JSTracer *trc, JSRuntime *rt, JSTraceCallback callback)
-{
-    trc->runtime = rt;
-    trc->callback = callback;
-    trc->debugPrinter = nullptr;
-    trc->debugPrintArg = nullptr;
-    trc->debugPrintIndex = size_t(-1);
-    trc->eagerlyTraceWeakMaps = TraceWeakMapValues;
-#ifdef JS_GC_ZEAL
-    trc->realLocation = nullptr;
-#endif
-}
-
 /* static */ int64_t
 SliceBudget::TimeBudget(int64_t millis)
 {
     return millis * PRMJ_USEC_PER_MSEC;
 }
 
 /* static */ int64_t
 SliceBudget::WorkBudget(int64_t work)
@@ -1858,25 +1844,29 @@ bool
 SliceBudget::checkOverBudget()
 {
     bool over = PRMJ_Now() > deadline;
     if (!over)
         counter = CounterReset;
     return over;
 }
 
+/*
+ * DoNotTraceWeakMaps: the GC is recomputing the liveness of WeakMap entries,
+ * so we delay visting entries.
+ */
 GCMarker::GCMarker(JSRuntime *rt)
-  : stack(size_t(-1)),
+  : JSTracer(rt, nullptr, DoNotTraceWeakMaps),
+    stack(size_t(-1)),
     color(BLACK),
     started(false),
     unmarkedArenaStackTop(nullptr),
     markLaterArenas(0),
     grayBufferState(GRAY_BUFFER_UNUSED)
 {
-    InitTracer(this, rt, nullptr);
 }
 
 bool
 GCMarker::init(JSGCMode gcMode)
 {
     return stack.init(gcMode);
 }
 
@@ -1885,21 +1875,16 @@ GCMarker::start()
 {
     JS_ASSERT(!started);
     started = true;
     color = BLACK;
 
     JS_ASSERT(!unmarkedArenaStackTop);
     JS_ASSERT(markLaterArenas == 0);
 
-    /*
-     * The GC is recomputing the liveness of WeakMap entries, so we delay
-     * visting entries.
-     */
-    eagerlyTraceWeakMaps = DoNotTraceWeakMaps;
 }
 
 void
 GCMarker::stop()
 {
     JS_ASSERT(isDrained());
 
     JS_ASSERT(started);
@@ -1996,18 +1981,18 @@ GCMarker::markDelayedChildren(ArenaHeade
      * allocatedDuringIncremental flag if we continue marking.
      */
 }
 
 bool
 GCMarker::markDelayedChildren(SliceBudget &budget)
 {
     gcstats::MaybeAutoPhase ap;
-    if (runtime->gcIncrementalState == MARK)
-        ap.construct(runtime->gcStats, gcstats::PHASE_MARK_DELAYED);
+    if (runtime()->gcIncrementalState == MARK)
+        ap.construct(runtime()->gcStats, gcstats::PHASE_MARK_DELAYED);
 
     JS_ASSERT(unmarkedArenaStackTop);
     do {
         /*
          * If marking gets delayed at the same arena again, we must repeat
          * marking of its things. For that we pop arena from the stack and
          * clear its hasDelayedMarking flag before we begin the marking.
          */
@@ -2044,17 +2029,17 @@ GCMarker::hasBufferedGrayRoots() const
     return grayBufferState == GRAY_BUFFER_OK;
 }
 
 void
 GCMarker::startBufferingGrayRoots()
 {
     JS_ASSERT(grayBufferState == GRAY_BUFFER_UNUSED);
     grayBufferState = GRAY_BUFFER_OK;
-    for (GCZonesIter zone(runtime); !zone.done(); zone.next())
+    for (GCZonesIter zone(runtime()); !zone.done(); zone.next())
         JS_ASSERT(zone->gcGrayRoots.empty());
 
     JS_ASSERT(!callback);
     callback = GrayCallback;
     JS_ASSERT(IS_GC_MARKING_TRACER(this));
 }
 
 void
@@ -2065,52 +2050,50 @@ GCMarker::endBufferingGrayRoots()
     JS_ASSERT(IS_GC_MARKING_TRACER(this));
     JS_ASSERT(grayBufferState == GRAY_BUFFER_OK ||
               grayBufferState == GRAY_BUFFER_FAILED);
 }
 
 void
 GCMarker::resetBufferedGrayRoots()
 {
-    for (GCZonesIter zone(runtime); !zone.done(); zone.next())
+    for (GCZonesIter zone(runtime()); !zone.done(); zone.next())
         zone->gcGrayRoots.clearAndFree();
 }
 
 void
 GCMarker::markBufferedGrayRoots(JS::Zone *zone)
 {
     JS_ASSERT(grayBufferState == GRAY_BUFFER_OK);
     JS_ASSERT(zone->isGCMarkingGray());
 
     for (GrayRoot *elem = zone->gcGrayRoots.begin(); elem != zone->gcGrayRoots.end(); elem++) {
 #ifdef DEBUG
-        debugPrinter = elem->debugPrinter;
-        debugPrintArg = elem->debugPrintArg;
-        debugPrintIndex = elem->debugPrintIndex;
+        setTracingDetails(elem->debugPrinter, elem->debugPrintArg, elem->debugPrintIndex);
 #endif
         void *tmp = elem->thing;
-        JS_SET_TRACING_LOCATION(this, (void *)&elem->thing);
+        setTracingLocation((void *)&elem->thing);
         MarkKind(this, &tmp, elem->kind);
         JS_ASSERT(tmp == elem->thing);
     }
 }
 
 void
 GCMarker::appendGrayRoot(void *thing, JSGCTraceKind kind)
 {
     JS_ASSERT(started);
 
     if (grayBufferState == GRAY_BUFFER_FAILED)
         return;
 
     GrayRoot root(thing, kind);
 #ifdef DEBUG
-    root.debugPrinter = debugPrinter;
-    root.debugPrintArg = debugPrintArg;
-    root.debugPrintIndex = debugPrintIndex;
+    root.debugPrinter = debugPrinter();
+    root.debugPrintArg = debugPrintArg();
+    root.debugPrintIndex = debugPrintIndex();
 #endif
 
     Zone *zone = static_cast<Cell *>(thing)->tenuredZone();
     if (zone->isCollecting()) {
         zone->maybeAlive = true;
         if (!zone->gcGrayRoots.append(root)) {
             resetBufferedGrayRoots();
             grayBufferState = GRAY_BUFFER_FAILED;
@@ -2126,17 +2109,17 @@ GCMarker::GrayCallback(JSTracer *trc, vo
     GCMarker *gcmarker = static_cast<GCMarker *>(trc);
     gcmarker->appendGrayRoot(*thingp, kind);
 }
 
 size_t
 GCMarker::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
 {
     size_t size = stack.sizeOfExcludingThis(mallocSizeOf);
-    for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next())
+    for (ZonesIter zone(runtime(), WithAtoms); !zone.done(); zone.next())
         size += zone->gcGrayRoots.sizeOfExcludingThis(mallocSizeOf);
     return size;
 }
 
 void
 js::SetMarkStackLimit(JSRuntime *rt, size_t limit)
 {
     JS_ASSERT(!rt->isHeapBusy());
@@ -2898,18 +2881,23 @@ ShouldPreserveJITCode(JSCompartment *com
         return true;
     if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime)
         return true;
 
     return false;
 }
 
 #ifdef DEBUG
-struct CompartmentCheckTracer : public JSTracer
-{
+class CompartmentCheckTracer : public JSTracer
+{
+  public:
+    CompartmentCheckTracer(JSRuntime *rt, JSTraceCallback callback)
+      : JSTracer(rt, callback)
+    {}
+
     Cell *src;
     JSGCTraceKind srcKind;
     Zone *zone;
     JSCompartment *compartment;
 };
 
 static bool
 InCrossCompartmentMap(JSObject *src, Cell *dst, JSGCTraceKind dstKind)
@@ -2936,17 +2924,17 @@ InCrossCompartmentMap(JSObject *src, Cel
     return false;
 }
 
 static void
 CheckCompartment(CompartmentCheckTracer *trc, JSCompartment *thingCompartment,
                  Cell *thing, JSGCTraceKind kind)
 {
     JS_ASSERT(thingCompartment == trc->compartment ||
-              trc->runtime->isAtomsCompartment(thingCompartment) ||
+              trc->runtime()->isAtomsCompartment(thingCompartment) ||
               (trc->srcKind == JSTRACE_OBJECT &&
                InCrossCompartmentMap((JSObject *)trc->src, thing, kind)));
 }
 
 static JSCompartment *
 CompartmentOfCell(Cell *thing, JSGCTraceKind kind)
 {
     if (kind == JSTRACE_OBJECT)
@@ -2967,29 +2955,27 @@ CheckCompartmentCallback(JSTracer *trcAr
     CompartmentCheckTracer *trc = static_cast<CompartmentCheckTracer *>(trcArg);
     Cell *thing = (Cell *)*thingp;
 
     JSCompartment *comp = CompartmentOfCell(thing, kind);
     if (comp && trc->compartment) {
         CheckCompartment(trc, comp, thing, kind);
     } else {
         JS_ASSERT(thing->tenuredZone() == trc->zone ||
-                  trc->runtime->isAtomsZone(thing->tenuredZone()));
+                  trc->runtime()->isAtomsZone(thing->tenuredZone()));
     }
 }
 
 static void
 CheckForCompartmentMismatches(JSRuntime *rt)
 {
     if (rt->gcDisableStrictProxyCheckingCount)
         return;
 
-    CompartmentCheckTracer trc;
-    JS_TracerInit(&trc, rt, CheckCompartmentCallback);
-
+    CompartmentCheckTracer trc(rt, CheckCompartmentCallback);
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
         trc.zone = zone;
         for (size_t thingKind = 0; thingKind < FINALIZE_LAST; thingKind++) {
             for (CellIterUnderGC i(zone, AllocKind(thingKind)); !i.done(); i.next()) {
                 trc.src = i.getCell();
                 trc.srcKind = MapAllocToTraceKind(AllocKind(thingKind));
                 trc.compartment = CompartmentOfCell(trc.src, trc.srcKind);
                 JS_TraceChildren(&trc, trc.src, trc.srcKind);
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -10,19 +10,19 @@
 #define jsgc_h
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "jslock.h"
 #include "jsobj.h"
 
+#include "gc/Tracer.h"
 #include "js/GCAPI.h"
 #include "js/SliceBudget.h"
-#include "js/Tracer.h"
 #include "js/Vector.h"
 
 class JSAtom;
 struct JSCompartment;
 class JSFlatString;
 class JSLinearString;
 
 namespace js {
@@ -771,19 +771,16 @@ extern void
 NotifyGCNukeWrapper(JSObject *o);
 
 extern unsigned
 NotifyGCPreSwap(JSObject *a, JSObject *b);
 
 extern void
 NotifyGCPostSwap(JSObject *a, JSObject *b, unsigned preResult);
 
-void
-InitTracer(JSTracer *trc, JSRuntime *rt, JSTraceCallback callback);
-
 /*
  * Helper that implements sweeping and allocation for kinds that can be swept
  * and allocated off the main thread.
  *
  * In non-threadsafe builds, all actual sweeping and allocation is performed
  * on the main thread, but GCHelperThread encapsulates this from clients as
  * much as possible.
  */
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -3789,17 +3789,17 @@ class NewTypeObjectsSetRef : public Buff
 
   public:
     NewTypeObjectsSetRef(TypeObjectWithNewScriptSet *s, const Class *clasp, JSObject *proto, JSFunction *newFunction)
         : set(s), clasp(clasp), proto(proto), newFunction(newFunction)
     {}
 
     void mark(JSTracer *trc) {
         JSObject *prior = proto;
-        JS_SET_TRACING_LOCATION(trc, (void*)&*prior);
+        trc->setTracingLocation(&*prior);
         Mark(trc, &proto, "newTypeObjects set prototype");
         if (prior == proto)
             return;
 
         TypeObjectWithNewScriptSet::Ptr p = set->lookup(TypeObjectWithNewScriptSet::Lookup(clasp, prior, proto, newFunction));
         JS_ASSERT(p);  // newTypeObjects set must still contain original entry.
 
         set->rekeyAs(TypeObjectWithNewScriptSet::Lookup(clasp, prior, proto, newFunction),
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5544,20 +5544,20 @@ js::ToObjectSlow(JSContext *cx, HandleVa
     }
 
     return PrimitiveToObject(cx, val);
 }
 
 void
 js_GetObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
 {
-    JS_ASSERT(trc->debugPrinter == js_GetObjectSlotName);
-
-    JSObject *obj = (JSObject *)trc->debugPrintArg;
-    uint32_t slot = uint32_t(trc->debugPrintIndex);
+    JS_ASSERT(trc->debugPrinter() == js_GetObjectSlotName);
+
+    JSObject *obj = (JSObject *)trc->debugPrintArg();
+    uint32_t slot = uint32_t(trc->debugPrintIndex());
 
     Shape *shape;
     if (obj->isNative()) {
         shape = obj->lastProperty();
         while (shape && (!shape->hasSlot() || shape->slot() != slot))
             shape = shape->previous();
     } else {
         shape = nullptr;
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -3009,17 +3009,17 @@ js::proxy_Trace(JSTracer *trc, JSObject 
 }
 
 /* static */ void
 ProxyObject::trace(JSTracer *trc, JSObject *obj)
 {
     ProxyObject *proxy = &obj->as<ProxyObject>();
 
 #ifdef DEBUG
-    if (!trc->runtime->gcDisableStrictProxyCheckingCount && proxy->is<WrapperObject>()) {
+    if (!trc->runtime()->gcDisableStrictProxyCheckingCount && proxy->is<WrapperObject>()) {
         JSObject *referent = &proxy->private_().toObject();
         if (referent->compartment() != proxy->compartment()) {
             /*
              * Assert that this proxy is tracked in the wrapper map. We maintain
              * the invariant that the wrapped object is the key in the wrapper map.
              */
             Value key = ObjectValue(*referent);
             WrapperMap::Ptr p = proxy->compartment()->lookupWrapper(key);
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -130,19 +130,19 @@ struct JSPrincipals;
 struct JSPropertyDescriptor;
 struct JSPropertyName;
 struct JSPropertySpec;
 struct JSRuntime;
 struct JSSecurityCallbacks;
 struct JSStructuredCloneCallbacks;
 struct JSStructuredCloneReader;
 struct JSStructuredCloneWriter;
-struct JSTracer;
+class JSTracer;
 
-class                                       JSFlatString;
+class JSFlatString;
 
 #ifdef JS_THREADSAFE
 typedef struct PRCallOnceType   JSCallOnceType;
 #else
 typedef bool                    JSCallOnceType;
 #endif
 typedef bool                    (*JSInitCallback)(void);
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -3293,17 +3293,17 @@ JSScript::clearTraps(FreeOp *fop)
 void
 JSScript::markChildren(JSTracer *trc)
 {
     // NOTE: this JSScript may be partially initialized at this point.  E.g. we
     // may have created it and partially initialized it with
     // JSScript::Create(), but not yet finished initializing it with
     // fullyInitFromEmitter() or fullyInitTrivial().
 
-    JS_ASSERT_IF(trc->runtime->gcStrictCompartmentChecking, zone()->isCollecting());
+    JS_ASSERT_IF(trc->runtime()->gcStrictCompartmentChecking, zone()->isCollecting());
 
     for (uint32_t i = 0; i < natoms(); ++i) {
         if (atoms[i])
             MarkString(trc, &atoms[i], "atom");
     }
 
     if (hasObjects()) {
         ObjectArray *objarray = objects();
@@ -3333,17 +3333,17 @@ JSScript::markChildren(JSTracer *trc)
 
     if (maybeLazyScript())
         MarkLazyScriptUnbarriered(trc, &lazyScript, "lazyScript");
 
     if (IS_GC_MARKING_TRACER(trc)) {
         compartment()->mark();
 
         if (code())
-            MarkScriptData(trc->runtime, code());
+            MarkScriptData(trc->runtime(), code());
     }
 
     bindings.trace(trc);
 
     if (hasAnyBreakpointsOrStepMode()) {
         for (unsigned i = 0; i < length(); i++) {
             BreakpointSite *site = debugScript()->breakpoints[i];
             if (site && site->trapHandler)
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -43,34 +43,34 @@ class WeakMapBase {
     virtual ~WeakMapBase();
 
     void trace(JSTracer *tracer) {
         if (IS_GC_MARKING_TRACER(tracer)) {
             // We don't do anything with a WeakMap at trace time. Rather, we wait until as
             // many keys as possible have been marked, and add ourselves to the list of
             // known-live WeakMaps to be scanned in the iterative marking phase, by
             // markAllIteratively.
-            JS_ASSERT(tracer->eagerlyTraceWeakMaps == DoNotTraceWeakMaps);
+            JS_ASSERT(tracer->eagerlyTraceWeakMaps() == DoNotTraceWeakMaps);
 
             // Add ourselves to the list if we are not already in the list. We can already
             // be in the list if the weak map is marked more than once due delayed marking.
             if (next == WeakMapNotInList) {
                 next = compartment->gcWeakMapList;
                 compartment->gcWeakMapList = this;
             }
         } else {
             // If we're not actually doing garbage collection, the keys won't be marked
             // nicely as needed by the true ephemeral marking algorithm --- custom tracers
             // such as the cycle collector must use their own means for cycle detection.
             // So here we do a conservative approximation: pretend all keys are live.
-            if (tracer->eagerlyTraceWeakMaps == DoNotTraceWeakMaps)
+            if (tracer->eagerlyTraceWeakMaps() == DoNotTraceWeakMaps)
                 return;
 
             nonMarkingTraceValues(tracer);
-            if (tracer->eagerlyTraceWeakMaps == TraceWeakMapKeysValues)
+            if (tracer->eagerlyTraceWeakMaps() == TraceWeakMapKeysValues)
                 nonMarkingTraceKeys(tracer);
         }
     }
 
     // Garbage collector entry points.
 
     // Check all weak maps in a compartment that have been marked as live in this garbage
     // collection, and mark the values of all entries that have become strong references
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -79,17 +79,17 @@ EXPORTS.js += [
     '../public/OldDebugAPI.h',
     '../public/Principals.h',
     '../public/ProfilingStack.h',
     '../public/PropertyKey.h',
     '../public/RequiredDefines.h',
     '../public/RootingAPI.h',
     '../public/SliceBudget.h',
     '../public/StructuredClone.h',
-    '../public/Tracer.h',
+    '../public/TracingAPI.h',
     '../public/TypeDecls.h',
     '../public/Utility.h',
     '../public/Value.h',
     '../public/Vector.h',
     '../public/WeakMapPtr.h',
 ]
 
 UNIFIED_SOURCES += [
--- a/js/src/shell/jsheaptools.cpp
+++ b/js/src/shell/jsheaptools.cpp
@@ -149,22 +149,22 @@ class HeapReverser : public JSTracer, pu
      * The result of a reversal is a map from Cells' addresses to Node
      * structures describing their incoming edges.
      */
     typedef HashMap<void *, Node, DefaultHasher<void *>, SystemAllocPolicy> Map;
     Map map;
 
     /* Construct a HeapReverser for |context|'s heap. */
     HeapReverser(JSContext *cx)
-      : JS::CustomAutoRooter(cx),
+      : JSTracer(cx->runtime(), traverseEdgeWithThis),
+        JS::CustomAutoRooter(cx),
         noggc(JS_GetRuntime(cx)),
         runtime(JS_GetRuntime(cx)),
         parent(nullptr)
     {
-        JS_TracerInit(this, runtime, traverseEdgeWithThis);
     }
 
     bool init() { return map.init(); }
 
     /* Build a reversed map of the heap in |map|. */
     bool reverseHeap();
 
   private:
@@ -303,35 +303,35 @@ HeapReverser::reverseHeap()
     }
 
     return true;
 }
 
 char *
 HeapReverser::getEdgeDescription()
 {
-    if (!debugPrinter && debugPrintIndex == (size_t) -1) {
-        const char *arg = static_cast<const char *>(debugPrintArg);
+    if (!debugPrinter() && debugPrintIndex() == (size_t) -1) {
+        const char *arg = static_cast<const char *>(debugPrintArg());
         char *name = js_pod_malloc<char>(strlen(arg) + 1);
         if (!name)
             return nullptr;
         strcpy(name, arg);
         return name;
     }
 
     /* Lovely; but a fixed size is required by JSTraceNamePrinter. */
     static const int nameSize = 200;
     char *name = js_pod_malloc<char>(nameSize);
     if (!name)
         return nullptr;
-    if (debugPrinter)
-        debugPrinter(this, name, nameSize);
+    if (debugPrinter())
+        debugPrinter()(this, name, nameSize);
     else
         JS_snprintf(name, nameSize, "%s[%lu]",
-                    static_cast<const char *>(debugPrintArg), debugPrintIndex);
+                    static_cast<const char *>(debugPrintArg()), debugPrintIndex());
 
     /* Shrink storage to fit. */
     return static_cast<char *>(js_realloc(name, strlen(name) + 1));
 }
 
 
 /*** class ReferenceFinder ***********************************************************************/
 
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -790,17 +790,17 @@ ArrayBufferObject::finalize(FreeOp *fop,
 
     if (buffer.ownsData())
         buffer.releaseData(fop);
 }
 
 /* static */ void
 ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj)
 {
-    if (!IS_GC_MARKING_TRACER(trc) && !trc->runtime->isHeapMinorCollecting())
+    if (!IS_GC_MARKING_TRACER(trc) && !trc->runtime()->isHeapMinorCollecting())
         return;
 
     // ArrayBufferObjects need to maintain a list of possibly-weak pointers to
     // their views. The straightforward way to update the weak pointers would
     // be in the views' finalizers, but giving views finalizers means they
     // cannot be swept in the background. This results in a very high
     // performance cost.  Instead, ArrayBufferObjects with a single view hold a
     // strong pointer to the view. This can entrain garbage when the single
@@ -810,17 +810,17 @@ ArrayBufferObject::obj_trace(JSTracer *t
     // multiple views are collected into a linked list during collection, and
     // then swept to prune out their dead views.
 
     ArrayBufferObject &buffer = AsArrayBuffer(obj);
     ArrayBufferViewObject *viewsHead = buffer.viewList();
     if (!viewsHead)
         return;
 
-    buffer.setViewList(UpdateObjectIfRelocated(trc->runtime, &viewsHead));
+    buffer.setViewList(UpdateObjectIfRelocated(trc->runtime(), &viewsHead));
 
     if (viewsHead->nextView() == nullptr) {
         // Single view: mark it, but only if we're actually doing a GC pass
         // right now. Otherwise, the tracing pass for barrier verification will
         // fail if we add another view and the pointer becomes weak.
         MarkObjectUnbarriered(trc, &viewsHead, "arraybuffer.singleview");
         buffer.setViewListNoBarrier(viewsHead);
     } else {
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -1423,27 +1423,27 @@ Debugger::slowPathOnNewGlobalObject(JSCo
     }
     JS_ASSERT(!cx->isExceptionPending());
 }
 
 
 /*** Debugger JSObjects **************************************************************************/
 
 void
-Debugger::markKeysInCompartment(JSTracer *tracer)
+Debugger::markKeysInCompartment(JSTracer *trc)
 {
     /*
      * WeakMap::Range is deliberately private, to discourage C++ code from
      * enumerating WeakMap keys. However in this case we need access, so we
      * make a base-class reference. Range is public in HashMap.
      */
-    objects.markKeys(tracer);
-    environments.markKeys(tracer);
-    scripts.markKeys(tracer);
-    sources.markKeys(tracer);
+    objects.markKeys(trc);
+    environments.markKeys(trc);
+    scripts.markKeys(trc);
+    sources.markKeys(trc);
 }
 
 /*
  * Ordinarily, WeakMap keys and values are marked because at some point it was
  * discovered that the WeakMap was live; that is, some object containing the
  * WeakMap was marked during mark phase.
  *
  * However, during compartment GC, we have to do something about
@@ -1459,27 +1459,27 @@ Debugger::markKeysInCompartment(JSTracer
  * We must scan all Debugger objects regardless of whether they *currently*
  * have any debuggees in a compartment being GC'd, because the WeakMap
  * entries persist even when debuggees are removed.
  *
  * This happens during the initial mark phase, not iterative marking, because
  * all the edges being reported here are strong references.
  */
 void
-Debugger::markCrossCompartmentDebuggerObjectReferents(JSTracer *tracer)
-{
-    JSRuntime *rt = tracer->runtime;
+Debugger::markCrossCompartmentDebuggerObjectReferents(JSTracer *trc)
+{
+    JSRuntime *rt = trc->runtime();
 
     /*
      * Mark all objects in comp that are referents of Debugger.Objects in other
      * compartments.
      */
     for (Debugger *dbg = rt->debuggerList.getFirst(); dbg; dbg = dbg->getNext()) {
         if (!dbg->object->zone()->isCollecting())
-            dbg->markKeysInCompartment(tracer);
+            dbg->markKeysInCompartment(trc);
     }
 }
 
 /*
  * This method has two tasks:
  *   1. Mark Debugger objects that are unreachable except for debugger hooks that
  *      may yet be called.
  *   2. Mark breakpoint handlers.
@@ -1492,17 +1492,17 @@ bool
 Debugger::markAllIteratively(GCMarker *trc)
 {
     bool markedAny = false;
 
     /*
      * Find all Debugger objects in danger of GC. This code is a little
      * convoluted since the easiest way to find them is via their debuggees.
      */
-    JSRuntime *rt = trc->runtime;
+    JSRuntime *rt = trc->runtime();
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         GlobalObjectSet &debuggees = c->getDebuggees();
         for (GlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) {
             GlobalObject *global = e.front();
             if (!IsObjectMarked(&global))
                 continue;
             else if (global != e.front())
                 e.rekeyFront(global);
@@ -1561,17 +1561,17 @@ Debugger::markAllIteratively(GCMarker *t
 /*
  * Mark all debugger-owned GC things unconditionally. This is used by the minor
  * GC: the minor GC cannot apply the weak constraints of the full GC because it
  * visits only part of the heap.
  */
 void
 Debugger::markAll(JSTracer *trc)
 {
-    JSRuntime *rt = trc->runtime;
+    JSRuntime *rt = trc->runtime();
     for (Debugger *dbg = rt->debuggerList.getFirst(); dbg; dbg = dbg->getNext()) {
         GlobalObjectSet &debuggees = dbg->debuggees;
         for (GlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) {
             GlobalObject *global = e.front();
 
             MarkObjectUnbarriered(trc, &global, "Global Object");
             if (global != e.front())
                 e.rekeyFront(global);
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -208,17 +208,17 @@ VectorMatchPairs::allocOrExpandArray(siz
 static void
 regexp_trace(JSTracer *trc, JSObject *obj)
 {
      /*
       * We have to check both conditions, since:
       *   1. During TraceRuntime, isHeapBusy() is true
       *   2. When a write barrier executes, IS_GC_MARKING_TRACER is true.
       */
-    if (trc->runtime->isHeapBusy() && IS_GC_MARKING_TRACER(trc))
+    if (trc->runtime()->isHeapBusy() && IS_GC_MARKING_TRACER(trc))
         obj->setPrivate(nullptr);
 }
 
 const Class RegExpObject::class_ = {
     js_RegExp_str,
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1597,17 +1597,17 @@ class DebugScopes::MissingScopesRef : pu
   public:
     MissingScopesRef(MissingScopeMap *m, const ScopeIterKey &k) : map(m), key(k) {}
 
     void mark(JSTracer *trc) {
         ScopeIterKey prior = key;
         MissingScopeMap::Ptr p = map->lookup(key);
         if (!p)
             return;
-        JS_SET_TRACING_LOCATION(trc, &const_cast<ScopeIterKey &>(p->key()).enclosingScope());
+        trc->setTracingLocation(&const_cast<ScopeIterKey &>(p->key()).enclosingScope());
         Mark(trc, &key.enclosingScope(), "MissingScopesRef");
         map->rekeyIfMoved(prior, key);
     }
 };
 #endif
 
 /* static */ MOZ_ALWAYS_INLINE void
 DebugScopes::missingScopesPostWriteBarrier(JSRuntime *rt, MissingScopeMap *map,
--- a/js/xpconnect/src/XPCVariant.cpp
+++ b/js/xpconnect/src/XPCVariant.cpp
@@ -65,25 +65,25 @@ XPCTraceableVariant::~XPCTraceableVarian
 
     if (!JSVAL_IS_NULL(val))
         RemoveFromRootSet();
 }
 
 void XPCTraceableVariant::TraceJS(JSTracer* trc)
 {
     MOZ_ASSERT(mJSVal.isMarkable());
-    JS_SET_TRACING_DETAILS(trc, GetTraceName, this, 0);
+    trc->setTracingDetails(GetTraceName, this, 0);
     JS_CallHeapValueTracer(trc, &mJSVal, "XPCTraceableVariant::mJSVal");
 }
 
 // static
 void
 XPCTraceableVariant::GetTraceName(JSTracer* trc, char *buf, size_t bufsize)
 {
-    JS_snprintf(buf, bufsize, "XPCVariant[0x%p].mJSVal", trc->debugPrintArg);
+    JS_snprintf(buf, bufsize, "XPCVariant[0x%p].mJSVal", trc->debugPrintArg());
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XPCVariant)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XPCVariant)
     JS::Value val = tmp->GetJSValPreserveColor();
     if (val.isObjectOrNull()) {
         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSVal");
--- a/js/xpconnect/src/XPCWrappedJS.cpp
+++ b/js/xpconnect/src/XPCWrappedJS.cpp
@@ -279,26 +279,26 @@ nsXPCWrappedJS::DeleteCycleCollectable(v
 {
     delete this;
 }
 
 void
 nsXPCWrappedJS::TraceJS(JSTracer* trc)
 {
     MOZ_ASSERT(mRefCnt >= 2 && IsValid(), "must be strongly referenced");
-    JS_SET_TRACING_DETAILS(trc, GetTraceName, this, 0);
+    trc->setTracingDetails(GetTraceName, this, 0);
     JS_CallHeapObjectTracer(trc, &mJSObj, "nsXPCWrappedJS::mJSObj");
 }
 
 // static
 void
 nsXPCWrappedJS::GetTraceName(JSTracer* trc, char *buf, size_t bufsize)
 {
     const nsXPCWrappedJS* self = static_cast<const nsXPCWrappedJS*>
-                                            (trc->debugPrintArg);
+                                            (trc->debugPrintArg());
     JS_snprintf(buf, bufsize, "nsXPCWrappedJS[%s,0x%p:0x%p].mJSObj",
                 self->GetClass()->GetInterfaceName(), self, self->mXPTCStub);
 }
 
 NS_IMETHODIMP
 nsXPCWrappedJS::GetWeakReference(nsIWeakReference** aInstancePtr)
 {
     if (!IsRootWrapper())
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -2672,26 +2672,26 @@ XPCJSObjectHolder::XPCJSObjectHolder(JSO
 XPCJSObjectHolder::~XPCJSObjectHolder()
 {
     RemoveFromRootSet();
 }
 
 void
 XPCJSObjectHolder::TraceJS(JSTracer *trc)
 {
-    JS_SET_TRACING_DETAILS(trc, GetTraceName, this, 0);
+    trc->setTracingDetails(GetTraceName, this, 0);
     JS_CallHeapObjectTracer(trc, &mJSObj, "XPCJSObjectHolder::mJSObj");
 }
 
 // static
 void
 XPCJSObjectHolder::GetTraceName(JSTracer* trc, char *buf, size_t bufsize)
 {
     JS_snprintf(buf, bufsize, "XPCJSObjectHolder[0x%p].mJSObj",
-                trc->debugPrintArg);
+                trc->debugPrintArg());
 }
 
 // static
 XPCJSObjectHolder*
 XPCJSObjectHolder::newHolder(JSObject* obj)
 {
     if (!obj) {
         NS_ERROR("bad param");
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -318,28 +318,31 @@ nsXPConnect::InitClasses(JSContext * aJS
 
     if (!XPCNativeWrapper::AttachNewConstructorObject(aJSContext, globalJSObj))
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     return NS_OK;
 }
 
 #ifdef DEBUG
-struct VerifyTraceXPCGlobalCalledTracer
-{
-    JSTracer base;
-    bool ok;
-};
-
 static void
 VerifyTraceXPCGlobalCalled(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
     // We don't do anything here, we only want to verify that TraceXPCGlobal
     // was called.
 }
+
+struct VerifyTraceXPCGlobalCalledTracer : public JSTracer
+{
+    bool ok;
+
+    VerifyTraceXPCGlobalCalledTracer(JSRuntime *rt)
+      : JSTracer(rt, VerifyTraceXPCGlobalCalled), ok(false)
+    {}
+};
 #endif
 
 void
 TraceXPCGlobal(JSTracer *trc, JSObject *obj)
 {
 #ifdef DEBUG
     if (trc->callback == VerifyTraceXPCGlobalCalled) {
         // We don't do anything here, we only want to verify that TraceXPCGlobal
@@ -374,20 +377,18 @@ CreateGlobalObject(JSContext *cx, const 
     (void) new XPCWrappedNativeScope(cx, global);
 
 #ifdef DEBUG
     // Verify that the right trace hook is called. Note that this doesn't
     // work right for wrapped globals, since the tracing situation there is
     // more complicated. Manual inspection shows that they do the right thing.
     if (!((const js::Class*)clasp)->ext.isWrappedNative)
     {
-        VerifyTraceXPCGlobalCalledTracer trc;
-        JS_TracerInit(&trc.base, JS_GetRuntime(cx), VerifyTraceXPCGlobalCalled);
-        trc.ok = false;
-        JS_TraceChildren(&trc.base, global, JSTRACE_OBJECT);
+        VerifyTraceXPCGlobalCalledTracer trc(JS_GetRuntime(cx));
+        JS_TraceChildren(&trc, global, JSTRACE_OBJECT);
         MOZ_ASSERT(trc.ok, "Trace hook on global needs to call TraceXPCGlobal for XPConnect compartments.");
     }
 #endif
 
     if (clasp->flags & JSCLASS_DOM_GLOBAL) {
         const char* className = clasp->name;
         AllocateProtoAndIfaceCache(global,
                                    (strcmp(className, "Window") == 0 ||
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -85,17 +85,17 @@
 #include "mozilla/TimeStamp.h"
 
 #include <math.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "xpcpublic.h"
-#include "js/Tracer.h"
+#include "js/TracingAPI.h"
 #include "js/WeakMapPtr.h"
 #include "pldhash.h"
 #include "nscore.h"
 #include "nsXPCOM.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDebug.h"
 #include "nsISupports.h"
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -112,20 +112,23 @@ public:
 } // namespace mozilla
 
 inline bool
 AddToCCKind(JSGCTraceKind kind)
 {
   return kind == JSTRACE_OBJECT || kind == JSTRACE_SCRIPT;
 }
 
+static void
+TraceWeakMappingChild(JSTracer* trc, void** thingp, JSGCTraceKind kind);
+
 struct NoteWeakMapChildrenTracer : public JSTracer
 {
-  NoteWeakMapChildrenTracer(nsCycleCollectionNoteRootCallback& cb)
-  : mCb(cb)
+  NoteWeakMapChildrenTracer(JSRuntime *rt, nsCycleCollectionNoteRootCallback& cb)
+  : JSTracer(rt, TraceWeakMappingChild), mCb(cb)
   {
   }
   nsCycleCollectionNoteRootCallback& mCb;
   bool mTracedAny;
   JSObject* mMap;
   void* mKey;
   void* mKeyDelegate;
 };
@@ -153,19 +156,18 @@ TraceWeakMappingChild(JSTracer* trc, voi
     JS_TraceChildren(trc, thing, kind);
   }
 }
 
 struct NoteWeakMapsTracer : public js::WeakMapTracer
 {
   NoteWeakMapsTracer(JSRuntime* rt, js::WeakMapTraceCallback cb,
                      nsCycleCollectionNoteRootCallback& cccb)
-  : js::WeakMapTracer(rt, cb), mCb(cccb), mChildTracer(cccb)
+  : js::WeakMapTracer(rt, cb), mCb(cccb), mChildTracer(rt, cccb)
   {
-    JS_TracerInit(&mChildTracer, rt, TraceWeakMappingChild);
   }
   nsCycleCollectionNoteRootCallback& mCb;
   NoteWeakMapChildrenTracer mChildTracer;
 };
 
 static void
 TraceWeakMapping(js::WeakMapTracer* trc, JSObject* m,
                void* k, JSGCTraceKind kkind,
@@ -354,19 +356,23 @@ JSZoneParticipant::Traverse(void* p, nsC
 
   MOZ_ASSERT(!cb.WantAllTraces());
   JS::Zone* zone = static_cast<JS::Zone*>(p);
 
   runtime->TraverseZone(zone, cb);
   return NS_OK;
 }
 
+static void
+NoteJSChildTracerShim(JSTracer* aTrc, void** aThingp, JSGCTraceKind aTraceKind);
+
 struct TraversalTracer : public JSTracer
 {
-  TraversalTracer(nsCycleCollectionTraversalCallback& aCb) : mCb(aCb)
+  TraversalTracer(JSRuntime *rt, nsCycleCollectionTraversalCallback& aCb)
+    : JSTracer(rt, NoteJSChildTracerShim, DoNotTraceWeakMaps), mCb(aCb)
   {
   }
   nsCycleCollectionTraversalCallback& mCb;
 };
 
 static void
 NoteJSChild(JSTracer* aTrc, void* aThing, JSGCTraceKind aTraceKind)
 {
@@ -380,31 +386,31 @@ NoteJSChild(JSTracer* aTrc, void* aThing
   /*
    * This function needs to be careful to avoid stack overflow. Normally, when
    * AddToCCKind is true, the recursion terminates immediately as we just add
    * |thing| to the CC graph. So overflow is only possible when there are long
    * chains of non-AddToCCKind GC things. Currently, this only can happen via
    * shape parent pointers. The special JSTRACE_SHAPE case below handles
    * parent pointers iteratively, rather than recursively, to avoid overflow.
    */
-if (AddToCCKind(aTraceKind)) {
+  if (AddToCCKind(aTraceKind)) {
     if (MOZ_UNLIKELY(tracer->mCb.WantDebugInfo())) {
-      // based on DumpNotify in jsapi.c
-      if (tracer->debugPrinter) {
+      // based on DumpNotify in jsapi.cpp
+      if (tracer->debugPrinter()) {
         char buffer[200];
-        tracer->debugPrinter(aTrc, buffer, sizeof(buffer));
+        tracer->debugPrinter()(aTrc, buffer, sizeof(buffer));
         tracer->mCb.NoteNextEdgeName(buffer);
-      } else if (tracer->debugPrintIndex != (size_t)-1) {
+      } else if (tracer->debugPrintIndex() != (size_t)-1) {
         char buffer[200];
         JS_snprintf(buffer, sizeof(buffer), "%s[%lu]",
-                    static_cast<const char *>(tracer->debugPrintArg),
-                    tracer->debugPrintIndex);
+                    static_cast<const char *>(tracer->debugPrintArg()),
+                    tracer->debugPrintIndex());
         tracer->mCb.NoteNextEdgeName(buffer);
       } else {
-        tracer->mCb.NoteNextEdgeName(static_cast<const char*>(tracer->debugPrintArg));
+        tracer->mCb.NoteNextEdgeName(static_cast<const char*>(tracer->debugPrintArg()));
       }
     }
     tracer->mCb.NoteJSChild(aThing);
   } else if (aTraceKind == JSTRACE_SHAPE) {
     JS_TraceShapeCycleCollectorChildren(aTrc, aThing);
   } else if (aTraceKind != JSTRACE_STRING) {
     JS_TraceChildren(aTrc, aThing, aTraceKind);
   }
@@ -571,19 +577,17 @@ CycleCollectedJSRuntime::DescribeGCThing
 }
 
 void
 CycleCollectedJSRuntime::NoteGCThingJSChildren(void* aThing,
                                                JSGCTraceKind aTraceKind,
                                                nsCycleCollectionTraversalCallback& aCb) const
 {
   MOZ_ASSERT(mJSRuntime);
-  TraversalTracer trc(aCb);
-  JS_TracerInit(&trc, mJSRuntime, NoteJSChildTracerShim);
-  trc.eagerlyTraceWeakMaps = DoNotTraceWeakMaps;
+  TraversalTracer trc(mJSRuntime, aCb);
   JS_TraceChildren(&trc, aThing, aTraceKind);
 }
 
 void
 CycleCollectedJSRuntime::NoteGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj,
                                                   nsCycleCollectionTraversalCallback& aCb) const
 {
   MOZ_ASSERT(aClasp);
@@ -667,19 +671,17 @@ CycleCollectedJSRuntime::TraverseZone(JS
   /*
    * Every JS child of everything in the zone is either in the zone
    * or is a cross-compartment wrapper. In the former case, we don't need to
    * represent these edges in the CC graph because JS objects are not ref counted.
    * In the latter case, the JS engine keeps a map of these wrappers, which we
    * iterate over. Edges between compartments in the same zone will add
    * unnecessary loop edges to the graph (bug 842137).
    */
-  TraversalTracer trc(aCb);
-  JS_TracerInit(&trc, mJSRuntime, NoteJSChildTracerShim);
-  trc.eagerlyTraceWeakMaps = DoNotTraceWeakMaps;
+  TraversalTracer trc(mJSRuntime, aCb);
   js::VisitGrayWrapperTargets(aZone, NoteJSChildGrayWrapperShim, &trc);
 
   /*
    * To find C++ children of things in the zone, we scan every JS Object in
    * the zone. Only JS Objects can have C++ children.
    */
   TraverseObjectShimClosure closure = { aCb, this };
   js::IterateGrayObjects(aZone, TraverseObjectShim, &closure);
--- a/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp
+++ b/xpcom/glue/tests/gtest/TestGCPostBarriers.cpp
@@ -10,17 +10,17 @@
  * implemented for nsTArrays that contain JavaScript Values.
  */
 
 #include "jsapi.h"
 #include "nsTArray.h"
 
 #include "gtest/gtest.h"
 
-#include "js/Tracer.h"
+#include "js/TracingAPI.h"
 #include "js/HeapAPI.h"
 
 #include "mozilla/CycleCollectedJSRuntime.h"
 
 using namespace JS;
 using namespace mozilla;
 
 template <class ArrayT>