Bug 616666 - Use explicit stack for GC marking (r=gal)
authorBill McCloskey <wmccloskey@mozilla.com>
Fri, 15 Apr 2011 16:56:08 -0700
changeset 68575 3e5aaea1ccf87aef2f17c6e71eb9958664e6eb82
parent 68574 e677c55655ea68a07226bb5c0cd98fb1d54f5369
child 68576 d19afdbdd3fb4b896309e2a23c1209c1da930703
push id19680
push usercleary@mozilla.com
push dateTue, 26 Apr 2011 17:44:40 +0000
treeherdermozilla-central@28bc239d3d9d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgal
bugs616666
milestone6.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 616666 - Use explicit stack for GC marking (r=gal)
js/src/Makefile.in
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsatom.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jsdbgapi.cpp
js/src/jsexn.cpp
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsgcinlines.h
js/src/jsgcmark.cpp
js/src/jsgcmark.h
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsparse.cpp
js/src/jsproxy.cpp
js/src/jsregexp.cpp
js/src/jsscope.cpp
js/src/jsscope.h
js/src/jsscript.cpp
js/src/jsstr.cpp
js/src/jsstr.h
js/src/jstracer.cpp
js/src/jstypedarray.cpp
js/src/jsweakmap.cpp
js/src/jswrapper.cpp
js/src/jsxml.cpp
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -139,16 +139,17 @@ CPPSRCS		= \
 		jsdbgapi.cpp \
 		jsdhash.cpp \
 		jsdtoa.cpp \
 		jsemit.cpp \
 		jsexn.cpp \
 		jsfriendapi.cpp \
 		jsfun.cpp \
 		jsgc.cpp \
+		jsgcmark.cpp \
 		jsgcchunk.cpp \
 		jsgcstats.cpp \
 		jshash.cpp \
 		jsinterp.cpp \
 		jsinvoke.cpp \
 		jsiter.cpp \
 		jslock.cpp \
 		jslog2.cpp \
@@ -201,16 +202,17 @@ INSTALLED_HEADERS = \
 		jsdate.h \
 		jsdbgapi.h \
 		jsdhash.h \
 		jsdtoa.h \
 		jsemit.h \
 		jsfun.h \
 		jsfriendapi.h \
 		jsgc.h \
+		jsgcmark.h \
 		jscell.h \
 		jsgcchunk.h \
 		jsgcstats.h \
 		jscompartment.h \
 		jshash.h \
 		jsinterp.h \
 		jsinttypes.h \
 		jsiter.h \
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -61,16 +61,17 @@
 #include "jsclone.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsdate.h"
 #include "jsemit.h"
 #include "jsexn.h"
 #include "jsfun.h"
 #include "jsgc.h"
+#include "jsgcmark.h"
 #include "jsinterp.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsmath.h"
 #include "jsnum.h"
 #include "json.h"
 #include "jsobj.h"
 #include "jsopcode.h"
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -87,16 +87,17 @@
 #include "jsatom.h"
 #include "jsbit.h"
 #include "jsbool.h"
 #include "jsbuiltins.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsfun.h"
 #include "jsgc.h"
+#include "jsgcmark.h"
 #include "jsinterp.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsscope.h"
 #include "jsstr.h"
 #include "jsstaticcheck.h"
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -47,16 +47,17 @@
 #include "jsutil.h"
 #include "jshash.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jsbit.h"
 #include "jscntxt.h"
 #include "jsgc.h"
+#include "jsgcmark.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsparse.h"
 #include "jsstr.h"
 #include "jsversion.h"
 #include "jsxml.h"
 
 #include "jsstrinlines.h"
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -60,16 +60,17 @@
 #include "jsprf.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsdbgapi.h"
 #include "jsexn.h"
 #include "jsfun.h"
 #include "jsgc.h"
+#include "jsgcmark.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsmath.h"
 #include "jsnativestack.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jspubtd.h"
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1063,16 +1063,21 @@ struct JSRuntime {
     uint32              gcNumber;
     js::GCMarker        *gcMarkingTracer;
     uint32              gcTriggerFactor;
     int64               gcJitReleaseTime;
     JSGCMode            gcMode;
     volatile bool       gcIsNeeded;
     JSObject           *gcWeakMapList;
 
+    /* Pre-allocated space for the GC mark stacks. Pointer type ensures alignment. */
+    void                *gcMarkStackObjs[js::OBJECT_MARK_STACK_SIZE];
+    void                *gcMarkStackXMLs[js::XML_MARK_STACK_SIZE];
+    void                *gcMarkStackLarges[js::LARGE_MARK_STACK_SIZE];
+
     /*
      * Compartment that triggered GC. If more than one Compatment need GC,
      * gcTriggerCompartment is reset to NULL and a global GC is performed.
      */
     JSCompartment       *gcTriggerCompartment;
 
     /* Compartment that is currently involved in per-compartment GC */
     JSCompartment       *gcCurrentCompartment;
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -36,16 +36,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsgc.h"
+#include "jsgcmark.h"
 #include "jsiter.h"
 #include "jsproxy.h"
 #include "jsscope.h"
 #include "jstracer.h"
 #include "jswrapper.h"
 #include "assembler/wtf/Platform.h"
 #include "methodjit/MethodJIT.h"
 #include "methodjit/PolyIC.h"
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -50,16 +50,17 @@
 #include "jshashtable.h"
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsdbgapi.h"
 #include "jsemit.h"
 #include "jsfun.h"
 #include "jsgc.h"
+#include "jsgcmark.h"
 #include "jsinterp.h"
 #include "jslock.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsparse.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstaticcheck.h"
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -48,16 +48,18 @@
 #include "jsbit.h"
 #include "jsutil.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsexn.h"
 #include "jsfun.h"
+#include "jsgc.h"
+#include "jsgcmark.h"
 #include "jsinterp.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstaticcheck.h"
 #include "jswrapper.h"
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -51,16 +51,17 @@
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jsbuiltins.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsemit.h"
 #include "jsfun.h"
 #include "jsgc.h"
+#include "jsgcmark.h"
 #include "jsinterp.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsparse.h"
 #include "jspropertytree.h"
 #include "jsproxy.h"
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -61,16 +61,17 @@
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsdbgapi.h"
 #include "jsexn.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsgcchunk.h"
+#include "jsgcmark.h"
 #include "jsinterp.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsparse.h"
 #include "jsproxy.h"
 #include "jsscope.h"
@@ -218,18 +219,17 @@ Arena<T>::mark(T *thing, JSTracer *trc)
 
     if (alignedThing > &t.things[ThingsPerArena-1] || alignedThing < &t.things[0])
         return CGCT_NOTARENA;
 
     if (inFreeList(alignedThing))
         return CGCT_NOTLIVE;
 
     JS_ASSERT(sizeof(T) == aheader.thingSize);
-    JS_SET_TRACING_NAME(trc, "machine stack");
-    js::gc::Mark(trc, alignedThing);
+    js::gc::MarkRoot(trc, alignedThing, "machine stack");
 
 #ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
     if (alignedThing != thing)
         return CGCT_VALIDWITHOFFSET;
 #endif
     return CGCT_VALID;
 }
 
@@ -1429,43 +1429,16 @@ js_UnlockGCThingRT(JSRuntime *rt, void *
         rt->gcPoke = true;
         if (--p->value == 0)
             rt->gcLocksHash.remove(p);
 
         METER(rt->gcStats.unlock++);
     }
 }
 
-JS_PUBLIC_API(void)
-JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind)
-{
-    switch (kind) {
-      case JSTRACE_OBJECT: {
-        MarkChildren(trc, (JSObject *)thing);
-        break;
-      }
-
-      case JSTRACE_STRING: {
-        MarkChildren(trc, (JSString *)thing);
-        break;
-      }
-
-      case JSTRACE_SHAPE: {
-        MarkChildren(trc, (Shape *)thing);
-        break;
-      }
-
-#if JS_HAS_XML_SUPPORT
-      case JSTRACE_XML:
-        MarkChildren(trc, (JSXML *)thing);
-        break;
-#endif
-    }
-}
-
 namespace js {
 
 /*
  * When the native stack is low, the GC does not call JS_TraceChildren to mark
  * the reachable "children" of the thing. Rather the thing is put aside and
  * JS_TraceChildren is called later with more space on the C stack.
  *
  * To implement such delayed marking of the children with minimal overhead for
@@ -1473,17 +1446,22 @@ namespace js {
  * arena. The field marlingdelay->link links all arenas with delayed things
  * into a stack list with the pointer to stack top in
  * GCMarker::unmarkedArenaStackTop. delayMarkingChildren adds
  * arenas to the stack as necessary while markDelayedChildren pops the arenas
  * from the stack until it empties.
  */
 
 GCMarker::GCMarker(JSContext *cx)
-  : color(0), stackLimit(0), unmarkedArenaStackTop(NULL)
+  : color(0),
+    stackLimit(0),
+    unmarkedArenaStackTop(NULL),
+    objStack(cx->runtime->gcMarkStackObjs, sizeof(cx->runtime->gcMarkStackObjs)),
+    xmlStack(cx->runtime->gcMarkStackXMLs, sizeof(cx->runtime->gcMarkStackXMLs)),
+    largeStack(cx->runtime->gcMarkStackLarges, sizeof(cx->runtime->gcMarkStackLarges))
 {
     JS_TRACER_INIT(this, cx, NULL);
 #ifdef DEBUG
     markLaterCount = 0;
 #endif
 #ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
     conservativeDumpFileName = getenv("JS_DUMP_CONSERVATIVE_GC_ROOTS");
     memset(&conservativeStats, 0, sizeof(conservativeStats));
@@ -2346,17 +2324,23 @@ SweepCrossCompartmentWrappers(JSContext 
         releaseInterval = 8;
         while (now >= rt->gcJitReleaseTime) {
             if (--releaseInterval == 1)
                 rt->gcJitReleaseTime = now;
             rt->gcJitReleaseTime += JIT_SCRIPT_EIGHTH_LIFETIME;
         }
     }
 
-    /* Remove dead wrappers from the compartment map. */
+    /*
+     * Sweep the compartment:
+     * (1) Remove dead wrappers from the compartment map.
+     * (2) Finalize any unused empty shapes.
+     * (3) Sweep the trace JIT of unused code.
+     * (4) Sweep the method JIT ICs and release infrequently used JIT code.
+     */
     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
         (*c)->sweep(cx, releaseInterval);
 }
 
 static void
 SweepCompartments(JSContext *cx, JSGCInvocationKind gckind)
 {
     JSRuntime *rt = cx->runtime;
@@ -2489,31 +2473,25 @@ MarkAndSweep(JSContext *cx, JSCompartmen
         for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
             (*c)->markCrossCompartmentWrappers(&gcmarker);
     } else {
         js_MarkScriptFilenames(rt);
     }
 
     MarkRuntime(&gcmarker);
 
-    /*
-     * Mark children of things that caused too deep recursion during the above
-     * tracing.
-     */
-    gcmarker.markDelayedChildren();
+    gcmarker.drainMarkStack();
 
     /*
      * Mark weak roots.
      */
     while (true) {
-        if (!js_TraceWatchPoints(&gcmarker) &&
-            !WeakMap::markIteratively(&gcmarker)) {
+        if (!js_TraceWatchPoints(&gcmarker) && !WeakMap::markIteratively(&gcmarker))
             break;
-        }
-        gcmarker.markDelayedChildren();
+        gcmarker.drainMarkStack();
     }
 
     rt->gcMarkingTracer = NULL;
 
     if (rt->gcCallback)
         (void) rt->gcCallback(cx, JSGC_MARK_END);
 
 #ifdef DEBUG
@@ -2930,28 +2908,16 @@ js_GC(JSContext *cx, JSCompartment *comp
     js_DumpGCStats(cx->runtime, stderr);
 #endif
     GCTIMER_END(gckind == GC_LAST_CONTEXT);
 }
 
 namespace js {
 namespace gc {
 
-void
-MarkObjectSlots(JSTracer *trc, JSObject *obj)
-{
-    JS_ASSERT(obj->slotSpan() <= obj->numSlots());
-    uint32 nslots = obj->slotSpan();
-    for (uint32 i = 0; i != nslots; ++i) {
-        const Value &v = obj->getSlot(i);
-        JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
-        MarkValueRaw(trc, v);
-    }
-}
-
 bool
 SetProtoCheckingForCycles(JSContext *cx, JSObject *obj, JSObject *proto)
 {
     /*
      * This function cannot be called during the GC and always requires a
      * request.
      */
 #ifdef JS_THREADSAFE
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1063,20 +1063,65 @@ struct ConservativeGCThreadData {
     }
 #endif
 
     bool hasStackToScan() const {
         return !!nativeStackTop;
     }
 };
 
+template<class T>
+struct MarkStack {
+    T *stack;
+    uintN tos, limit;
+
+    bool push(T item) {
+        if (tos == limit)
+            return false;
+        stack[tos++] = item;
+        return true;
+    }
+
+    bool isEmpty() { return tos == 0; }
+
+    T pop() {
+        JS_ASSERT(!isEmpty());
+        return stack[--tos];
+    }
+
+    T &peek() {
+        JS_ASSERT(!isEmpty());
+        return stack[tos-1];
+    }
+
+    MarkStack(void **buffer, size_t size)
+    {
+        tos = 0;
+        limit = size / sizeof(T) - 1;
+        stack = (T *)buffer;
+    }
+};
+
+struct LargeMarkItem
+{
+    JSObject *obj;
+    uintN markpos;
+
+    LargeMarkItem(JSObject *obj) : obj(obj), markpos(0) {}
+};
+
+static const size_t OBJECT_MARK_STACK_SIZE = 32768 * sizeof(JSObject *);
+static const size_t XML_MARK_STACK_SIZE = 1024 * sizeof(JSXML *);
+static const size_t LARGE_MARK_STACK_SIZE = 64 * sizeof(LargeMarkItem);
+
 struct GCMarker : public JSTracer {
   private:
     /* The color is only applied to objects, functions and xml. */
     uint32 color;
+
   public:
     jsuword stackLimit;
     /* See comments before delayMarkingChildren is jsgc.cpp. */
     js::gc::Arena<js::gc::Cell> *unmarkedArenaStackTop;
 #ifdef DEBUG
     size_t              markLaterCount;
 #endif
 
@@ -1087,36 +1132,53 @@ struct GCMarker : public JSTracer {
 #ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
     struct ConservativeRoot { void *thing; uint32 thingKind; };
     Vector<ConservativeRoot, 0, SystemAllocPolicy> conservativeRoots;
     const char *conservativeDumpFileName;
 
     void dumpConservativeRoots();
 #endif
 
+    MarkStack<JSObject *> objStack;
+    MarkStack<JSXML *> xmlStack;
+    MarkStack<LargeMarkItem> largeStack;
+
   public:
     explicit GCMarker(JSContext *cx);
     ~GCMarker();
 
     uint32 getMarkColor() const {
         return color;
     }
 
     void setMarkColor(uint32 newColor) {
-        /*
-         * We must process any delayed marking here, otherwise we confuse
-         * colors.
-         */
-        markDelayedChildren();
+        /* We must process the mark stack here, otherwise we confuse colors. */
+        drainMarkStack();
         color = newColor;
     }
 
     void delayMarkingChildren(const void *thing);
 
-    JS_FRIEND_API(void) markDelayedChildren();
+    void markDelayedChildren();
+
+    bool isMarkStackEmpty() {
+        return objStack.isEmpty() && xmlStack.isEmpty() && largeStack.isEmpty();
+    }
+
+    JS_FRIEND_API(void) drainMarkStack();
+
+    void pushObject(JSObject *obj) {
+        if (!objStack.push(obj))
+            delayMarkingChildren(obj);
+    }
+
+    void pushXML(JSXML *xml) {
+        if (!xmlStack.push(xml))
+            delayMarkingChildren(xml);
+    }
 };
 
 void
 MarkStackRangeConservatively(JSTracer *trc, Value *begin, Value *end);
 
 } /* namespace js */
 
 extern void
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -167,17 +167,17 @@ GetGCKindSlots(FinalizeKind thingKind)
 /*
  * Allocates a new GC thing. After a successful allocation the caller must
  * fully initialize the thing before calling any function that can potentially
  * trigger GC. This will ensure that GC tracing never sees junk values stored
  * in the partially initialized thing.
  */
 
 template <typename T>
-JS_ALWAYS_INLINE T *
+inline T *
 NewFinalizableGCThing(JSContext *cx, unsigned thingKind)
 {
     JS_ASSERT(thingKind < js::gc::FINALIZE_LIMIT);
 #ifdef JS_THREADSAFE
     JS_ASSERT_IF((cx->compartment == cx->runtime->atomsCompartment),
                  (thingKind == js::gc::FINALIZE_STRING) ||
                  (thingKind == js::gc::FINALIZE_SHORT_STRING));
 #endif
@@ -249,465 +249,10 @@ js_NewGCShape(JSContext *cx)
 #if JS_HAS_XML_SUPPORT
 inline JSXML *
 js_NewGCXML(JSContext *cx)
 {
     return NewFinalizableGCThing<JSXML>(cx, js::gc::FINALIZE_XML);
 }
 #endif
 
-namespace js {
-namespace gc {
-
-static JS_ALWAYS_INLINE void
-TypedMarker(JSTracer *trc, JSXML *thing);
-
-static JS_ALWAYS_INLINE void
-TypedMarker(JSTracer *trc, JSObject *thing);
-
-static JS_ALWAYS_INLINE void
-TypedMarker(JSTracer *trc, JSFunction *thing);
-
-static JS_ALWAYS_INLINE void
-TypedMarker(JSTracer *trc, const Shape *thing);
-
-static JS_ALWAYS_INLINE void
-TypedMarker(JSTracer *trc, JSShortString *thing);
-
-extern void
-TypedMarker(JSTracer *trc, JSString *thing);
-
-template<typename T>
-static JS_ALWAYS_INLINE void
-Mark(JSTracer *trc, T *thing)
-{
-    JS_ASSERT(thing);
-    JS_ASSERT(JS_IS_VALID_TRACE_KIND(js::gc::GetGCThingTraceKind(thing)));
-    JS_ASSERT(trc->debugPrinter || trc->debugPrintArg);
-
-    /* Per-Compartment GC only with GCMarker and no custom JSTracer */
-    JS_ASSERT_IF(trc->context->runtime->gcCurrentCompartment, IS_GC_MARKING_TRACER(trc));
-
-    JSRuntime *rt = trc->context->runtime;
-    JS_ASSERT(thing->arena()->header()->compartment);
-    JS_ASSERT(thing->arena()->header()->compartment->rt == rt);
-    /* Don't mark things outside a compartment if we are in a per-compartment GC */
-    if (rt->gcCurrentCompartment && thing->compartment() != rt->gcCurrentCompartment)
-        goto out;
-
-    if (!IS_GC_MARKING_TRACER(trc)) {
-        uint32 kind = js::gc::GetGCThingTraceKind(thing);
-        trc->callback(trc, (void *)thing, kind);
-        goto out;
-    }
-
-    js::gc::TypedMarker(trc, thing);
-
-  out:
-#ifdef DEBUG
-    trc->debugPrinter = NULL;
-    trc->debugPrintArg = NULL;
-#endif
-    return;     /* to avoid out: right_curl when DEBUG is not defined */
-}
-
-static inline void
-MarkString(JSTracer *trc, JSString *str)
-{
-    JS_ASSERT(str);
-    if (str->isStaticAtom())
-        return;
-    JS_ASSERT(GetArena<JSString>((Cell *)str)->assureThingIsAligned((JSString *)str));
-    Mark(trc, str);
-}
-
-static inline void
-MarkString(JSTracer *trc, JSString *str, const char *name)
-{
-    JS_ASSERT(str);
-    JS_SET_TRACING_NAME(trc, name);
-    MarkString(trc, str);
-}
-
-static inline void
-MarkObject(JSTracer *trc, JSObject &obj, const char *name)
-{
-    JS_ASSERT(trc);
-    JS_ASSERT(&obj);
-    JS_SET_TRACING_NAME(trc, name);
-    JS_ASSERT(GetArena<JSObject>((Cell *)&obj)->assureThingIsAligned(&obj) ||
-              GetArena<JSObject_Slots2>((Cell *)&obj)->assureThingIsAligned(&obj) ||
-              GetArena<JSObject_Slots4>((Cell *)&obj)->assureThingIsAligned(&obj) ||
-              GetArena<JSObject_Slots8>((Cell *)&obj)->assureThingIsAligned(&obj) ||
-              GetArena<JSObject_Slots12>((Cell *)&obj)->assureThingIsAligned(&obj) ||
-              GetArena<JSObject_Slots16>((Cell *)&obj)->assureThingIsAligned(&obj) ||
-              GetArena<JSFunction>((Cell *)&obj)->assureThingIsAligned(&obj));
-    Mark(trc, &obj);
-}
-
-static inline void
-MarkShape(JSTracer *trc, const Shape *shape, const char *name)
-{
-    JS_ASSERT(trc);
-    JS_ASSERT(shape);
-    JS_SET_TRACING_NAME(trc, name);
-    JS_ASSERT(GetArena<Shape>((Cell *)shape)->assureThingIsAligned((void *)shape));
-    Mark(trc, shape);
-}
-
-} // namespace gc
-} // namespace js
-
-inline void
-JSObject::trace(JSTracer *trc)
-{
-    if (!isNative())
-        return;
-
-    JSContext *cx = trc->context;
-    js::Shape *shape = lastProp;
-
-    MarkShape(trc, shape, "shape");
-
-    if (IS_GC_MARKING_TRACER(trc) && cx->runtime->gcRegenShapes) {
-        /*
-         * MarkShape will regenerate the shape if need be. However, we need to
-         * regenerate our shape if hasOwnShape() is true.
-         */
-        uint32 newShape = shape->shape;
-        if (hasOwnShape()) {
-            newShape = js_RegenerateShapeForGC(cx->runtime);
-            JS_ASSERT(newShape != shape->shape);
-        }
-        objShape = newShape;
-    }
-}
-
-namespace js {
-namespace gc {
-
-void
-MarkObjectSlots(JSTracer *trc, JSObject *obj);
-
-static inline void
-MarkChildren(JSTracer *trc, JSObject *obj)
-{
-    /* If obj has no map, it must be a newborn. */
-    if (!obj->map)
-        return;
-
-    /* Trace universal (ops-independent) members. */
-    if (JSObject *proto = obj->getProto())
-        MarkObject(trc, *proto, "proto");
-    if (JSObject *parent = obj->getParent())
-        MarkObject(trc, *parent, "parent");
-
-    if (obj->emptyShapes) {
-        int count = FINALIZE_OBJECT_LAST - FINALIZE_OBJECT0 + 1;
-        for (int i = 0; i < count; i++) {
-            if (obj->emptyShapes[i])
-                MarkShape(trc, obj->emptyShapes[i], "emptyShape");
-        }
-    }
-
-    Class *clasp = obj->getClass();
-    if (clasp->trace)
-        clasp->trace(trc, obj);
-
-    if (obj->isNative()) {
-#ifdef JS_DUMP_SCOPE_METERS
-        js::MeterEntryCount(obj->propertyCount);
-#endif
-
-        obj->trace(trc);
-
-        if (obj->slotSpan() > 0)
-            MarkObjectSlots(trc, obj);
-    }
-}
-
-static inline void
-MarkChildren(JSTracer *trc, JSString *str)
-{
-    if (str->isDependent())
-        MarkString(trc, str->asDependent().base(), "base");
-    else if (str->isRope()) {
-        JSRope &rope = str->asRope();
-        MarkString(trc, rope.leftChild(), "left child");
-        MarkString(trc, rope.rightChild(), "right child");
-    }
-}
-
-static inline void
-MarkChildren(JSTracer *trc, const Shape *shape)
-{
-    shape->markChildren(trc);
-}
-
-#ifdef JS_HAS_XML_SUPPORT
-static inline void
-MarkChildren(JSTracer *trc, JSXML *xml)
-{
-    js_TraceXML(trc, xml);
-}
-#endif
-
-static inline bool
-RecursionTooDeep(GCMarker *gcmarker) {
-#ifdef JS_GC_ASSUME_LOW_C_STACK
-    return true;
-#else
-    int stackDummy;
-    return !JS_CHECK_STACK_SIZE(gcmarker->stackLimit, &stackDummy);
-#endif
-}
-
-static JS_ALWAYS_INLINE void
-TypedMarker(JSTracer *trc, JSXML *thing)
-{
-    if (!thing->markIfUnmarked(reinterpret_cast<GCMarker *>(trc)->getMarkColor()))
-        return;
-    GCMarker *gcmarker = static_cast<GCMarker *>(trc);
-    if (RecursionTooDeep(gcmarker))
-        gcmarker->delayMarkingChildren(thing);
-    else
-        MarkChildren(trc, thing);
-}
-
-static JS_ALWAYS_INLINE void
-TypedMarker(JSTracer *trc, JSObject *thing)
-{
-    JS_ASSERT(thing);
-    JS_ASSERT(JSTRACE_OBJECT == GetFinalizableTraceKind(thing->arena()->header()->thingKind));
-
-    GCMarker *gcmarker = static_cast<GCMarker *>(trc);
-    if (!thing->markIfUnmarked(gcmarker->getMarkColor()))
-        return;
-    
-    if (RecursionTooDeep(gcmarker))
-        gcmarker->delayMarkingChildren(thing);
-    else
-        MarkChildren(trc, thing);
-}
-
-static JS_ALWAYS_INLINE void
-TypedMarker(JSTracer *trc, JSFunction *thing)
-{
-    JS_ASSERT(thing);
-    JS_ASSERT(JSTRACE_OBJECT == GetFinalizableTraceKind(thing->arena()->header()->thingKind));
-
-    GCMarker *gcmarker = static_cast<GCMarker *>(trc);
-    if (!thing->markIfUnmarked(gcmarker->getMarkColor()))
-        return;
-
-    if (RecursionTooDeep(gcmarker))
-        gcmarker->delayMarkingChildren(thing);
-    else
-        MarkChildren(trc, static_cast<JSObject *>(thing));
-}
-
-static JS_ALWAYS_INLINE void
-TypedMarker(JSTracer *trc, JSShortString *thing)
-{
-    /*
-     * A short string cannot refer to other GC things so we don't have
-     * anything to mark if the string was unmarked and ignore the
-     * markIfUnmarked result.
-     */
-    (void) thing->markIfUnmarked();
-}
-
-static JS_ALWAYS_INLINE void
-TypedMarker(JSTracer *trc, const Shape *thing)
-{
-    JS_ASSERT(thing);
-    JS_ASSERT(JSTRACE_SHAPE == GetFinalizableTraceKind(thing->arena()->header()->thingKind));
-
-    GCMarker *gcmarker = static_cast<GCMarker *>(trc);
-    if (!thing->markIfUnmarked(gcmarker->getMarkColor()))
-        return;
-
-    /*
-     * We regenerate the shape number early. If we did it inside MarkChildren,
-     * then it might be called multiple times during delayed marking, which
-     * would be incorrect. However, this does mean that Shape::regenerate
-     * shouldn't use too much stack.
-     */
-    thing->regenerate(trc);
-
-    if (RecursionTooDeep(gcmarker))
-        gcmarker->delayMarkingChildren(thing);
-    else
-        MarkChildren(trc, thing);
-}
-
-static inline void
-MarkAtomRange(JSTracer *trc, size_t len, JSAtom **vec, const char *name)
-{
-    for (uint32 i = 0; i < len; i++) {
-        if (JSAtom *atom = vec[i]) {
-            JS_SET_TRACING_INDEX(trc, name, i);
-            if (!atom->isStaticAtom())
-                Mark(trc, atom);
-        }
-    }
-}
-
-static inline void
-MarkObjectRange(JSTracer *trc, size_t len, JSObject **vec, const char *name)
-{
-    for (uint32 i = 0; i < len; i++) {
-        if (JSObject *obj = vec[i]) {
-            JS_SET_TRACING_INDEX(trc, name, i);
-            Mark(trc, obj);
-        }
-    }
-}
-
-static inline void
-MarkId(JSTracer *trc, jsid id)
-{
-    if (JSID_IS_STRING(id)) {
-        JSString *str = JSID_TO_STRING(id);
-        if (!str->isStaticAtom())
-            Mark(trc, str);
-    } else if (JS_UNLIKELY(JSID_IS_OBJECT(id))) {
-        Mark(trc, JSID_TO_OBJECT(id));
-    }
-}
-
-static inline void
-MarkId(JSTracer *trc, jsid id, const char *name)
-{
-    JS_SET_TRACING_NAME(trc, name);
-    MarkId(trc, id);
-}
-
-static inline void
-MarkIdRange(JSTracer *trc, jsid *beg, jsid *end, const char *name)
-{
-    for (jsid *idp = beg; idp != end; ++idp) {
-        JS_SET_TRACING_INDEX(trc, name, (idp - beg));
-        MarkId(trc, *idp);
-    }
-}
-
-static inline void
-MarkIdRange(JSTracer *trc, size_t len, jsid *vec, const char *name)
-{
-    MarkIdRange(trc, vec, vec + len, name);
-}
-
-static inline void
-MarkKind(JSTracer *trc, void *thing, uint32 kind)
-{
-    JS_ASSERT(thing);
-    JS_ASSERT(kind == GetGCThingTraceKind(thing));
-    switch (kind) {
-        case JSTRACE_OBJECT:
-            Mark(trc, reinterpret_cast<JSObject *>(thing));
-            break;
-        case JSTRACE_STRING:
-            MarkString(trc, reinterpret_cast<JSString *>(thing));
-            break;
-        case JSTRACE_SHAPE:
-            Mark(trc, reinterpret_cast<Shape *>(thing));
-            break;
-#if JS_HAS_XML_SUPPORT
-        case JSTRACE_XML:
-            Mark(trc, reinterpret_cast<JSXML *>(thing));
-            break;
-#endif
-        default:
-            JS_ASSERT(false);
-    }
-}
-
-/* N.B. Assumes JS_SET_TRACING_NAME/INDEX has already been called. */
-static inline void
-MarkValueRaw(JSTracer *trc, const js::Value &v)
-{
-    if (v.isMarkable()) {
-        JS_ASSERT(v.toGCThing());
-        return MarkKind(trc, v.toGCThing(), v.gcKind());
-    }
-}
-
-static inline void
-MarkValue(JSTracer *trc, const js::Value &v, const char *name)
-{
-    JS_SET_TRACING_NAME(trc, name);
-    MarkValueRaw(trc, v);
-}
-
-static inline void
-MarkValueRange(JSTracer *trc, Value *beg, Value *end, const char *name)
-{
-    for (Value *vp = beg; vp < end; ++vp) {
-        JS_SET_TRACING_INDEX(trc, name, vp - beg);
-        MarkValueRaw(trc, *vp);
-    }
-}
-
-static inline void
-MarkValueRange(JSTracer *trc, size_t len, Value *vec, const char *name)
-{
-    MarkValueRange(trc, vec, vec + len, name);
-}
-
-static inline void
-MarkShapeRange(JSTracer *trc, const Shape **beg, const Shape **end, const char *name)
-{
-    for (const Shape **sp = beg; sp < end; ++sp) {
-        JS_SET_TRACING_INDEX(trc, name, sp - beg);
-        MarkShape(trc, *sp, name);
-    }
-}
-
-static inline void
-MarkShapeRange(JSTracer *trc, size_t len, const Shape **vec, const char *name)
-{
-    MarkShapeRange(trc, vec, vec + len, name);
-}
-
-/* N.B. Assumes JS_SET_TRACING_NAME/INDEX has already been called. */
-static inline void
-MarkGCThing(JSTracer *trc, void *thing, uint32 kind)
-{
-    if (!thing)
-        return;
-
-    MarkKind(trc, thing, kind);
-}
-
-static inline void
-MarkGCThing(JSTracer *trc, void *thing)
-{
-    if (!thing)
-        return;
-    MarkKind(trc, thing, GetGCThingTraceKind(thing));
-}
-
-static inline void
-MarkGCThing(JSTracer *trc, void *thing, const char *name)
-{
-    JS_SET_TRACING_NAME(trc, name);
-    MarkGCThing(trc, thing);
-}
-
-static inline void
-MarkGCThing(JSTracer *trc, void *thing, const char *name, size_t index)
-{
-    JS_SET_TRACING_INDEX(trc, name, index);
-    MarkGCThing(trc, thing);
-}
-
-static inline void
-Mark(JSTracer *trc, void *thing, uint32 kind, const char *name)
-{
-    JS_ASSERT(thing);
-    JS_SET_TRACING_NAME(trc, name);
-    MarkKind(trc, thing, kind);
-}
-
-}}
 
 #endif /* jsgcinlines_h___ */
new file mode 100644
--- /dev/null
+++ b/js/src/jsgcmark.cpp
@@ -0,0 +1,756 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is SpiderMonkey code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "jsgcmark.h"
+#include "jsprf.h"
+#include "jsscope.h"
+#include "jsstr.h"
+
+#include "jsobjinlines.h"
+#include "jsscopeinlines.h"
+
+using namespace js;
+using namespace js::gc;
+
+namespace js {
+namespace gc {
+
+static inline void
+PushMarkStack(GCMarker *gcmarker, JSXML *thing);
+
+static inline void
+PushMarkStack(GCMarker *gcmarker, JSObject *thing);
+
+static inline void
+PushMarkStack(GCMarker *gcmarker, JSFunction *thing);
+
+static inline void
+PushMarkStack(GCMarker *gcmarker, const Shape *thing);
+
+static inline void
+PushMarkStack(GCMarker *gcmarker, JSShortString *thing);
+
+static inline void
+PushMarkStack(GCMarker *gcmarker, JSString *thing);
+
+template<typename T>
+void
+Mark(JSTracer *trc, T *thing)
+{
+    JS_ASSERT(thing);
+    JS_ASSERT(JS_IS_VALID_TRACE_KIND(GetGCThingTraceKind(thing)));
+    JS_ASSERT(trc->debugPrinter || trc->debugPrintArg);
+
+    /* Per-Compartment GC only with GCMarker and no custom JSTracer */
+    JS_ASSERT_IF(trc->context->runtime->gcCurrentCompartment, IS_GC_MARKING_TRACER(trc));
+
+    JSRuntime *rt = trc->context->runtime;
+    JS_ASSERT(thing->arena()->header()->compartment);
+    JS_ASSERT(thing->arena()->header()->compartment->rt == rt);
+
+    /* Don't mark things outside a compartment if we are in a per-compartment GC */
+    if (!rt->gcCurrentCompartment || thing->compartment() == rt->gcCurrentCompartment) {
+        if (IS_GC_MARKING_TRACER(trc))
+            PushMarkStack(static_cast<GCMarker *>(trc), thing);
+        else
+            trc->callback(trc, (void *)thing, GetGCThingTraceKind(thing));
+    }
+
+#ifdef DEBUG
+    trc->debugPrinter = NULL;
+    trc->debugPrintArg = NULL;
+#endif
+}
+
+void
+MarkString(JSTracer *trc, JSString *str)
+{
+    JS_ASSERT(str);
+    if (str->isStaticAtom())
+        return;
+    JS_ASSERT(GetArena<JSString>((Cell *)str)->assureThingIsAligned((JSString *)str));
+    Mark(trc, str);
+}
+
+void
+MarkString(JSTracer *trc, JSString *str, const char *name)
+{
+    JS_ASSERT(str);
+    JS_SET_TRACING_NAME(trc, name);
+    MarkString(trc, str);
+}
+
+void
+MarkObject(JSTracer *trc, JSObject &obj, const char *name)
+{
+    JS_ASSERT(trc);
+    JS_ASSERT(&obj);
+    JS_SET_TRACING_NAME(trc, name);
+    JS_ASSERT(GetArena<JSObject>((Cell *)&obj)->assureThingIsAligned(&obj) ||
+              GetArena<JSObject_Slots2>((Cell *)&obj)->assureThingIsAligned(&obj) ||
+              GetArena<JSObject_Slots4>((Cell *)&obj)->assureThingIsAligned(&obj) ||
+              GetArena<JSObject_Slots8>((Cell *)&obj)->assureThingIsAligned(&obj) ||
+              GetArena<JSObject_Slots12>((Cell *)&obj)->assureThingIsAligned(&obj) ||
+              GetArena<JSObject_Slots16>((Cell *)&obj)->assureThingIsAligned(&obj) ||
+              GetArena<JSFunction>((Cell *)&obj)->assureThingIsAligned(&obj));
+    Mark(trc, &obj);
+}
+
+void
+MarkObjectWithPrinter(JSTracer *trc, JSObject &obj, JSTraceNamePrinter printer,
+		      const void *arg, size_t index)
+{
+    JS_ASSERT(trc);
+    JS_ASSERT(&obj);
+    JS_SET_TRACING_DETAILS(trc, printer, arg, index);
+    JS_ASSERT(GetArena<JSObject>((Cell *)&obj)->assureThingIsAligned(&obj) ||
+              GetArena<JSObject_Slots2>((Cell *)&obj)->assureThingIsAligned(&obj) ||
+              GetArena<JSObject_Slots4>((Cell *)&obj)->assureThingIsAligned(&obj) ||
+              GetArena<JSObject_Slots8>((Cell *)&obj)->assureThingIsAligned(&obj) ||
+              GetArena<JSObject_Slots12>((Cell *)&obj)->assureThingIsAligned(&obj) ||
+              GetArena<JSObject_Slots16>((Cell *)&obj)->assureThingIsAligned(&obj) ||
+              GetArena<JSFunction>((Cell *)&obj)->assureThingIsAligned(&obj));
+    Mark(trc, &obj);
+}
+
+void
+MarkShape(JSTracer *trc, const Shape *shape, const char *name)
+{
+    JS_ASSERT(trc);
+    JS_ASSERT(shape);
+    JS_SET_TRACING_NAME(trc, name);
+    JS_ASSERT(GetArena<Shape>((Cell *)shape)->assureThingIsAligned((void *)shape));
+    Mark(trc, shape);
+}
+
+void
+MarkXML(JSTracer *trc, JSXML *xml, const char *name)
+{
+    JS_ASSERT(trc);
+    JS_ASSERT(xml);
+    JS_SET_TRACING_NAME(trc, name);
+    JS_ASSERT(GetArena<JSXML>(xml)->assureThingIsAligned(xml));
+    Mark(trc, xml);
+}
+
+void
+PushMarkStack(GCMarker *gcmarker, JSXML *thing)
+{
+    JS_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment,
+                 thing->compartment() == gcmarker->context->runtime->gcCurrentCompartment);
+
+    if (thing->markIfUnmarked(gcmarker->getMarkColor()))
+        gcmarker->pushXML(thing);
+}
+
+void
+PushMarkStack(GCMarker *gcmarker, JSObject *thing)
+{
+    JS_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment,
+                 thing->compartment() == gcmarker->context->runtime->gcCurrentCompartment);
+
+    if (thing->markIfUnmarked(gcmarker->getMarkColor()))
+        gcmarker->pushObject(thing);
+}
+
+void
+PushMarkStack(GCMarker *gcmarker, JSFunction *thing)
+{
+    JS_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment,
+                 thing->compartment() == gcmarker->context->runtime->gcCurrentCompartment);
+
+    if (thing->markIfUnmarked(gcmarker->getMarkColor()))
+        gcmarker->pushObject(thing);
+}
+
+void
+PushMarkStack(GCMarker *gcmarker, JSShortString *thing)
+{
+    JS_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment,
+                 thing->compartment() == gcmarker->context->runtime->gcCurrentCompartment);
+
+    (void) thing->markIfUnmarked(gcmarker->getMarkColor());
+}
+
+static void
+ScanShape(GCMarker *gcmarker, const Shape *shape);
+
+void
+PushMarkStack(GCMarker *gcmarker, const Shape *thing)
+{
+    JS_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment,
+                 thing->compartment() == gcmarker->context->runtime->gcCurrentCompartment);
+
+    /* We mark shapes directly rather than pushing on the stack. */
+    if (thing->markIfUnmarked(gcmarker->getMarkColor()))
+        ScanShape(gcmarker, thing);
+}
+
+void
+MarkAtomRange(JSTracer *trc, size_t len, JSAtom **vec, const char *name)
+{
+    for (uint32 i = 0; i < len; i++) {
+        if (JSAtom *atom = vec[i]) {
+            JS_SET_TRACING_INDEX(trc, name, i);
+            if (!atom->isStaticAtom())
+                Mark(trc, atom);
+        }
+    }
+}
+
+void
+MarkObjectRange(JSTracer *trc, size_t len, JSObject **vec, const char *name)
+{
+    for (uint32 i = 0; i < len; i++) {
+        if (JSObject *obj = vec[i]) {
+            JS_SET_TRACING_INDEX(trc, name, i);
+            Mark(trc, obj);
+        }
+    }
+}
+
+void
+MarkXMLRange(JSTracer *trc, size_t len, JSXML **vec, const char *name)
+{
+    for (size_t i = 0; i < len; i++) {
+        if (JSXML *xml = vec[i]) {
+            JS_SET_TRACING_INDEX(trc, "xml_vector", i);
+            Mark(trc, xml);
+        }
+    }
+}
+
+void
+MarkId(JSTracer *trc, jsid id)
+{
+    if (JSID_IS_STRING(id)) {
+        JSString *str = JSID_TO_STRING(id);
+        if (!str->isStaticAtom())
+            Mark(trc, str);
+    } else if (JS_UNLIKELY(JSID_IS_OBJECT(id))) {
+        Mark(trc, JSID_TO_OBJECT(id));
+    }
+}
+
+void
+MarkId(JSTracer *trc, jsid id, const char *name)
+{
+    JS_SET_TRACING_NAME(trc, name);
+    MarkId(trc, id);
+}
+
+void
+MarkIdRange(JSTracer *trc, jsid *beg, jsid *end, const char *name)
+{
+    for (jsid *idp = beg; idp != end; ++idp) {
+        JS_SET_TRACING_INDEX(trc, name, (idp - beg));
+        MarkId(trc, *idp);
+    }
+}
+
+void
+MarkIdRange(JSTracer *trc, size_t len, jsid *vec, const char *name)
+{
+    MarkIdRange(trc, vec, vec + len, name);
+}
+
+void
+MarkKind(JSTracer *trc, void *thing, uint32 kind)
+{
+    JS_ASSERT(thing);
+    JS_ASSERT(kind == GetGCThingTraceKind(thing));
+    switch (kind) {
+        case JSTRACE_OBJECT:
+            Mark(trc, reinterpret_cast<JSObject *>(thing));
+            break;
+        case JSTRACE_STRING:
+            MarkString(trc, reinterpret_cast<JSString *>(thing));
+            break;
+        case JSTRACE_SHAPE:
+            Mark(trc, reinterpret_cast<Shape *>(thing));
+            break;
+#if JS_HAS_XML_SUPPORT
+        case JSTRACE_XML:
+            Mark(trc, reinterpret_cast<JSXML *>(thing));
+            break;
+#endif
+        default:
+            JS_ASSERT(false);
+    }
+}
+
+/* N.B. Assumes JS_SET_TRACING_NAME/INDEX has already been called. */
+void
+MarkValueRaw(JSTracer *trc, const js::Value &v)
+{
+    if (v.isMarkable()) {
+        JS_ASSERT(v.toGCThing());
+        return MarkKind(trc, v.toGCThing(), v.gcKind());
+    }
+}
+
+void
+MarkValue(JSTracer *trc, const js::Value &v, const char *name)
+{
+    JS_SET_TRACING_NAME(trc, name);
+    MarkValueRaw(trc, v);
+}
+
+void
+MarkValueRange(JSTracer *trc, Value *beg, Value *end, const char *name)
+{
+    for (Value *vp = beg; vp < end; ++vp) {
+        JS_SET_TRACING_INDEX(trc, name, vp - beg);
+        MarkValueRaw(trc, *vp);
+    }
+}
+
+void
+MarkValueRange(JSTracer *trc, size_t len, Value *vec, const char *name)
+{
+    MarkValueRange(trc, vec, vec + len, name);
+}
+
+void
+MarkShapeRange(JSTracer *trc, const Shape **beg, const Shape **end, const char *name)
+{
+    for (const Shape **sp = beg; sp < end; ++sp) {
+        JS_SET_TRACING_INDEX(trc, name, sp - beg);
+        MarkShape(trc, *sp, name);
+    }
+}
+
+void
+MarkShapeRange(JSTracer *trc, size_t len, const Shape **vec, const char *name)
+{
+    MarkShapeRange(trc, vec, vec + len, name);
+}
+
+/* N.B. Assumes JS_SET_TRACING_NAME/INDEX has already been called. */
+void
+MarkGCThing(JSTracer *trc, void *thing, uint32 kind)
+{
+    if (!thing)
+        return;
+
+    MarkKind(trc, thing, kind);
+}
+
+void
+MarkGCThing(JSTracer *trc, void *thing)
+{
+    if (!thing)
+        return;
+    MarkKind(trc, thing, GetGCThingTraceKind(thing));
+}
+
+void
+MarkGCThing(JSTracer *trc, void *thing, const char *name)
+{
+    JS_SET_TRACING_NAME(trc, name);
+    MarkGCThing(trc, thing);
+}
+
+void
+MarkGCThing(JSTracer *trc, void *thing, const char *name, size_t index)
+{
+    JS_SET_TRACING_INDEX(trc, name, index);
+    MarkGCThing(trc, thing);
+}
+
+void
+Mark(JSTracer *trc, void *thing, uint32 kind, const char *name)
+{
+    JS_ASSERT(thing);
+    JS_SET_TRACING_NAME(trc, name);
+    MarkKind(trc, thing, kind);
+}
+
+void
+MarkRoot(JSTracer *trc, JSObject *thing, const char *name)
+{
+    MarkObject(trc, *thing, name);
+}
+
+void
+MarkRoot(JSTracer *trc, JSString *thing, const char *name)
+{
+    MarkString(trc, thing, name);
+}
+
+void
+MarkRoot(JSTracer *trc, const Shape *thing, const char *name)
+{
+    MarkShape(trc, thing, name);
+}
+
+void
+MarkRoot(JSTracer *trc, JSXML *thing, const char *name)
+{
+    MarkXML(trc, thing, name);
+}
+
+static void
+PrintPropertyGetterOrSetter(JSTracer *trc, char *buf, size_t bufsize)
+{
+    Shape *shape;
+    jsid id;
+    size_t n;
+    const char *name;
+
+    JS_ASSERT(trc->debugPrinter == PrintPropertyGetterOrSetter);
+    shape = (Shape *)trc->debugPrintArg;
+    id = shape->id;
+    JS_ASSERT(!JSID_IS_VOID(id));
+    name = trc->debugPrintIndex ? js_setter_str : js_getter_str;
+
+    if (JSID_IS_ATOM(id)) {
+        n = PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0);
+        if (n < bufsize)
+            JS_snprintf(buf + n, bufsize - n, " %s", name);
+    } else if (JSID_IS_INT(shape->id)) {
+        JS_snprintf(buf, bufsize, "%d %s", JSID_TO_INT(id), name);
+    } else {
+        JS_snprintf(buf, bufsize, "<object> %s", name);
+    }
+}
+
+static void
+PrintPropertyMethod(JSTracer *trc, char *buf, size_t bufsize)
+{
+    Shape *shape;
+    jsid id;
+    size_t n;
+
+    JS_ASSERT(trc->debugPrinter == PrintPropertyMethod);
+    shape = (Shape *)trc->debugPrintArg;
+    id = shape->id;
+    JS_ASSERT(!JSID_IS_VOID(id));
+
+    JS_ASSERT(JSID_IS_ATOM(id));
+    n = PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0);
+    if (n < bufsize)
+        JS_snprintf(buf + n, bufsize - n, " method");
+}
+
+static inline void
+ScanValue(GCMarker *gcmarker, const Value &v)
+{
+    if (v.isMarkable()) {
+        switch (v.gcKind()) {
+          case JSTRACE_STRING: {
+            JSString *str = (JSString *)v.toGCThing();
+            if (!str->isStaticAtom())
+                PushMarkStack(gcmarker, str);
+            break;
+          }
+          case JSTRACE_OBJECT:
+            PushMarkStack(gcmarker, (JSObject *)v.toGCThing());
+            break;
+          case JSTRACE_XML:
+            PushMarkStack(gcmarker, (JSXML *)v.toGCThing());
+            break;
+        }
+    }
+}
+
+static void
+ScanShape(GCMarker *gcmarker, const Shape *shape)
+{
+restart:
+    JSRuntime *rt = gcmarker->context->runtime;
+    if (rt->gcRegenShapes)
+        shape->shape = js_RegenerateShapeForGC(rt);
+
+    if (JSID_IS_STRING(shape->id)) {
+        JSString *str = JSID_TO_STRING(shape->id);
+        if (!str->isStaticAtom())
+            PushMarkStack(gcmarker, str);
+    } else if (JS_UNLIKELY(JSID_IS_OBJECT(shape->id))) {
+        PushMarkStack(gcmarker, JSID_TO_OBJECT(shape->id));
+    }
+
+    if (shape->hasGetterValue() && shape->getter())
+        PushMarkStack(gcmarker, shape->getterObject());
+    if (shape->hasSetterValue() && shape->setter())
+        PushMarkStack(gcmarker, shape->setterObject());
+
+    if (shape->isMethod())
+        PushMarkStack(gcmarker, &shape->methodObject());
+
+    shape = shape->previous();
+    if (shape && shape->markIfUnmarked(gcmarker->getMarkColor()))
+        goto restart;
+}
+
+static inline void
+PushMarkStack(GCMarker *gcmarker, JSString *str)
+{
+    JS_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment,
+                 str->compartment() == gcmarker->context->runtime->gcCurrentCompartment
+                 || str->compartment() == gcmarker->context->runtime->atomsCompartment);
+
+    str->mark(gcmarker);
+}
+
+static const uintN LARGE_OBJECT_CHUNK_SIZE = 2048;
+
+static void
+ScanObject(GCMarker *gcmarker, JSObject *obj)
+{
+    if (!obj->map)
+        return;
+
+    if (JSObject *parent = obj->getParent())
+        PushMarkStack(gcmarker, parent);
+    if (JSObject *proto = obj->getProto())
+        PushMarkStack(gcmarker, proto);
+
+    Class *clasp = obj->getClass();
+    if (clasp->trace) {
+        if (obj->isDenseArray() && obj->getDenseArrayCapacity() > LARGE_OBJECT_CHUNK_SIZE)
+            gcmarker->largeStack.push(LargeMarkItem(obj));
+        else
+            clasp->trace(gcmarker, obj);
+    }
+
+    if (obj->emptyShapes) {
+        int count = FINALIZE_OBJECT_LAST - FINALIZE_OBJECT0 + 1;
+        for (int i = 0; i < count; i++) {
+            if (obj->emptyShapes[i])
+                PushMarkStack(gcmarker, obj->emptyShapes[i]);
+        }
+    }
+
+    if (obj->isNative()) {
+#ifdef JS_DUMP_SCOPE_METERS
+        js::MeterEntryCount(obj->propertyCount);
+#endif
+
+        js::Shape *shape = obj->lastProp;
+        PushMarkStack(gcmarker, shape);
+
+        if (gcmarker->context->runtime->gcRegenShapes) {
+            /* We need to regenerate our shape if hasOwnShape(). */
+            uint32 newShape = shape->shape;
+            if (obj->hasOwnShape()) {
+                newShape = js_RegenerateShapeForGC(gcmarker->context->runtime);
+                JS_ASSERT(newShape != shape->shape);
+            }
+            obj->objShape = newShape;
+        }
+
+        uint32 nslots = obj->slotSpan();
+        JS_ASSERT(obj->slotSpan() <= obj->numSlots());
+        if (nslots > LARGE_OBJECT_CHUNK_SIZE) {
+            gcmarker->largeStack.push(LargeMarkItem(obj));
+        } else {
+            for (uint32 i = nslots; i > 0; i--) {
+                ScanValue(gcmarker, obj->getSlot(i-1));
+            }
+        }
+    }
+}
+
+static bool
+ScanLargeObject(GCMarker *gcmarker, LargeMarkItem &item)
+{
+    JSObject *obj = item.obj;
+
+    uint32 capacity;
+    if (obj->isDenseArray()) {
+        capacity = obj->getDenseArrayCapacity();
+    } else {
+        JS_ASSERT(obj->isNative());
+        capacity = obj->slotSpan();
+    }
+
+    uintN start = item.markpos;
+    uintN stop = JS_MIN(start + LARGE_OBJECT_CHUNK_SIZE, capacity);
+    for (uintN i=stop; i>start; i--)
+        ScanValue(gcmarker, obj->getSlot(i-1));
+
+    if (stop == capacity)
+        return true;
+
+    item.markpos += LARGE_OBJECT_CHUNK_SIZE;
+    return false;
+}
+
+static void
+MarkObjectSlots(JSTracer *trc, JSObject *obj)
+{
+    JS_ASSERT(obj->slotSpan() <= obj->numSlots());
+    uint32 nslots = obj->slotSpan();
+    for (uint32 i = 0; i != nslots; ++i) {
+        const Value &v = obj->getSlot(i);
+        JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
+        MarkValueRaw(trc, v);
+    }
+}
+
+void
+MarkChildren(JSTracer *trc, JSObject *obj)
+{
+    /* If obj has no map, it must be a newborn. */
+    if (!obj->map)
+        return;
+
+    /* Trace universal (ops-independent) members. */
+    if (JSObject *proto = obj->getProto())
+        MarkObject(trc, *proto, "proto");
+    if (JSObject *parent = obj->getParent())
+        MarkObject(trc, *parent, "parent");
+
+    if (obj->emptyShapes) {
+        int count = FINALIZE_OBJECT_LAST - FINALIZE_OBJECT0 + 1;
+        for (int i = 0; i < count; i++) {
+            if (obj->emptyShapes[i])
+                MarkShape(trc, obj->emptyShapes[i], "emptyShape");
+        }
+    }
+
+    Class *clasp = obj->getClass();
+    if (clasp->trace)
+        clasp->trace(trc, obj);
+
+    if (obj->isNative()) {
+#ifdef JS_DUMP_SCOPE_METERS
+        js::MeterEntryCount(obj->propertyCount);
+#endif
+
+        MarkShape(trc, obj->lastProp, "shape");
+
+        if (obj->slotSpan() > 0)
+            MarkObjectSlots(trc, obj);
+    }
+}
+
+void
+MarkChildren(JSTracer *trc, JSString *str)
+{
+    if (str->isDependent()) {
+        MarkString(trc, str->asDependent().base(), "base");
+    } else if (str->isRope()) {
+        JSRope &rope = str->asRope();
+        MarkString(trc, rope.leftChild(), "left child");
+        MarkString(trc, rope.rightChild(), "right child");
+    }
+}
+
+void
+MarkChildren(JSTracer *trc, const Shape *shape)
+{
+restart:
+    MarkId(trc, shape->id, "id");
+
+    if (shape->hasGetterValue() && shape->getter())
+        MarkObjectWithPrinter(trc, *shape->getterObject(), PrintPropertyGetterOrSetter, shape, 0);
+    if (shape->hasSetterValue() && shape->setter())
+        MarkObjectWithPrinter(trc, *shape->setterObject(), PrintPropertyGetterOrSetter, shape, 1);
+
+    if (shape->isMethod())
+        MarkObjectWithPrinter(trc, shape->methodObject(), PrintPropertyMethod, shape, 0);
+
+    shape = shape->previous();
+    if (shape)
+        goto restart;
+}
+
+#ifdef JS_HAS_XML_SUPPORT
+void
+MarkChildren(JSTracer *trc, JSXML *xml)
+{
+    js_TraceXML(trc, xml);
+}
+#endif
+
+} /* namespace gc */
+
+void
+GCMarker::drainMarkStack()
+{
+    while (!isMarkStackEmpty()) {
+        while (!objStack.isEmpty())
+            ScanObject(this, objStack.pop());
+
+        while (!xmlStack.isEmpty())
+            MarkChildren(this, xmlStack.pop());
+
+        if (!largeStack.isEmpty()) {
+            LargeMarkItem &item = largeStack.peek();
+            if (ScanLargeObject(this, item))
+                largeStack.pop();
+        }
+
+        if (isMarkStackEmpty()) {
+            /*
+             * Mark children of things that caused too deep recursion during the above
+             * tracing. Don't do this until we're done with everything else.
+             */
+            markDelayedChildren();
+        }
+    }
+}
+
+} /* namespace js */
+
+JS_PUBLIC_API(void)
+JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind)
+{
+    switch (kind) {
+      case JSTRACE_OBJECT:
+	MarkChildren(trc, (JSObject *)thing);
+        break;
+
+      case JSTRACE_STRING:
+	MarkChildren(trc, (JSString *)thing);
+        break;
+
+      case JSTRACE_SHAPE:
+	MarkChildren(trc, (js::Shape *)thing);
+        break;
+
+#if JS_HAS_XML_SUPPORT
+      case JSTRACE_XML:
+        MarkChildren(trc, (JSXML *)thing);
+        break;
+#endif
+    }
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jsgcmark.h
@@ -0,0 +1,160 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is SpiderMonkey code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef jsgcmark_h___
+#define jsgcmark_h___
+
+#include "jsgc.h"
+#include "jscntxt.h"
+#include "jscompartment.h"
+
+#include "jslock.h"
+#include "jstl.h"
+
+namespace js {
+namespace gc {
+
+template<typename T>
+void Mark(JSTracer *trc, T *thing);
+
+void
+MarkString(JSTracer *trc, JSString *str);
+
+void
+MarkString(JSTracer *trc, JSString *str, const char *name);
+
+void
+MarkObject(JSTracer *trc, JSObject &obj, const char *name);
+
+void
+MarkObjectWithPrinter(JSTracer *trc, JSObject &obj, JSTraceNamePrinter printer,
+		      const void *arg, size_t index);
+
+void
+MarkShape(JSTracer *trc, const Shape *shape, const char *name);
+
+void
+MarkXML(JSTracer *trc, JSXML *xml, const char *name);
+
+void
+MarkAtomRange(JSTracer *trc, size_t len, JSAtom **vec, const char *name);
+
+void
+MarkObjectRange(JSTracer *trc, size_t len, JSObject **vec, const char *name);
+
+void
+MarkXMLRange(JSTracer *trc, size_t len, JSXML **vec, const char *name);
+
+void
+MarkId(JSTracer *trc, jsid id);
+
+void
+MarkId(JSTracer *trc, jsid id, const char *name);
+
+void
+MarkIdRange(JSTracer *trc, jsid *beg, jsid *end, const char *name);
+
+void
+MarkIdRange(JSTracer *trc, size_t len, jsid *vec, const char *name);
+
+void
+MarkKind(JSTracer *trc, void *thing, uint32 kind);
+
+void
+MarkValueRaw(JSTracer *trc, const js::Value &v);
+
+void
+MarkValue(JSTracer *trc, const js::Value &v, const char *name);
+
+void
+MarkValueRange(JSTracer *trc, Value *beg, Value *end, const char *name);
+
+void
+MarkValueRange(JSTracer *trc, size_t len, Value *vec, const char *name);
+
+void
+MarkShapeRange(JSTracer *trc, const Shape **beg, const Shape **end, const char *name);
+
+void
+MarkShapeRange(JSTracer *trc, size_t len, const Shape **vec, const char *name);
+
+/* N.B. Assumes JS_SET_TRACING_NAME/INDEX has already been called. */
+void
+MarkGCThing(JSTracer *trc, void *thing, uint32 kind);
+
+void
+MarkGCThing(JSTracer *trc, void *thing);
+
+void
+MarkGCThing(JSTracer *trc, void *thing, const char *name);
+
+void
+MarkGCThing(JSTracer *trc, void *thing, const char *name, size_t index);
+
+void
+Mark(JSTracer *trc, void *thing, uint32 kind, const char *name);
+
+void
+MarkRoot(JSTracer *trc, JSObject *thing, const char *name);
+
+void
+MarkRoot(JSTracer *trc, JSString *thing, const char *name);
+
+void
+MarkRoot(JSTracer *trc, const Shape *thing, const char *name);
+
+void
+MarkRoot(JSTracer *trc, JSXML *thing, const char *name);
+
+void
+MarkChildren(JSTracer *trc, JSObject *obj);
+
+void
+MarkChildren(JSTracer *trc, JSString *str);
+
+void
+MarkChildren(JSTracer *trc, const Shape *shape);
+
+void
+MarkChildren(JSTracer *trc, JSXML *xml);
+
+}
+}
+
+#endif
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -51,16 +51,17 @@
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jsbuiltins.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsexn.h"
 #include "jsfun.h"
 #include "jsgc.h"
+#include "jsgcmark.h"
 #include "jshashtable.h"
 #include "jsinterp.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsproxy.h"
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -56,16 +56,17 @@
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jsbuiltins.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsemit.h"
 #include "jsfun.h"
 #include "jsgc.h"
+#include "jsgcmark.h"
 #include "jsinterp.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsonparser.h"
 #include "jsopcode.h"
 #include "jsparse.h"
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -427,18 +427,16 @@ struct JSObject : js::gc::Cell {
     bool hasClass(const js::Class *c) const {
         return c == clasp;
     }
 
     const js::ObjectOps *getOps() const {
         return &getClass()->ops;
     }
 
-    inline void trace(JSTracer *trc);
-
     uint32 shape() const {
         JS_ASSERT(objShape != JSObjectMap::INVALID_SHAPE);
         return objShape;
     }
 
     bool isDelegate() const     { return !!(flags & DELEGATE); }
     void setDelegate()          { flags |= DELEGATE; }
     void clearDelegate()        { flags &= ~DELEGATE; }
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -60,16 +60,18 @@
 #include "jsutil.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsemit.h"
 #include "jsfun.h"
+#include "jsgc.h"
+#include "jsgcmark.h"
 #include "jsinterp.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsparse.h"
 #include "jsscan.h"
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -37,16 +37,18 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include <string.h>
 #include "jsapi.h"
 #include "jscntxt.h"
+#include "jsgc.h"
+#include "jsgcmark.h"
 #include "jsprvtd.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsproxy.h"
 #include "jsscope.h"
 
 #include "jsobjinlines.h"
 
--- a/js/src/jsregexp.cpp
+++ b/js/src/jsregexp.cpp
@@ -44,16 +44,17 @@
 #include <stdlib.h>
 #include <string.h>
 #include "jstypes.h"
 #include "jsstdint.h"
 #include "jsutil.h"
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsgc.h"
+#include "jsgcmark.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsregexp.h"
 #include "jsstr.h"
 #include "jsvector.h"
 
 #include "jsobjinlines.h"
 #include "jsregexpinlines.h"
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -1369,111 +1369,8 @@ JSObject::shadowingShapeChange(JSContext
 }
 
 bool
 JSObject::globalObjectOwnShapeChange(JSContext *cx)
 {
     generateOwnShape(cx);
     return !js_IsPropertyCacheDisabled(cx);
 }
-
-#ifdef DEBUG
-static void
-PrintPropertyGetterOrSetter(JSTracer *trc, char *buf, size_t bufsize)
-{
-    Shape *shape;
-    jsid id;
-    size_t n;
-    const char *name;
-
-    JS_ASSERT(trc->debugPrinter == PrintPropertyGetterOrSetter);
-    shape = (Shape *)trc->debugPrintArg;
-    id = shape->id;
-    JS_ASSERT(!JSID_IS_VOID(id));
-    name = trc->debugPrintIndex ? js_setter_str : js_getter_str;
-
-    if (JSID_IS_ATOM(id)) {
-        n = PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0);
-        if (n < bufsize)
-            JS_snprintf(buf + n, bufsize - n, " %s", name);
-    } else if (JSID_IS_INT(shape->id)) {
-        JS_snprintf(buf, bufsize, "%d %s", JSID_TO_INT(id), name);
-    } else {
-        JS_snprintf(buf, bufsize, "<object> %s", name);
-    }
-}
-
-static void
-PrintPropertyMethod(JSTracer *trc, char *buf, size_t bufsize)
-{
-    Shape *shape;
-    jsid id;
-    size_t n;
-
-    JS_ASSERT(trc->debugPrinter == PrintPropertyMethod);
-    shape = (Shape *)trc->debugPrintArg;
-    id = shape->id;
-    JS_ASSERT(!JSID_IS_VOID(id));
-
-    JS_ASSERT(JSID_IS_ATOM(id));
-    n = PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0);
-    if (n < bufsize)
-        JS_snprintf(buf + n, bufsize - n, " method");
-}
-#endif
-
-/*
- * A few notes on shape marking:
- *
- * We want to make sure that we regenerate the shape number exactly once per
- * shape-regenerating GC. Since delayed marking calls MarkChildren many times,
- * we handle regeneration in the PreMark stage.
- *
- * We also want to make sure to mark iteratively up the parent chain, not
- * recursively. So marking is split into markChildren and markChildrenNotParent.
- */
-
-void
-Shape::regenerate(JSTracer *trc) const
-{
-    JSRuntime *rt = trc->context->runtime;
-    if (IS_GC_MARKING_TRACER(trc) && rt->gcRegenShapes)
-        shape = js_RegenerateShapeForGC(rt);
-}
-
-void
-Shape::markChildrenNotParent(JSTracer *trc) const
-{
-    MarkId(trc, id, "id");
-
-    if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
-        if ((attrs & JSPROP_GETTER) && rawGetter) {
-            JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, this, 0);
-            Mark(trc, getterObject());
-        }
-        if ((attrs & JSPROP_SETTER) && rawSetter) {
-            JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, this, 1);
-            Mark(trc, setterObject());
-        }
-    }
-
-    if (isMethod()) {
-        JS_SET_TRACING_DETAILS(trc, PrintPropertyMethod, this, 0);
-        Mark(trc, &methodObject());
-    }
-}
-
-void
-Shape::markChildren(JSTracer *trc) const
-{
-    markChildrenNotParent(trc);
-
-    for (Shape *shape = parent; shape; shape = shape->parent) {
-        if (IS_GC_MARKING_TRACER(trc)) {
-            GCMarker *gcmarker = static_cast<GCMarker *>(trc);
-            if (!shape->markIfUnmarked(gcmarker->getMarkColor()))
-                break;
-        }
-
-        shape->regenerate(trc);
-        shape->markChildrenNotParent(trc);
-    }
-}
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -543,20 +543,16 @@ struct Shape : public JSObjectMap
                                      uint32 aslot, uintN aattrs, uintN aflags,
                                      intN ashortid) const;
 
     bool get(JSContext* cx, JSObject *receiver, JSObject *obj, JSObject *pobj, js::Value* vp) const;
     bool set(JSContext* cx, JSObject *obj, bool strict, js::Value* vp) const;
 
     inline bool isSharedPermanent() const;
 
-    void regenerate(JSTracer *trc) const;
-    void markChildrenNotParent(JSTracer *trc) const;
-    void markChildren(JSTracer *trc) const;
-
     bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
 
     uint8 attributes() const { return attrs; }
     bool configurable() const { return (attrs & JSPROP_PERMANENT) == 0; }
     bool enumerable() const { return (attrs & JSPROP_ENUMERATE) != 0; }
     bool writable() const {
         // JS_ASSERT(isDataDescriptor());
         return (attrs & JSPROP_READONLY) == 0;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -48,16 +48,18 @@
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsdbgapi.h"
 #include "jsemit.h"
 #include "jsfun.h"
+#include "jsgc.h"
+#include "jsgcmark.h"
 #include "jsinterp.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsopcode.h"
 #include "jsparse.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jstracer.h"
@@ -1513,47 +1515,31 @@ js_DestroyCachedScript(JSContext *cx, JS
 void
 js_TraceScript(JSTracer *trc, JSScript *script)
 {
     JSAtomMap *map = &script->atomMap;
     MarkAtomRange(trc, map->length, map->vector, "atomMap");
 
     if (JSScript::isValidOffset(script->objectsOffset)) {
         JSObjectArray *objarray = script->objects();
-        uintN i = objarray->length;
-        do {
-            --i;
-            if (objarray->vector[i]) {
-                JS_SET_TRACING_INDEX(trc, "objects", i);
-                Mark(trc, objarray->vector[i]);
-            }
-        } while (i != 0);
+        MarkObjectRange(trc, objarray->length, objarray->vector, "objects");
     }
 
     if (JSScript::isValidOffset(script->regexpsOffset)) {
         JSObjectArray *objarray = script->regexps();
-        uintN i = objarray->length;
-        do {
-            --i;
-            if (objarray->vector[i]) {
-                JS_SET_TRACING_INDEX(trc, "regexps", i);
-                Mark(trc, objarray->vector[i]);
-            }
-        } while (i != 0);
+        MarkObjectRange(trc, objarray->length, objarray->vector, "objects");
     }
 
     if (JSScript::isValidOffset(script->constOffset)) {
         JSConstArray *constarray = script->consts();
         MarkValueRange(trc, constarray->length, constarray->vector, "consts");
     }
 
-    if (script->u.object) {
-        JS_SET_TRACING_NAME(trc, "object");
-        Mark(trc, script->u.object);
-    }
+    if (script->u.object)
+        MarkObject(trc, *script->u.object, "object");
 
     if (IS_GC_MARKING_TRACER(trc) && script->filename)
         js_MarkScriptFilename(script->filename);
 
     script->bindings.trace(trc);
 }
 
 JSObject *
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -117,25 +117,25 @@ Tagged(JSString *str)
 
 static JS_ALWAYS_INLINE JSRope *
 Untag(JSString *str)
 {
     JS_ASSERT((size_t(str) & 1) == 1);
     return (JSRope *)(size_t(str) & ~size_t(1));
 }
 
-JS_ALWAYS_INLINE void
+void
 JSLinearString::mark(JSTracer *)
 {
     JSLinearString *str = this;
     while (!str->isStaticAtom() && str->markIfUnmarked() && str->isDependent())
         str = str->asDependent().base();
 }
 
-JS_ALWAYS_INLINE void
+void
 JSString::mark(JSTracer *trc)
 {
     if (isLinear()) {
         asLinear().mark(trc);
         return;
     }
 
     /*
@@ -183,22 +183,16 @@ JSString::mark(JSTracer *trc)
         JSRope *nextParent = Untag(parent->d.s.u2.right);
         parent->d.s.u2.right = str;
         str = parent;
         parent = nextParent;
         goto finish_node;
     }
 }
 
-void
-js::gc::TypedMarker(JSTracer *trc, JSString *str)
-{
-    str->mark(trc);
-}
-
 static JS_ALWAYS_INLINE size_t
 RopeCapacityFor(size_t length)
 {
     static const size_t ROPE_DOUBLING_MAX = 1024 * 1024;
 
     /*
      * Grow by 12.5% if the buffer is very large. Otherwise, round up to the
      * next power of 2. This is similar to what we do with arrays; see
@@ -228,17 +222,17 @@ JSRope::flatten(JSContext *maybecx)
      * into a contiguous buffer. Visit each rope node three times:
      *   1. record position in the buffer and recurse into left child;
      *   2. recurse into the right child;
      *   3. transform the node into a dependent string.
      * To avoid maintaining a stack, tree nodes are mutated to indicate how many
      * times they have been visited. Since ropes can be dags, a node may be
      * encountered multiple times during traversal. However, step 3 above leaves
      * a valid dependent string, so everything works out. This algorithm is
-     * homomorphic to TypedMarker(JSTracer *, JSString *).
+     * homomorphic to marking code.
      *
      * While ropes avoid all sorts of quadratic cases with string
      * concatenation, they can't help when ropes are immediately flattened.
      * One idiomatic case that we'd like to keep linear (and has traditionally
      * been linear in SM and other JS engines) is:
      *
      *   while (...) {
      *     s += ...
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -359,17 +359,17 @@ class JSString : public js::gc::Cell
     }
 
     /* Only called by the GC for strings with the FINALIZE_STRING kind. */
 
     inline void finalize(JSContext *cx);
 
     /* Called during GC for any string. */
 
-    inline void mark(JSTracer *trc);
+    void mark(JSTracer *trc);
 
     /* Offsets for direct field from jit code. */
 
     static size_t offsetOfLengthAndFlags() {
         return offsetof(JSString, d.lengthAndFlags);
     }
 
     static size_t offsetOfChars() {
@@ -399,17 +399,17 @@ class JSRope : public JSString
     }
 };
 
 JS_STATIC_ASSERT(sizeof(JSRope) == sizeof(JSString));
 
 class JSLinearString : public JSString
 {
     friend class JSString;
-    inline void mark(JSTracer *trc);
+    void mark(JSTracer *trc);
 
   public:
     JS_ALWAYS_INLINE
     const jschar *chars() const {
         JS_ASSERT(isLinear());
         return d.u1.chars;
     }
 };
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -60,16 +60,18 @@
 #include "jsarray.h"
 #include "jsbool.h"
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsdate.h"
 #include "jsdbgapi.h"
 #include "jsemit.h"
 #include "jsfun.h"
+#include "jsgc.h"
+#include "jsgcmark.h"
 #include "jsinterp.h"
 #include "jsiter.h"
 #include "jsmath.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsregexp.h"
 #include "jsscope.h"
 #include "jsscript.h"
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -47,16 +47,17 @@
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jsbuiltins.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsgc.h"
+#include "jsgcmark.h"
 #include "jsinterp.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsstaticcheck.h"
 #include "jsbit.h"
 #include "jsvector.h"
 #include "jstypedarray.h"
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -41,16 +41,17 @@
 
 #include <string.h>
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsgc.h"
 #include "jshashtable.h"
 #include "jsobj.h"
 #include "jsgc.h"
+#include "jsgcmark.h"
 #include "jsweakmap.h"
 
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 
 bool
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -36,16 +36,18 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "jsapi.h"
 #include "jscntxt.h"
+#include "jsgc.h"
+#include "jsgcmark.h"
 #include "jsiter.h"
 #include "jsnum.h"
 #include "jsregexp.h"
 #include "jswrapper.h"
 #include "methodjit/PolyIC.h"
 #include "methodjit/MonoIC.h"
 #ifdef JS_METHODJIT
 # include "assembler/jit/ExecutableAllocator.h"
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -51,16 +51,17 @@
 #include "jsutil.h"
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jsbool.h"
 #include "jscntxt.h"
 #include "jsfun.h"
 #include "jsgc.h"
+#include "jsgcmark.h"
 #include "jsinterp.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsparse.h"
 #include "jsscan.h"
 #include "jsscope.h"
@@ -4691,26 +4692,17 @@ xml_finalize(JSContext *cx, JSObject *ob
         return;
     if (xml->object == obj)
         xml->object = NULL;
 }
 
 static void
 xml_trace_vector(JSTracer *trc, JSXML **vec, uint32 len)
 {
-    uint32 i;
-    JSXML *xml;
-
-    for (i = 0; i < len; i++) {
-        xml = vec[i];
-        if (xml) {
-            JS_SET_TRACING_INDEX(trc, "xml_vector", i);
-            Mark(trc, xml);
-        }
-    }
+    MarkXMLRange(trc, len, vec, "xml_vector");
 }
 
 /*
  * XML objects are native. Thus xml_lookupProperty must return a valid
  * Shape pointer parameter via *propp to signify "property found". Since the
  * only call to xml_lookupProperty is via JSObject::lookupProperty, and then
  * only from js_FindProperty (in jsobj.c, called from jsinterp.c) or from
  * JSOP_IN case in the interpreter, the only time we add a Shape here is when