bug 674251 - making JSScript a GC-thing. r=jorendorff,bhackett1024
authorIgor Bukanov <igor@mir2.org>
Tue, 09 Aug 2011 10:51:59 +0200
changeset 76348 de4425a74643cdfcfb6bd909a53de379b3c3003a
parent 76347 e43df9a4b36e65c09b30d853bc645207bff7ed35
child 76349 b4193e7aa44c17cb7aba097f2eac5f4f29e9a73a
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewersjorendorff, bhackett1024
bugs674251
milestone9.0a1
bug 674251 - making JSScript a GC-thing. r=jorendorff,bhackett1024
js/src/gnuplot/gcTimer.gnu
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsatom.cpp
js/src/jsatom.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsdbgapi.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/jsgcstats.cpp
js/src/jsgcstats.h
js/src/jsinfer.cpp
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsopcode.cpp
js/src/jsparse.cpp
js/src/jsprvtd.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jstracer.cpp
js/src/jsvalue.h
js/src/jsxdrapi.cpp
js/src/methodjit/BaseCompiler.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/Retcon.cpp
js/src/shell/js.cpp
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/Stack-inl.h
js/src/vm/Stack.h
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/xpcjsruntime.cpp
js/src/xpconnect/src/xpcpublic.h
--- a/js/src/gnuplot/gcTimer.gnu
+++ b/js/src/gnuplot/gcTimer.gnu
@@ -15,9 +15,10 @@ set style data linespoints
 plot 'gcTimer.dat' using 2 title columnheader(2), \
 '' u 3 title columnheader(3) with points, \
 '' u 4 title columnheader(4), \
 '' u 5 title columnheader(5), \
 '' u 6 title columnheader(6) with points, \
 '' u 7 title columnheader(7) with points, \
 '' u 8 title columnheader(8) with points, \
 '' u 9 title columnheader(9) with points, \
-'' u 10 title columnheader(10) with points
+'' u 10 title columnheader(10) with points, \
+'' u 11 title columnheader(11) with points
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -303,28 +303,28 @@ ScriptAnalysis::analyzeBytecode(JSContex
      * Populate arg and local slots which can escape and be accessed in ways
      * other than through ARG* and LOCAL* opcodes (though arguments can still
      * be indirectly read but not written through 'arguments' properties).
      * All escaping locals are treated as having possible use-before-defs.
      */
 
     PodZero(escapedSlots, numSlots);
 
-    if (script->usesEval || script->usesArguments || script->compartment->debugMode()) {
+    if (script->usesEval || script->usesArguments || script->compartment()->debugMode()) {
         for (unsigned i = 0; i < nargs; i++)
             escapedSlots[ArgSlot(i)] = true;
     } else {
         for (unsigned i = 0; i < script->nClosedArgs; i++) {
             unsigned arg = script->getClosedArg(i);
             JS_ASSERT(arg < nargs);
             escapedSlots[ArgSlot(arg)] = true;
         }
     }
 
-    if (script->usesEval || script->compartment->debugMode()) {
+    if (script->usesEval || script->compartment()->debugMode()) {
         for (unsigned i = 0; i < script->nfixed; i++) {
             escapedSlots[LocalSlot(script, i)] = true;
             setLocal(i, LOCAL_USE_BEFORE_DEF);
         }
     } else {
         for (uint32 i = 0; i < script->nClosedVars; i++) {
             unsigned local = script->getClosedVar(i);
             escapedSlots[LocalSlot(script, local)] = true;
@@ -607,17 +607,17 @@ ScriptAnalysis::analyzeBytecode(JSContex
              * Note that there is no problem with code which is skipped by a thrown
              * exception but is not caught by a later handler in the same function:
              * no more code will execute, and it does not matter what is defined.
              */
             isInlineable = canTrackVars = false;
             JSTryNote *tn = script->trynotes()->vector;
             JSTryNote *tnlimit = tn + script->trynotes()->length;
             for (; tn < tnlimit; tn++) {
-                unsigned startOffset = script->main - script->code + tn->start;
+                unsigned startOffset = script->mainOffset + tn->start;
                 if (startOffset == offset + 1) {
                     unsigned catchOffset = startOffset + tn->length;
 
                     /* This will overestimate try block code, for multiple catch/finally. */
                     if (catchOffset > forwardCatch)
                         forwardCatch = catchOffset;
 
                     if (tn->kind != JSTRY_ITER) {
@@ -889,17 +889,17 @@ ScriptAnalysis::analyzeLifetimes(JSConte
         if (loop && code->jumpTarget && offset != loop->entry && offset > loop->lastBlock)
             loop->lastBlock = offset;
 
         if (code->exceptionEntry) {
             DebugOnly<bool> found = false;
             JSTryNote *tn = script->trynotes()->vector;
             JSTryNote *tnlimit = tn + script->trynotes()->length;
             for (; tn < tnlimit; tn++) {
-                unsigned startOffset = script->main - script->code + tn->start;
+                unsigned startOffset = script->mainOffset + tn->start;
                 if (startOffset + tn->length == offset) {
                     /*
                      * Extend all live variables at exception entry to the start of
                      * the try block.
                      */
                     for (unsigned i = 0; i < numSlots; i++) {
                         if (lifetimes[i].lifetime)
                             ensureVariable(lifetimes[i], startOffset - 1);
@@ -1669,17 +1669,17 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
             }
             break;
           }
 
           case JSOP_TRY: { 
             JSTryNote *tn = script->trynotes()->vector;
             JSTryNote *tnlimit = tn + script->trynotes()->length;
             for (; tn < tnlimit; tn++) {
-                unsigned startOffset = script->main - script->code + tn->start;
+                unsigned startOffset = script->mainOffset + tn->start;
                 if (startOffset == offset + 1) {
                     unsigned catchOffset = startOffset + tn->length;
 
                     if (tn->kind != JSTRY_ITER)
                         checkBranchTarget(cx, catchOffset, branchTargets, values, stackDepth);
                 }
             }
             break;
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -947,25 +947,25 @@ class ScriptAnalysis
      * True if there are any LOCAL opcodes aliasing values on the stack (above
      * script->nfixed).
      */
     bool localsAliasStack() { return localsAliasStack_; }
 
     /* Accessors for bytecode information. */
 
     Bytecode& getCode(uint32 offset) {
-        JS_ASSERT(script->compartment->activeAnalysis);
+        JS_ASSERT(script->compartment()->activeAnalysis);
         JS_ASSERT(offset < script->length);
         JS_ASSERT(codeArray[offset]);
         return *codeArray[offset];
     }
     Bytecode& getCode(const jsbytecode *pc) { return getCode(pc - script->code); }
 
     Bytecode* maybeCode(uint32 offset) {
-        JS_ASSERT(script->compartment->activeAnalysis);
+        JS_ASSERT(script->compartment()->activeAnalysis);
         JS_ASSERT(offset < script->length);
         return codeArray[offset];
     }
     Bytecode* maybeCode(const jsbytecode *pc) { return maybeCode(pc - script->code); }
 
     bool jumpTarget(uint32 offset) {
         JS_ASSERT(offset < script->length);
         return codeArray[offset] && codeArray[offset]->jumpTarget;
@@ -1143,32 +1143,32 @@ class ScriptAnalysis
     /*
      * Escaping slots include all slots that can be accessed in ways other than
      * through the corresponding LOCAL/ARG opcode. This includes all closed
      * slots in the script, all slots in scripts which use eval or are in debug
      * mode, and slots which are aliased by NAME or similar opcodes in the
      * containing script (which does not imply the variable is closed).
      */
     bool slotEscapes(uint32 slot) {
-        JS_ASSERT(script->compartment->activeAnalysis);
+        JS_ASSERT(script->compartment()->activeAnalysis);
         if (slot >= numSlots)
             return true;
         return escapedSlots[slot];
     }
 
     /*
      * Whether we distinguish different writes of this variable while doing
      * SSA analysis. Escaping locals can be written in other scripts, and the
      * presence of NAME opcodes, switch or try blocks keeps us from tracking
      * variable values at each point.
      */
     bool trackSlot(uint32 slot) { return !slotEscapes(slot) && canTrackVars; }
 
     const LifetimeVariable & liveness(uint32 slot) {
-        JS_ASSERT(script->compartment->activeAnalysis);
+        JS_ASSERT(script->compartment()->activeAnalysis);
         JS_ASSERT(!slotEscapes(slot));
         return lifetimes[slot];
     }
 
     void printSSA(JSContext *cx);
     void printTypes(JSContext *cx);
 
     void clearAllocations();
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1206,17 +1206,17 @@ JSClass js_dummy_class = {
 
 JS_PUBLIC_API(JSCrossCompartmentCall *)
 JS_EnterCrossCompartmentCallScript(JSContext *cx, JSScript *target)
 {
     CHECK_REQUEST(cx);
 
     JSObject *scriptObject = target->u.object;
     if (!scriptObject) {
-        SwitchToCompartment sc(cx, target->compartment);
+        SwitchToCompartment sc(cx, target->compartment());
         scriptObject = JS_NewGlobalObject(cx, &js_dummy_class);
         if (!scriptObject)
             return NULL;
     }
     return JS_EnterCrossCompartmentCall(cx, scriptObject);
 }
 
 JS_PUBLIC_API(JSCrossCompartmentCall *)
@@ -1256,17 +1256,17 @@ JSAutoEnterCompartment::enterAndIgnoreEr
 }
 
 namespace JS {
 
 bool
 AutoEnterScriptCompartment::enter(JSContext *cx, JSScript *target)
 {
     JS_ASSERT(!call);
-    if (cx->compartment == target->compartment) {
+    if (cx->compartment == target->compartment()) {
         call = reinterpret_cast<JSCrossCompartmentCall*>(1);
         return true;
     }
     call = JS_EnterCrossCompartmentCallScript(cx, target);
     return call != NULL;
 }
 
 bool
@@ -2190,29 +2190,32 @@ JS_TraceRuntime(JSTracer *trc)
 {
     TraceRuntime(trc);
 }
 
 JS_PUBLIC_API(void)
 JS_CallTracer(JSTracer *trc, void *thing, uint32 kind)
 {
     JS_ASSERT(thing);
-    MarkKind(trc, thing, kind);
+    JS_ASSERT(kind <= JSTRACE_LAST);
+    MarkKind(trc, thing, JSGCTraceKind(kind));
 }
 
 #ifdef DEBUG
 
 #ifdef HAVE_XPCONNECT
 #include "dump_xpc.h"
 #endif
 
 JS_PUBLIC_API(void)
-JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing, uint32 kind,
+JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing, uint32 kindIndex,
                        JSBool details)
 {
+    JS_ASSERT(kindIndex <= JSTRACE_LAST);
+    JSGCTraceKind kind = JSGCTraceKind(kindIndex);
     const char *name;
     size_t n;
 
     if (bufsize == 0)
         return;
 
     switch (kind) {
       case JSTRACE_OBJECT:
@@ -2235,29 +2238,33 @@ JS_PrintTraceThingInfo(char *buf, size_t
       }
 
       case JSTRACE_STRING:
         name = ((JSString *)thing)->isDependent()
                ? "substring"
                : "string";
         break;
 
+      case JSTRACE_SCRIPT:
+        name = "script";
+        break;
+
       case JSTRACE_SHAPE:
         name = "shape";
         break;
 
+      case JSTRACE_TYPE_OBJECT:
+        name = "type_object";
+        break;
+
 #if JS_HAS_XML_SUPPORT
       case JSTRACE_XML:
         name = "xml";
         break;
 #endif
-      default:
-        JS_ASSERT(0);
-        return;
-        break;
     }
 
     n = strlen(name);
     if (n > bufsize - 1)
         n = bufsize - 1;
     memcpy(buf, name, n + 1);
     buf += n;
     bufsize -= n;
@@ -2294,35 +2301,37 @@ JS_PrintTraceThingInfo(char *buf, size_t
             JSString *str = (JSString *)thing;
             if (str->isLinear())
                 PutEscapedString(buf, bufsize, &str->asLinear(), 0);
             else
                 JS_snprintf(buf, bufsize, "<rope: length %d>", (int)str->length());
             break;
           }
 
-          case JSTRACE_SHAPE:
+          case JSTRACE_SCRIPT:
           {
-            JS_snprintf(buf, bufsize, "<shape>");
+            JSScript *script = static_cast<JSScript *>(thing);
+            JS_snprintf(buf, bufsize, "%s:%u", script->filename, unsigned(script->lineno));
             break;
           }
 
+          case JSTRACE_SHAPE:
+          case JSTRACE_TYPE_OBJECT:
+            break;
+
 #if JS_HAS_XML_SUPPORT
           case JSTRACE_XML:
           {
             extern const char *js_xml_class_str[];
             JSXML *xml = (JSXML *)thing;
 
             JS_snprintf(buf, bufsize, "%s", js_xml_class_str[xml->xml_class]);
             break;
           }
 #endif
-          default:
-            JS_ASSERT(0);
-            break;
         }
     }
     buf[bufsize - 1] = '\0';
 }
 
 typedef struct JSHeapDumpNode JSHeapDumpNode;
 
 struct JSHeapDumpNode {
@@ -3201,17 +3210,16 @@ LookupResult(JSContext *cx, JSObject *ob
         vp->setUndefined();
         return JS_TRUE;
     }
 
     if (obj2->isNative()) {
         Shape *shape = (Shape *) prop;
 
         if (shape->isMethod()) {
-            AutoShapeRooter root(cx, shape);
             vp->setObject(shape->methodObject());
             return !!obj2->methodReadBarrier(cx, *shape, vp);
         }
 
         /* Peek at the native property's slot value, without doing a Get. */
         if (obj2->containsSlot(shape->slot)) {
             *vp = obj2->nativeGetSlot(shape->slot);
             return true;
@@ -4738,17 +4746,16 @@ CompileUCFunctionForPrincipalsCommon(JSC
         funAtom = NULL;
     } else {
         funAtom = js_Atomize(cx, name, strlen(name));
         if (!funAtom)
             return NULL;
     }
 
     Bindings bindings(cx);
-    AutoBindingsRooter root(cx, bindings);
     for (uintN i = 0; i < nargs; i++) {
         uint16 dummy;
         JSAtom *argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]));
         if (!argAtom || !bindings.addArgument(cx, argAtom, &dummy))
             return NULL;
     }
 
     JSFunction *fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, obj, funAtom);
@@ -4837,18 +4844,18 @@ JS_PUBLIC_API(JSString *)
 JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, uintN indent)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     JSPrinter *jp;
     JSString *str;
 
     CHECK_REQUEST(cx);
 #ifdef DEBUG
-    if (cx->compartment != script->compartment)
-        CompartmentChecker::fail(cx->compartment, script->compartment);
+    if (cx->compartment != script->compartment())
+        CompartmentChecker::fail(cx->compartment, script->compartment());
 #endif
     jp = js_NewPrinter(cx, name, NULL,
                        indent & ~JS_DONT_PRETTY_PRINT,
                        !(indent & JS_DONT_PRETTY_PRINT),
                        false, false);
     if (!jp)
         return NULL;
     if (js_DecompileScript(jp, script))
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1596,26 +1596,35 @@ extern JS_PUBLIC_API(void)
 JS_SetExtraGCRoots(JSRuntime *rt, JSTraceDataOp traceOp, void *data);
 
 /*
  * JS_CallTracer API and related macros for implementors of JSTraceOp, to
  * enumerate all references to traceable things reachable via a property or
  * other strong ref identified for debugging purposes by name or index or
  * a naming callback.
  *
- * By definition references to traceable things include non-null pointers
- * to JSObject, JSString and jsdouble and corresponding jsvals.
- *
  * See the JSTraceOp typedef in jspubtd.h.
  */
 
-/* Trace kinds to pass to JS_Tracing. */
-#define JSTRACE_OBJECT  0
-#define JSTRACE_STRING  1
-#define JSTRACE_SHAPE   2
+typedef enum {
+    JSTRACE_OBJECT,
+    JSTRACE_STRING,
+    JSTRACE_SCRIPT,
+
+    /*
+     * Trace kinds internal to the engine. JSTraceCallback implementation can
+     * only call JS_TraceChildren on them.
+     */ 
+#if JS_HAS_XML_SUPPORT
+    JSTRACE_XML,
+#endif
+    JSTRACE_SHAPE,
+    JSTRACE_TYPE_OBJECT,
+    JSTRACE_LAST = JSTRACE_TYPE_OBJECT
+} JSGCTraceKind;
 
 /*
  * Use the following macros to check if a particular jsval is a traceable
  * thing and to extract the thing and its kind to pass to JS_CallTracer.
  */
 static JS_ALWAYS_INLINE JSBool
 JSVAL_IS_TRACEABLE(jsval v)
 {
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -640,38 +640,35 @@ js_DumpAtoms(JSContext *cx, FILE *fp)
 #define TEMP_SIZE_LIMIT_LOG2    (TEMP_SIZE_START_LOG2 + NUM_TEMP_FREELISTS)
 
 #define TEMP_SIZE_START         JS_BIT(TEMP_SIZE_START_LOG2)
 #define TEMP_SIZE_LIMIT         JS_BIT(TEMP_SIZE_LIMIT_LOG2)
 
 JS_STATIC_ASSERT(TEMP_SIZE_START >= sizeof(JSHashTable));
 
 void
-js_InitAtomMap(JSContext *cx, JSAtomMap *map, AtomIndexMap *indices)
+js_InitAtomMap(JSContext *cx, AtomIndexMap *indices, JSAtom **atoms)
 {
-    /* Map length must already be initialized. */
-    JS_ASSERT(indices->count() == map->length);
-
     if (indices->isMap()) {
         typedef AtomIndexMap::WordMap WordMap;
         const WordMap &wm = indices->asMap();
         for (WordMap::Range r = wm.all(); !r.empty(); r.popFront()) {
             JSAtom *atom = r.front().key;
             jsatomid index = r.front().value;
-            JS_ASSERT(index < map->length);
-            map->vector[index] = atom;
+            JS_ASSERT(index < indices->count());
+            atoms[index] = atom;
         }
     } else {
         for (const AtomIndexMap::InlineElem *it = indices->asInline(), *end = indices->inlineEnd();
              it != end; ++it) {
             JSAtom *atom = it->key;
             if (!atom)
                 continue;
-            JS_ASSERT(it->value < map->length);
-            map->vector[it->value] = atom;
+            JS_ASSERT(it->value < indices->count());
+            atoms[it->value] = atom;
         }
     }
 }
 
 namespace js {
 
 bool
 IndexToIdSlow(JSContext *cx, uint32 index, jsid *idp)
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -157,21 +157,16 @@ struct DefaultHasher<jsid>
 
 /*
  * Return a printable, lossless char[] representation of a string-type atom.
  * The lifetime of the result matches the lifetime of bytes.
  */
 extern const char *
 js_AtomToPrintableString(JSContext *cx, JSAtom *atom, JSAutoByteString *bytes);
 
-struct JSAtomMap {
-    JSAtom **vector;    /* array of ptrs to indexed atoms */
-    uint32 length;      /* count of (to-be-)indexed atoms */
-};
-
 namespace js {
 
 typedef TaggedPointerEntry<JSAtom> AtomStateEntry;
 
 struct AtomHasher
 {
     struct Lookup
     {
@@ -537,11 +532,11 @@ js_InternNonIntElementId(JSContext *cx, 
                          jsid *idp, js::Value *vp);
 
 /*
  * For all unmapped atoms recorded in al, add a mapping from the atom's index
  * to its address. map->length must already be set to the number of atoms in
  * the list and map->vector must point to pre-allocated memory.
  */
 extern void
-js_InitAtomMap(JSContext *cx, JSAtomMap *map, js::AtomIndexMap *indices);
+js_InitAtomMap(JSContext *cx, js::AtomIndexMap *indices, JSAtom **atoms);
 
 #endif /* jsatom_h___ */
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -109,16 +109,19 @@ ThreadData::ThreadData()
     maxCodeCacheBytes(DEFAULT_JIT_CACHE_SIZE),
 #endif
     waiveGCQuota(false),
     dtoaState(NULL),
     nativeStackBase(GetNativeStackBase()),
     pendingProxyOperation(NULL),
     interpreterFrames(NULL)
 {
+#ifdef DEBUG
+    noGCOrAllocationCheck = 0;
+#endif
 }
 
 ThreadData::~ThreadData()
 {
     if (dtoaState)
         js_DestroyDtoaState(dtoaState);
 }
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -204,16 +204,20 @@ struct ThreadData {
     /* Base address of the native stack for the current thread. */
     jsuword             *nativeStackBase;
 
     /* List of currently pending operations on proxies. */
     PendingProxyOperation *pendingProxyOperation;
 
     ConservativeGCThreadData conservativeGC;
 
+#ifdef DEBUG
+    size_t              noGCOrAllocationCheck;
+#endif
+
     ThreadData();
     ~ThreadData();
 
     bool init();
 
     void mark(JSTracer *trc) {
         stackSpace.mark(trc);
     }
@@ -1390,17 +1394,17 @@ class AutoCheckRequestDepth {
 # define CHECK_REQUEST_THREAD(cx)   ((void) 0)
 #endif
 
 static inline JSAtom **
 FrameAtomBase(JSContext *cx, js::StackFrame *fp)
 {
     return fp->hasImacropc()
            ? cx->runtime->atomState.commonAtomsStart()
-           : fp->script()->atomMap.vector;
+           : fp->script()->atoms;
 }
 
 struct AutoResolving {
   public:
     enum Kind {
         LOOKUP,
         WATCH
     };
@@ -1473,35 +1477,32 @@ class AutoGCRooter {
      * memory corruption.
      */
     ptrdiff_t tag;
 
     JSContext * const context;
 
     enum {
         JSVAL =        -1, /* js::AutoValueRooter */
-        SHAPE =        -2, /* js::AutoShapeRooter */
+        VALARRAY =     -2, /* js::AutoValueArrayRooter */
         PARSER =       -3, /* js::Parser */
-        SCRIPT =       -4, /* js::AutoScriptRooter */
+        SHAPEVECTOR =  -4, /* js::AutoShapeVector */
         ENUMERATOR =   -5, /* js::AutoEnumStateRooter */
         IDARRAY =      -6, /* js::AutoIdArray */
         DESCRIPTORS =  -7, /* js::AutoPropDescArrayRooter */
         NAMESPACES =   -8, /* js::AutoNamespaceArray */
         XML =          -9, /* js::AutoXMLRooter */
         OBJECT =      -10, /* js::AutoObjectRooter */
         ID =          -11, /* js::AutoIdRooter */
         VALVECTOR =   -12, /* js::AutoValueVector */
         DESCRIPTOR =  -13, /* js::AutoPropertyDescriptorRooter */
         STRING =      -14, /* js::AutoStringRooter */
         IDVECTOR =    -15, /* js::AutoIdVector */
-        BINDINGS =    -16, /* js::Bindings */
-        SHAPEVECTOR = -17, /* js::AutoShapeVector */
-        OBJVECTOR =   -18, /* js::AutoObjectVector */
-        TYPE =        -19, /* js::types::AutoTypeRooter */
-        VALARRAY =    -20  /* js::AutoValueArrayRooter */
+        OBJVECTOR =   -16, /* js::AutoObjectVector */
+        TYPE =        -17  /* js::types::AutoTypeRooter */
     };
 
     private:
     /* No copy or assignment semantics. */
     AutoGCRooter(AutoGCRooter &ida);
     void operator=(AutoGCRooter &ida);
 };
 
@@ -1662,53 +1663,16 @@ class AutoArrayRooter : private AutoGCRo
     Value *array;
 
     friend void AutoGCRooter::trace(JSTracer *trc);
 
   private:
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-class AutoShapeRooter : private AutoGCRooter {
-  public:
-    AutoShapeRooter(JSContext *cx, const js::Shape *shape
-                    JS_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, SHAPE), shape(shape)
-    {
-        JS_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    friend void AutoGCRooter::trace(JSTracer *trc);
-    friend void MarkRuntime(JSTracer *trc);
-
-  private:
-    const js::Shape * const shape;
-    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
-class AutoScriptRooter : private AutoGCRooter {
-  public:
-    AutoScriptRooter(JSContext *cx, JSScript *script
-                     JS_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, SCRIPT), script(script)
-    {
-        JS_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    void setScript(JSScript *script) {
-        this->script = script;
-    }
-
-    friend void AutoGCRooter::trace(JSTracer *trc);
-
-  private:
-    JSScript *script;
-    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
 class AutoIdRooter : private AutoGCRooter
 {
   public:
     explicit AutoIdRooter(JSContext *cx, jsid id = INT_TO_JSID(0)
                           JS_GUARD_OBJECT_NOTIFIER_PARAM)
       : AutoGCRooter(cx, ID), id_(id)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
@@ -1823,32 +1787,16 @@ class AutoXMLRooter : private AutoGCRoot
     friend void MarkRuntime(JSTracer *trc);
 
   private:
     JSXML * const xml;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 #endif /* JS_HAS_XML_SUPPORT */
 
-class AutoBindingsRooter : private AutoGCRooter {
-  public:
-    AutoBindingsRooter(JSContext *cx, Bindings &bindings
-                       JS_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, BINDINGS), bindings(bindings)
-    {
-        JS_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    friend void AutoGCRooter::trace(JSTracer *trc);
-
-  private:
-    Bindings &bindings;
-    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
 class AutoLockGC {
   public:
     explicit AutoLockGC(JSRuntime *rt
                         JS_GUARD_OBJECT_NOTIFIER_PARAM)
       : rt(rt)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
         JS_LOCK_GC(rt);
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -205,17 +205,17 @@ class CompartmentChecker
                 if (JSID_IS_OBJECT(ida->vector[i]))
                     check(ida->vector[i]);
             }
         }
     }
 
     void check(JSScript *script) {
         if (script) {
-            check(script->compartment);
+            check(script->compartment());
             if (script->u.object)
                 check(script->u.object);
         }
     }
 
     void check(StackFrame *fp) {
         check(&fp->scopeChain());
     }
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -95,19 +95,17 @@ JSCompartment::JSCompartment(JSRuntime *
     emptyWithShape(NULL),
     initialRegExpShape(NULL),
     initialStringShape(NULL),
     debugModeBits(rt->debugMode ? DebugFromC : 0),
     mathCache(NULL),
     breakpointSites(rt),
     watchpointMap(NULL)
 {
-    JS_INIT_CLIST(&scripts);
-
-    PodArrayZero(scriptsToGC);
+    PodArrayZero(evalCache);
 }
 
 JSCompartment::~JSCompartment()
 {
 #if ENABLE_YARR_JIT
     Foreground::delete_(regExpAllocator);
 #endif
 
@@ -118,18 +116,18 @@ JSCompartment::~JSCompartment()
 #ifdef JS_TRACER
     Foreground::delete_(traceMonitor_);
 #endif
 
     Foreground::delete_(mathCache);
     Foreground::delete_(watchpointMap);
 
 #ifdef DEBUG
-    for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i)
-        JS_ASSERT(!scriptsToGC[i]);
+    for (size_t i = 0; i != JS_ARRAY_LENGTH(evalCache); ++i)
+        JS_ASSERT(!evalCache[i]);
 #endif
 }
 
 bool
 JSCompartment::init(JSContext *cx)
 {
     for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
         arenas[i].init();
@@ -487,61 +485,43 @@ void
 JSCompartment::markCrossCompartmentWrappers(JSTracer *trc)
 {
     JS_ASSERT(trc->context->runtime->gcCurrentCompartment);
 
     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront())
         MarkValue(trc, e.front().key, "cross-compartment wrapper");
 }
 
-struct MarkSingletonObjectOp
-{
-    JSTracer *trc;
-    MarkSingletonObjectOp(JSTracer *trc) : trc(trc) {}
-    void operator()(Cell *cell) {
-        JSObject *object = static_cast<JSObject *>(cell);
-        if (!object->isNewborn() && object->hasSingletonType())
-            MarkObject(trc, *object, "mark_types_singleton");
-    }
-};
-
-struct MarkTypeObjectOp
-{
-    JSTracer *trc;
-    MarkTypeObjectOp(JSTracer *trc) : trc(trc) {}
-    void operator()(Cell *cell) {
-        types::TypeObject *object = static_cast<types::TypeObject *>(cell);
-        MarkTypeObject(trc, object, "mark_types_scan");
-    }
-};
-
 void
 JSCompartment::markTypes(JSTracer *trc)
 {
     /*
      * Mark all scripts, type objects and singleton JS objects in the
      * compartment. These can be referred to directly by type sets, which we
      * cannot modify while code which depends on these type sets is active.
      */
     JS_ASSERT(activeAnalysis);
 
-    for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
-        JSScript *script = reinterpret_cast<JSScript *>(cursor);
-        js_TraceScript(trc, script, NULL);
+    for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
+        JSScript *script = i.get<JSScript>();
+        MarkScript(trc, script, "mark_types_script");
     }
 
-    MarkSingletonObjectOp objectCellOp(trc);
     for (unsigned thingKind = FINALIZE_OBJECT0;
          thingKind <= FINALIZE_FUNCTION_AND_OBJECT_LAST;
          thingKind++) {
-        gc::ForEachArenaAndCell(this, (FinalizeKind) thingKind, EmptyArenaOp, objectCellOp);
+        for (CellIterUnderGC i(this, FinalizeKind(thingKind)); !i.done(); i.next()) {
+            JSObject *object = i.get<JSObject>();
+            if (!object->isNewborn() && object->hasSingletonType())
+                MarkObject(trc, *object, "mark_types_singleton");
+        }
     }
 
-    MarkTypeObjectOp typeCellOp(trc);
-    gc::ForEachArenaAndCell(this, FINALIZE_TYPE_OBJECT, EmptyArenaOp, typeCellOp);
+    for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next())
+        MarkTypeObject(trc, i.get<types::TypeObject>(), "mark_types_scan");
 }
 
 void
 JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
 {
     /* Remove dead wrappers from the table. */
     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
         JS_ASSERT_IF(IsAboutToBeFinalized(cx, e.front().key.toGCThing()) &&
@@ -579,18 +559,18 @@ JSCompartment::sweep(JSContext *cx, uint
         traceMonitor()->sweep(cx);
 #endif
 
 # if defined JS_METHODJIT && defined JS_POLYIC
     /*
      * Purge all PICs in the compartment. These can reference type data and
      * need to know which types are pending collection.
      */
-    for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
-        JSScript *script = reinterpret_cast<JSScript *>(cursor);
+    for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
+        JSScript *script = i.get<JSScript>();
         if (script->hasJITCode())
             mjit::ic::PurgePICs(cx, script);
     }
 # endif
 
     bool discardScripts = !active && (releaseInterval != 0 || hasDebugModeCodeToDrop);
 
 #if defined JS_METHODJIT && defined JS_MONOIC
@@ -601,18 +581,18 @@ JSCompartment::sweep(JSContext *cx, uint
      * Initialize counter so that the first pool will be destroyed, and eventually drive
      * the amount of JIT code in never-used compartments to zero. Don't discard anything
      * for compartments which currently have active stack frames.
      */
     uint32 counter = 1;
     if (discardScripts)
         hasDebugModeCodeToDrop = false;
 
-    for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
-        JSScript *script = reinterpret_cast<JSScript *>(cursor);
+    for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
+        JSScript *script = i.get<JSScript>();
         if (script->hasJITCode()) {
             mjit::ic::SweepCallICs(cx, script, discardScripts);
             if (discardScripts) {
                 ScriptTryDestroyCode(cx, script, true, releaseInterval, counter);
                 ScriptTryDestroyCode(cx, script, false, releaseInterval, counter);
             }
         }
     }
@@ -631,19 +611,18 @@ JSCompartment::sweep(JSContext *cx, uint
          * Sweep analysis information and everything depending on it from the
          * compartment, including all remaining mjit code if inference is
          * enabled in the compartment.
          */
         if (types.inferenceEnabled) {
 #ifdef JS_METHODJIT
             mjit::ClearAllFrames(this);
 #endif
-
-            for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
-                JSScript *script = reinterpret_cast<JSScript *>(cursor);
+            for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
+                JSScript *script = i.get<JSScript>();
                 if (script->types) {
                     types::TypeScript::Sweep(cx, script);
 
                     /*
                      * On each 1/8 lifetime, release observed types for all scripts.
                      * This is always safe to do when there are no frames for the
                      * compartment on the stack.
                      */
@@ -652,67 +631,73 @@ JSCompartment::sweep(JSContext *cx, uint
                         script->types = NULL;
                     }
                 }
             }
         }
 
         types.sweep(cx);
 
-        for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
-            JSScript *script = reinterpret_cast<JSScript *>(cursor);
+        for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
+            JSScript *script = i.get<JSScript>();
             if (script->types)
                 script->types->analysis = NULL;
         }
 
         /* Reset the analysis pool, releasing all analysis and intermediate type data. */
         JS_FinishArenaPool(&oldPool);
-
-        /*
-         * Destroy eval'ed scripts, now that any type inference information referring
-         * to eval scripts has been removed.
-         */
-        js_DestroyScriptsToGC(cx, this);
     }
 
     active = false;
 }
 
 void
 JSCompartment::purge(JSContext *cx)
 {
     freeLists.purge();
     dtoaCache.purge();
 
+    /*
+     * Clear the hash and reset all evalHashLink to null before the GC. This
+     * way MarkChildren(trc, JSScript *) can assume that JSScript::u.object is
+     * not null when we have script owned by an object and not from the eval
+     * cache.
+     */
+    for (size_t i = 0; i != JS_ARRAY_LENGTH(evalCache); ++i) {
+        for (JSScript **listHeadp = &evalCache[i]; *listHeadp; ) {
+            JSScript *script = *listHeadp;
+            JS_ASSERT(GetGCThingTraceKind(script) == JSTRACE_SCRIPT);
+            *listHeadp = NULL;
+            listHeadp = &script->u.evalHashLink;
+        }
+    }
+
     nativeIterCache.purge();
     toSourceCache.destroyIfConstructed();
 
 #ifdef JS_TRACER
     /*
      * If we are about to regenerate shapes, we have to flush the JIT cache,
      * which will eventually abort any current recording.
      */
     if (cx->runtime->gcRegenShapes)
         if (hasTraceMonitor())
             traceMonitor()->needFlush = JS_TRUE;
 #endif
 
-#ifdef JS_METHODJIT
-    for (JSScript *script = (JSScript *)scripts.next;
-         &script->links != &scripts;
-         script = (JSScript *)script->links.next) {
-        if (script->hasJITCode()) {
-# if defined JS_MONOIC
-            /*
-             * MICs do not refer to data which can be GC'ed and do not generate stubs
-             * which might need to be discarded, but are sensitive to shape regeneration.
-             */
-            if (cx->runtime->gcRegenShapes)
+#if defined JS_METHODJIT && defined JS_MONOIC
+    /*
+     * MICs do not refer to data which can be GC'ed and do not generate stubs
+     * which might need to be discarded, but are sensitive to shape regeneration.
+     */
+    if (cx->runtime->gcRegenShapes) {
+        for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
+            JSScript *script = i.get<JSScript>();
+            if (script->hasJITCode())
                 mjit::ic::PurgeMICs(cx, script);
-# endif
         }
     }
 #endif
 }
 
 MathCache *
 JSCompartment::allocMathCache(JSContext *cx)
 {
@@ -756,17 +741,17 @@ JSCompartment::incBackEdgeCount(jsbyteco
     return 1;  /* oom not reported by backEdgeTable, so ignore. */
 }
 
 bool
 JSCompartment::hasScriptsOnStack(JSContext *cx)
 {
     for (AllFramesIter i(cx->stack.space()); !i.done(); ++i) {
         JSScript *script = i.fp()->maybeScript();
-        if (script && script->compartment == this)
+        if (script && script->compartment() == this)
             return true;
     }
     return false;
 }
 
 bool
 JSCompartment::setDebugModeFromC(JSContext *cx, bool b)
 {
@@ -813,22 +798,22 @@ JSCompartment::updateForDebugMode(JSCont
 
     if (enabled) {
         JS_ASSERT(!hasScriptsOnStack(cx));
     } else if (hasScriptsOnStack(cx)) {
         hasDebugModeCodeToDrop = true;
         return;
     }
 
-    // Discard JIT code for any scripts that change debugMode. This assumes
-    // that 'comp' is in the same thread as 'cx'.
-    for (JSScript *script = (JSScript *) scripts.next;
-         &script->links != &scripts;
-         script = (JSScript *) script->links.next)
-    {
+    /*
+     * Discard JIT code for any scripts that change debugMode. This assumes
+     * that 'comp' is in the same thread as 'cx'.
+     */
+    for (gc::CellIter i(cx, this, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+        JSScript *script = i.get<JSScript>();
         if (script->debugMode != enabled) {
             mjit::ReleaseScriptCode(cx, script);
             script->debugMode = enabled;
         }
     }
     hasDebugModeCodeToDrop = false;
 #endif
 }
@@ -900,17 +885,17 @@ JSCompartment::getOrCreateBreakpointSite
 
     return site;
 }
 
 void
 JSCompartment::clearBreakpointsIn(JSContext *cx, js::Debugger *dbg, JSScript *script,
                                   JSObject *handler)
 {
-    JS_ASSERT_IF(script, script->compartment == this);
+    JS_ASSERT_IF(script, script->compartment() == this);
 
     for (BreakpointSiteMap::Enum e(breakpointSites); !e.empty(); e.popFront()) {
         BreakpointSite *site = e.front().value;
         if (!script || site->script == script) {
             Breakpoint *nextbp;
             for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) {
                 nextbp = bp->nextInSite();
                 if ((!dbg || bp->debugger == dbg) && (!handler || bp->getHandler() == handler))
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -293,20 +293,21 @@ struct TraceMonitor {
 namespace mjit {
 class JaegerCompartment;
 }
 }
 
 /* Defined in jsapi.cpp */
 extern JSClass js_dummy_class;
 
-/* Number of potentially reusable scriptsToGC to search for the eval cache. */
 #ifndef JS_EVAL_CACHE_SHIFT
 # define JS_EVAL_CACHE_SHIFT        6
 #endif
+
+/* Number of buckets in the hash of eval scripts. */
 #define JS_EVAL_CACHE_SIZE          JS_BIT(JS_EVAL_CACHE_SHIFT)
 
 namespace js {
 
 class NativeIterCache {
     static const size_t SIZE = size_t(1) << 8;
     
     /* Cached native iterators. */
@@ -421,17 +422,17 @@ struct JS_FRIEND_API(JSCompartment) {
      * Trace-tree JIT recorder/interpreter state.  It's created lazily because
      * many compartments don't end up needing it.
      */
     js::TraceMonitor             *traceMonitor_;
 #endif
 
   public:
     /* Hashed lists of scripts created by eval to garbage-collect. */
-    JSScript                     *scriptsToGC[JS_EVAL_CACHE_SIZE];
+    JSScript                     *evalCache[JS_EVAL_CACHE_SIZE];
 
     void                         *data;
     bool                         active;  // GC flag, whether there are active frames
     bool                         hasDebugModeCodeToDrop;
     js::WrapperMap               crossCompartmentWrappers;
 
 #ifdef JS_METHODJIT
   private:
@@ -502,18 +503,16 @@ struct JS_FRIEND_API(JSCompartment) {
     const js::Shape              *initialStringShape;
 
   private:
     enum { DebugFromC = 1, DebugFromJS = 2 };
 
     uintN                        debugModeBits;  // see debugMode() below
 
   public:
-    JSCList                      scripts;        // scripts in this compartment
-
     js::NativeIterCache          nativeIterCache;
 
     typedef js::Maybe<js::ToSourceCache> LazyToSourceCache;
     LazyToSourceCache            toSourceCache;
 
     js::ScriptFilenameTable      scriptFilenameTable;
 
     JSCompartment(JSRuntime *rt);
@@ -535,16 +534,17 @@ struct JS_FRIEND_API(JSCompartment) {
 
     void markTypes(JSTracer *trc);
     void sweep(JSContext *cx, uint32 releaseInterval);
     void purge(JSContext *cx);
     void finishArenaLists();
     void finalizeObjectArenaLists(JSContext *cx);
     void finalizeStringArenaLists(JSContext *cx);
     void finalizeShapeArenaLists(JSContext *cx);
+    void finalizeScriptArenaLists(JSContext *cx);
     bool arenaListsAreEmpty();
 
     void setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind);
     void reduceGCTriggerBytes(uint32 amount);
 
     js::DtoaCache dtoaCache;
 
   private:
@@ -628,17 +628,16 @@ struct JS_FRIEND_API(JSCompartment) {
 
   private:
     void sweepBreakpoints(JSContext *cx);
 
   public:
     js::WatchpointMap *watchpointMap;
 };
 
-#define JS_SCRIPTS_TO_GC(cx)    ((cx)->compartment->scriptsToGC)
 #define JS_PROPERTY_TREE(cx)    ((cx)->compartment->propertyTree)
 
 /*
  * N.B. JS_ON_TRACE(cx) is true if JIT code is on the stack in the current
  * thread, regardless of whether cx is the context in which that trace is
  * executing. cx must be a context on the current thread.
  */
 static inline bool
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -177,17 +177,17 @@ JS_SetSingleStepMode(JSContext *cx, JSSc
 
     return script->setStepModeFlag(cx, singleStep);
 }
 
 jsbytecode *
 js_UntrapScriptCode(JSContext *cx, JSScript *script)
 {
     jsbytecode *code = script->code;
-    BreakpointSiteMap &sites = script->compartment->breakpointSites;
+    BreakpointSiteMap &sites = script->compartment()->breakpointSites;
     for (BreakpointSiteMap::Range r = sites.all(); !r.empty(); r.popFront()) {
         BreakpointSite *site = r.front().value;
         if (site->script == script && size_t(site->pc - script->code) < script->length) {
             if (code == script->code) {
                 size_t nbytes = script->length * sizeof(jsbytecode);
                 jssrcnote *notes = script->notes();
                 jssrcnote *sn;
                 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
@@ -207,48 +207,48 @@ js_UntrapScriptCode(JSContext *cx, JSScr
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler handler, jsval closure)
 {
     if (!CheckDebugMode(cx))
         return false;
 
-    BreakpointSite *site = script->compartment->getOrCreateBreakpointSite(cx, script, pc, NULL);
+    BreakpointSite *site = script->compartment()->getOrCreateBreakpointSite(cx, script, pc, NULL);
     if (!site)
         return false;
     site->setTrap(cx, handler, Valueify(closure));
     return true;
 }
 
 JS_PUBLIC_API(JSOp)
 JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)
 {
-    BreakpointSite *site = script->compartment->getBreakpointSite(pc);
+    BreakpointSite *site = script->compartment()->getBreakpointSite(pc);
     return site ? site->realOpcode : JSOp(*pc);
 }
 
 JS_PUBLIC_API(void)
 JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
              JSTrapHandler *handlerp, jsval *closurep)
 {
-    if (BreakpointSite *site = script->compartment->getBreakpointSite(pc)) {
+    if (BreakpointSite *site = script->compartment()->getBreakpointSite(pc)) {
         site->clearTrap(cx, NULL, handlerp, Valueify(closurep));
     } else {
         if (handlerp)
             *handlerp = NULL;
         if (closurep)
             *closurep = JSVAL_VOID;
     }
 }
 
 JS_PUBLIC_API(void)
 JS_ClearScriptTraps(JSContext *cx, JSScript *script)
 {
-    script->compartment->clearTraps(cx, script);
+    script->compartment()->clearTraps(cx, script);
 }
 
 JS_PUBLIC_API(void)
 JS_ClearAllTrapsForCompartment(JSContext *cx)
 {
     cx->compartment->clearTraps(cx, NULL);
 }
 
@@ -1084,19 +1084,19 @@ JS_GetScriptTotalSize(JSContext *cx, JSS
     JSObjectArray *objarray;
     JSPrincipals *principals;
 
     nbytes = sizeof *script;
     if (script->u.object)
         nbytes += JS_GetObjectTotalSize(cx, script->u.object);
 
     nbytes += script->length * sizeof script->code[0];
-    nbytes += script->atomMap.length * sizeof script->atomMap.vector[0];
-    for (size_t i = 0; i < script->atomMap.length; i++)
-        nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]);
+    nbytes += script->natoms * sizeof script->atoms[0];
+    for (size_t i = 0; i < script->natoms; i++)
+        nbytes += GetAtomTotalSize(cx, script->atoms[i]);
 
     if (script->filename)
         nbytes += strlen(script->filename) + 1;
 
     notes = script->notes();
     for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
         continue;
     nbytes += (sn - notes + 1) * sizeof *sn;
@@ -2173,18 +2173,23 @@ JS_DumpBytecode(JSContext *cx, JSScript 
 
     fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename, script->lineno);
     js_Disassemble(cx, script, true, &sprinter);
     fputs(sprinter.base, stdout);
     fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename, script->lineno);
 #endif
 }
 
+static void
+DumpBytecodeScriptCallback(JSContext *cx, void *data, void *thing,
+                           JSGCTraceKind traceKind, size_t thingSize)
+{
+    JS_ASSERT(traceKind == JSTRACE_SCRIPT);
+    JS_ASSERT(!data);
+    JSScript *script = static_cast<JSScript *>(thing);
+    JS_DumpBytecode(cx, script);
+}
+
 JS_PUBLIC_API(void)
 JS_DumpCompartmentBytecode(JSContext *cx)
 {
-    for (JSScript *script = (JSScript *) JS_LIST_HEAD(&cx->compartment->scripts);
-         script != (JSScript *) &cx->compartment->scripts;
-         script = (JSScript *) JS_NEXT_LINK((JSCList *)script))
-    {
-        JS_DumpBytecode(cx, script);
-    }
+    IterateCells(cx, cx->compartment, gc::FINALIZE_SCRIPT, NULL, DumpBytecodeScriptCallback);
 }
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -202,17 +202,16 @@ ArgumentsObject::create(JSContext *cx, u
     JS_STATIC_ASSERT(StrictArgumentsObject::RESERVED_SLOTS == 2);
     JSObject *obj = js_NewGCObject(cx, FINALIZE_OBJECT2);
     if (!obj)
         return NULL;
 
     EmptyShape *emptyArgumentsShape = EmptyShape::getEmptyArgumentsShape(cx);
     if (!emptyArgumentsShape)
         return NULL;
-    AutoShapeRooter shapeRoot(cx, emptyArgumentsShape);
 
     ArgumentsData *data = (ArgumentsData *)
         cx->malloc_(offsetof(ArgumentsData, slots) + argc * sizeof(Value));
     if (!data)
         return NULL;
     SetValueRangeToUndefined(data->slots, argc);
 
     /* Can't fail from here on, so initialize everything in argsobj. */
@@ -1627,19 +1626,16 @@ js_XDRFunctionObject(JSXDRState *xdr, JS
     JSScript *script = fun->u.i.script;
     if (!js_XDRScript(xdr, &script))
         return false;
     fun->u.i.script = script;
 
     if (xdr->mode == JSXDR_DECODE) {
         *objp = fun;
         fun->u.i.script->setOwnerObject(fun);
-#ifdef CHECK_SCRIPT_OWNER
-        fun->script()->owner = NULL;
-#endif
         if (!fun->u.i.script->typeSetFunction(cx, fun))
             return false;
         JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
         js_CallNewScriptHook(cx, fun->script(), fun);
     }
 
     return true;
 }
@@ -1700,52 +1696,39 @@ fun_trace(JSTracer *trc, JSObject *obj)
                            obj->getFlatClosureUpvars(), "upvars");
         }
         return;
     }
 
     if (fun->atom)
         MarkString(trc, fun->atom, "atom");
 
-    if (fun->isInterpreted() && fun->script())
-        js_TraceScript(trc, fun->script(), obj);
+    if (fun->isInterpreted() && fun->script()) {
+        CheckScriptOwner(fun->script(), obj);
+        MarkScript(trc, fun->script(), "script");
+    }
 }
 
 static void
 fun_finalize(JSContext *cx, JSObject *obj)
 {
-    /* Ignore newborn function objects. */
-    JSFunction *fun = obj->getFunctionPrivate();
-    if (!fun)
-        return;
-
-    /* Cloned function objects may be flat closures with upvars to free. */
-    if (fun != obj) {
-        if (fun->isFlatClosure() && fun->script()->bindings.hasUpvars())
-            cx->free_((void *) obj->getFlatClosureUpvars());
-        return;
-    }
-
-    /*
-     * Null-check fun->script() because the parser sets interpreted very early.
-     */
-    if (fun->isInterpreted() && fun->script())
-        js_DestroyScriptFromGC(cx, fun->script(), obj);
+    obj->finalizeUpvarsIfFlatClosure();
 }
 
 /*
  * Reserve two slots in all function objects for XPConnect.  Note that this
  * does not bloat every instance, only those on which reserved slots are set,
  * and those on which ad-hoc properties are defined.
  */
 JS_PUBLIC_DATA(Class) js_FunctionClass = {
     js_Function_str,
     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
     JSCLASS_HAS_RESERVED_SLOTS(JSFunction::CLASS_RESERVED_SLOTS) |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Function) |
+    JSCLASS_CONCURRENT_FINALIZER,
     PropertyStub,         /* addProperty */
     PropertyStub,         /* delProperty */
     PropertyStub,         /* getProperty */
     StrictPropertyStub,   /* setProperty */
     fun_enumerate,
     (JSResolveOp)fun_resolve,
     ConvertStub,
     fun_finalize,
@@ -2175,18 +2158,16 @@ Function(JSContext *cx, uintN argc, Valu
     /* Block this call if security callbacks forbid it. */
     GlobalObject *global = call.callee().getGlobal();
     if (!global->isRuntimeCodeGenEnabled(cx)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_FUNCTION);
         return false;
     }
 
     Bindings bindings(cx);
-    AutoBindingsRooter root(cx, bindings);
-
     uintN lineno;
     const char *filename = CurrentScriptFileAndLine(cx, &lineno);
 
     Value *argv = call.argv();
     uintN n = argc ? argc - 1 : 0;
     if (n > 0) {
         /*
          * Collect the function-argument arguments into one string, separated
@@ -2406,19 +2387,16 @@ js_InitFunctionClass(JSContext *cx, JSOb
     fun->flags |= JSFUN_PROTOTYPE;
 
     JSScript *script = JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, JSVERSION_DEFAULT);
     if (!script)
         return NULL;
     script->noScriptRval = true;
     script->code[0] = JSOP_STOP;
     script->code[1] = SRC_NULL;
-#ifdef CHECK_SCRIPT_OWNER
-    script->owner = NULL;
-#endif
     fun->u.i.script = script;
     fun->getType(cx)->functionScript = script;
     script->hasFunction = true;
     script->where.fun = fun;
     script->setOwnerObject(fun);
     js_CallNewScriptHook(cx, script, fun);
 
     if (obj->isGlobal()) {
@@ -2527,33 +2505,30 @@ js_CloneFunctionObject(JSContext *cx, JS
         cfun->nargs = fun->nargs;
         cfun->flags = fun->flags;
         cfun->u = fun->getFunctionPrivate()->u;
         cfun->atom = fun->atom;
         clone->setPrivate(cfun);
         if (cfun->isInterpreted()) {
             JSScript *script = cfun->script();
             JS_ASSERT(script);
-            JS_ASSERT(script->compartment == fun->compartment());
-            JS_ASSERT(script->compartment != cx->compartment);
+            JS_ASSERT(script->compartment() == fun->compartment());
+            JS_ASSERT(script->compartment() != cx->compartment);
             JS_OPT_ASSERT(script->ownerObject == fun);
 
             cfun->u.i.script = NULL;
             JSScript *cscript = js_CloneScript(cx, script);
             if (!cscript)
                 return NULL;
 
             cfun->u.i.script = cscript;
             if (!cfun->u.i.script->typeSetFunction(cx, cfun))
                 return NULL;
 
             cfun->script()->setOwnerObject(cfun);
-#ifdef CHECK_SCRIPT_OWNER
-            cfun->script()->owner = NULL;
-#endif
             js_CallNewScriptHook(cx, cfun->script(), cfun);
             Debugger::onNewScript(cx, cfun->script(), cfun, Debugger::NewHeldScript);
         }
     }
     return clone;
 }
 
 #ifdef JS_TRACER
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -101,31 +101,16 @@
 #endif
 #ifdef JS_VALGRIND
 # include <valgrind/memcheck.h>
 #endif
 
 using namespace js;
 using namespace js::gc;
 
-/*
- * Check that JSTRACE_XML follows JSTRACE_OBJECT and JSTRACE_STRING.
- */
-JS_STATIC_ASSERT(JSTRACE_OBJECT      == 0);
-JS_STATIC_ASSERT(JSTRACE_STRING      == 1);
-JS_STATIC_ASSERT(JSTRACE_SHAPE       == 2);
-JS_STATIC_ASSERT(JSTRACE_TYPE_OBJECT == 3);
-JS_STATIC_ASSERT(JSTRACE_XML         == 4);
-
-/*
- * JS_IS_VALID_TRACE_KIND assumes that JSTRACE_TYPE_OBJECT is the last non-xml
- * trace kind when JS_HAS_XML_SUPPORT is false.
- */
-JS_STATIC_ASSERT(JSTRACE_TYPE_OBJECT + 1 == JSTRACE_XML);
-
 namespace js {
 namespace gc {
 
 /* This array should be const, but that doesn't link right under GCC. */
 FinalizeKind slotsToThingKind[] = {
     /* 0 */  FINALIZE_OBJECT0,  FINALIZE_OBJECT2,  FINALIZE_OBJECT2,  FINALIZE_OBJECT4,
     /* 4 */  FINALIZE_OBJECT4,  FINALIZE_OBJECT8,  FINALIZE_OBJECT8,  FINALIZE_OBJECT8,
     /* 8 */  FINALIZE_OBJECT8,  FINALIZE_OBJECT12, FINALIZE_OBJECT12, FINALIZE_OBJECT12,
@@ -144,16 +129,17 @@ const uint8 GCThingSizeMap[] = {
     sizeof(JSObject_Slots4),    /* FINALIZE_OBJECT4_BACKGROUND  */
     sizeof(JSObject_Slots8),    /* FINALIZE_OBJECT8             */
     sizeof(JSObject_Slots8),    /* FINALIZE_OBJECT8_BACKGROUND  */
     sizeof(JSObject_Slots12),   /* FINALIZE_OBJECT12            */
     sizeof(JSObject_Slots12),   /* FINALIZE_OBJECT12_BACKGROUND */
     sizeof(JSObject_Slots16),   /* FINALIZE_OBJECT16            */
     sizeof(JSObject_Slots16),   /* FINALIZE_OBJECT16_BACKGROUND */
     sizeof(JSFunction),         /* FINALIZE_FUNCTION            */
+    sizeof(JSScript),           /* FINALIZE_SCRIPT              */
     sizeof(Shape),              /* FINALIZE_SHAPE               */
     sizeof(types::TypeObject),  /* FINALIZE_TYPE_OBJECT         */
 #if JS_HAS_XML_SUPPORT
     sizeof(JSXML),              /* FINALIZE_XML                 */
 #endif
     sizeof(JSShortString),      /* FINALIZE_SHORT_STRING        */
     sizeof(JSString),           /* FINALIZE_STRING              */
     sizeof(JSExternalString),   /* FINALIZE_EXTERNAL_STRING     */
@@ -193,16 +179,20 @@ ArenaHeader::checkSynchronizedWithFreeLi
     JS_ASSERT(firstSpan.isSameNonEmptySpan(list));
 }
 #endif
 
 template<typename T>
 inline bool
 Arena::finalize(JSContext *cx)
 {
+    /* Enforce requirements on size of T. */
+    JS_STATIC_ASSERT(sizeof(T) % Cell::CellSize == 0);
+    JS_STATIC_ASSERT(sizeof(T) <= 255);
+
     JS_ASSERT(aheader.allocated());
     JS_ASSERT(!aheader.getMarkingDelay()->link);
 
     uintptr_t thing = thingsStart(sizeof(T));
     uintptr_t lastByte = thingsEnd() - 1;
 
     FreeSpan nextFree(aheader.getFirstFreeSpan());
     nextFree.checkSpan();
@@ -782,16 +772,19 @@ MarkIfGCThingWord(JSTracer *trc, jsuword
         test = MarkArenaPtrConservatively<JSExternalString>(trc, aheader, addr);
         break;
       case FINALIZE_SHORT_STRING:
         test = MarkArenaPtrConservatively<JSShortString>(trc, aheader, addr);
         break;
       case FINALIZE_FUNCTION:
         test = MarkArenaPtrConservatively<JSFunction>(trc, aheader, addr);
         break;
+      case FINALIZE_SCRIPT:
+        test = MarkArenaPtrConservatively<JSScript>(trc, aheader, addr);
+        break;
       case FINALIZE_SHAPE:
         test = MarkArenaPtrConservatively<Shape>(trc, aheader, addr);
         break;
       case FINALIZE_TYPE_OBJECT:
         test = MarkArenaPtrConservatively<types::TypeObject>(trc, aheader, addr);
         break;
 #if JS_HAS_XML_SUPPORT
       case FINALIZE_XML:
@@ -1248,28 +1241,35 @@ ArenaList::finalizeLater(JSContext *cx)
 {
     JS_ASSERT_IF(head,
                  head->getThingKind() == FINALIZE_OBJECT0_BACKGROUND  ||
                  head->getThingKind() == FINALIZE_OBJECT2_BACKGROUND  ||
                  head->getThingKind() == FINALIZE_OBJECT4_BACKGROUND  ||
                  head->getThingKind() == FINALIZE_OBJECT8_BACKGROUND  ||
                  head->getThingKind() == FINALIZE_OBJECT12_BACKGROUND ||
                  head->getThingKind() == FINALIZE_OBJECT16_BACKGROUND ||
+                 head->getThingKind() == FINALIZE_FUNCTION            ||
                  head->getThingKind() == FINALIZE_SHORT_STRING        ||
                  head->getThingKind() == FINALIZE_STRING);
     JS_ASSERT(!cx->runtime->gcHelperThread.sweeping);
 
     /*
      * The state can be just-finished if we have not allocated any GC things
      * from the arena list after the previous background finalization.
      */
     JS_ASSERT(backgroundFinalizeState == BFS_DONE ||
               backgroundFinalizeState == BFS_JUST_FINISHED);
 
-    if (head && cx->gcBackgroundFree && cx->gcBackgroundFree->finalizeVector.append(head)) {
+    if (head && cx->gcBackgroundFree) {
+        /*
+         * To ensure the finalization order even during the background GC we
+         * must use infallibleAppend so arenas scheduled for background
+         * finalization would not be finalized now if the append fails.
+         */
+        cx->gcBackgroundFree->finalizeVector.infallibleAppend(head);
         head = NULL;
         cursor = &head;
         backgroundFinalizeState = BFS_RUN;
     } else {
         JS_ASSERT_IF(!head, cursor == &head);
         backgroundFinalizeState = BFS_DONE;
         finalizeNow<T>(cx);
     }
@@ -1300,16 +1300,19 @@ ArenaList::backgroundFinalize(JSContext 
         FinalizeArenas<JSObject_Slots8>(cx, &listHead);
         break;
       case FINALIZE_OBJECT12_BACKGROUND:
         FinalizeArenas<JSObject_Slots12>(cx, &listHead);
         break;
       case FINALIZE_OBJECT16_BACKGROUND:
         FinalizeArenas<JSObject_Slots16>(cx, &listHead);
         break;
+      case FINALIZE_FUNCTION:
+        FinalizeArenas<JSFunction>(cx, &listHead);
+        break;
       case FINALIZE_STRING:
         FinalizeArenas<JSString>(cx, &listHead);
         break;
       case FINALIZE_SHORT_STRING:
         FinalizeArenas<JSShortString>(cx, &listHead);
         break;
     }
 
@@ -1458,16 +1461,18 @@ RefillFinalizableFreeList(JSContext *cx,
       case FINALIZE_STRING:
         return RefillTypedFreeList<JSString>(cx, thingKind);
       case FINALIZE_EXTERNAL_STRING:
         return RefillTypedFreeList<JSExternalString>(cx, thingKind);
       case FINALIZE_SHORT_STRING:
         return RefillTypedFreeList<JSShortString>(cx, thingKind);
       case FINALIZE_FUNCTION:
         return RefillTypedFreeList<JSFunction>(cx, thingKind);
+      case FINALIZE_SCRIPT:
+        return RefillTypedFreeList<JSScript>(cx, thingKind);
       case FINALIZE_SHAPE:
         return RefillTypedFreeList<Shape>(cx, thingKind);
       case FINALIZE_TYPE_OBJECT:
         return RefillTypedFreeList<types::TypeObject>(cx, thingKind);
 #if JS_HAS_XML_SUPPORT
       case FINALIZE_XML:
         return RefillTypedFreeList<JSXML>(cx, thingKind);
 #endif
@@ -1475,17 +1480,17 @@ RefillFinalizableFreeList(JSContext *cx,
         JS_NOT_REACHED("bad finalize kind");
         return 0;
     }
 }
 
 } /* namespace gc */
 } /* namespace js */
 
-uint32
+JSGCTraceKind
 js_GetGCThingTraceKind(void *thing)
 {
     return GetGCThingTraceKind(thing);
 }
 
 JSBool
 js_LockGCThingRT(JSRuntime *rt, void *thing)
 {
@@ -1573,17 +1578,17 @@ GCMarker::delayMarkingChildren(const voi
 #ifdef DEBUG
     markLaterArenas++;
 #endif
 }
 
 static void
 MarkDelayedChildren(JSTracer *trc, ArenaHeader *aheader)
 {
-    unsigned traceKind = GetFinalizableTraceKind(aheader->getThingKind());
+    JSGCTraceKind traceKind = GetFinalizableTraceKind(aheader->getThingKind());
     size_t thingSize = aheader->getThingSize();
     Arena *a = aheader->getArena();
     uintptr_t end = a->thingsEnd();
     for (uintptr_t thing = a->thingsStart(thingSize); thing != end; thing += thingSize) {
         Cell *t = reinterpret_cast<Cell *>(thing);
         if (t->isMarked())
             JS_TraceChildren(trc, t, traceKind);
     }
@@ -1670,18 +1675,18 @@ gc_lock_traversal(const GCLocks::Entry &
 void
 js_TraceStackFrame(JSTracer *trc, StackFrame *fp)
 {
     MarkObject(trc, fp->scopeChain(), "scope chain");
     if (fp->isDummyFrame())
         return;
     if (fp->hasArgsObj())
         MarkObject(trc, fp->argsObj(), "arguments");
-    js_TraceScript(trc, fp->script(), NULL);
-    fp->script()->compartment->active = true;
+    MarkScript(trc, fp->script(), "script");
+    fp->script()->compartment()->active = true;
     MarkValue(trc, fp->returnValue(), "rval");
 }
 
 void
 AutoIdArray::trace(JSTracer *trc)
 {
     JS_ASSERT(tag == IDARRAY);
     gc::MarkIdRange(trc, idArray->length, idArray->vector, "JSAutoIdArray.idArray");
@@ -1696,29 +1701,20 @@ AutoEnumStateRooter::trace(JSTracer *trc
 inline void
 AutoGCRooter::trace(JSTracer *trc)
 {
     switch (tag) {
       case JSVAL:
         MarkValue(trc, static_cast<AutoValueRooter *>(this)->val, "js::AutoValueRooter.val");
         return;
 
-      case SHAPE:
-        MarkShape(trc, static_cast<AutoShapeRooter *>(this)->shape, "js::AutoShapeRooter.val");
-        return;
-
       case PARSER:
         static_cast<Parser *>(this)->trace(trc);
         return;
 
-      case SCRIPT:
-        if (JSScript *script = static_cast<AutoScriptRooter *>(this)->script)
-            js_TraceScript(trc, script, NULL);
-        return;
-
       case ENUMERATOR:
         static_cast<AutoEnumStateRooter *>(this)->trace(trc);
         return;
 
       case IDARRAY: {
         JSIdArray *ida = static_cast<AutoIdArray *>(this)->idArray;
         MarkIdRange(trc, ida->length, ida->vector, "js::AutoIdArray.idArray");
         return;
@@ -1788,33 +1784,22 @@ AutoGCRooter::trace(JSTracer *trc)
       }
 
       case SHAPEVECTOR: {
         AutoShapeVector::VectorImpl &vector = static_cast<js::AutoShapeVector *>(this)->vector;
         MarkShapeRange(trc, vector.length(), vector.begin(), "js::AutoShapeVector.vector");
         return;
       }
 
-      case BINDINGS: {
-        static_cast<js::AutoBindingsRooter *>(this)->bindings.trace(trc);
-        return;
-      }
-
       case OBJVECTOR: {
         AutoObjectVector::VectorImpl &vector = static_cast<AutoObjectVector *>(this)->vector;
         MarkObjectRange(trc, vector.length(), vector.begin(), "js::AutoObjectVector.vector");
         return;
       }
 
-      case TYPE: {
-        types::TypeObject *type = static_cast<types::AutoTypeRooter *>(this)->type;
-        MarkTypeObject(trc, type, "js::AutoTypeRooter");
-        return;
-      }
-
       case VALARRAY: {
         AutoValueArray *array = static_cast<AutoValueArray *>(this);
         MarkValueRange(trc, array->length(), array->start(), "js::AutoValueArray");
         return;
       }
     }
 
     JS_ASSERT(tag >= 0);
@@ -1984,50 +1969,45 @@ MaybeGC(JSContext *cx)
             rt->gcNextFullGCTime = now + GC_IDLE_FULL_SPAN;
         }
     }
 }
 
 } /* namespace js */
 
 void
-js_DestroyScriptsToGC(JSContext *cx, JSCompartment *comp)
-{
-    JSScript **listp, *script;
-
-    for (size_t i = 0; i != JS_ARRAY_LENGTH(comp->scriptsToGC); ++i) {
-        listp = &comp->scriptsToGC[i];
-        while ((script = *listp) != NULL) {
-            *listp = script->u.nextToGC;
-            script->u.nextToGC = NULL;
-            js_DestroyCachedScript(cx, script);
-        }
-    }
-}
-
-void
 JSCompartment::finalizeObjectArenaLists(JSContext *cx)
 {
     arenas[FINALIZE_OBJECT0]. finalizeNow<JSObject>(cx);
     arenas[FINALIZE_OBJECT2]. finalizeNow<JSObject_Slots2>(cx);
     arenas[FINALIZE_OBJECT4]. finalizeNow<JSObject_Slots4>(cx);
     arenas[FINALIZE_OBJECT8]. finalizeNow<JSObject_Slots8>(cx);
     arenas[FINALIZE_OBJECT12].finalizeNow<JSObject_Slots12>(cx);
     arenas[FINALIZE_OBJECT16].finalizeNow<JSObject_Slots16>(cx);
-    arenas[FINALIZE_FUNCTION].finalizeNow<JSFunction>(cx);
 
 #ifdef JS_THREADSAFE
     arenas[FINALIZE_OBJECT0_BACKGROUND]. finalizeLater<JSObject>(cx);
     arenas[FINALIZE_OBJECT2_BACKGROUND]. finalizeLater<JSObject_Slots2>(cx);
     arenas[FINALIZE_OBJECT4_BACKGROUND]. finalizeLater<JSObject_Slots4>(cx);
     arenas[FINALIZE_OBJECT8_BACKGROUND]. finalizeLater<JSObject_Slots8>(cx);
     arenas[FINALIZE_OBJECT12_BACKGROUND].finalizeLater<JSObject_Slots12>(cx);
     arenas[FINALIZE_OBJECT16_BACKGROUND].finalizeLater<JSObject_Slots16>(cx);
 #endif
 
+    /*
+     * We must finalize Function instances after finalizing any other objects
+     * even if we use the background finalization for the latter. See comments
+     * in JSObject::finalizeUpvarsIfFlatClosure.
+     */
+#ifdef JS_THREADSAFE
+    arenas[FINALIZE_FUNCTION].finalizeLater<JSFunction>(cx);
+#else
+    arenas[FINALIZE_FUNCTION].finalizeNow<JSFunction>(cx);
+#endif
+
 #if JS_HAS_XML_SUPPORT
     arenas[FINALIZE_XML].finalizeNow<JSXML>(cx);
 #endif
 }
 
 void
 JSCompartment::finalizeStringArenaLists(JSContext *cx)
 {
@@ -2043,16 +2023,22 @@ JSCompartment::finalizeStringArenaLists(
 
 void
 JSCompartment::finalizeShapeArenaLists(JSContext *cx)
 {
     arenas[FINALIZE_TYPE_OBJECT].finalizeNow<types::TypeObject>(cx);
     arenas[FINALIZE_SHAPE].finalizeNow<Shape>(cx);
 }
 
+void
+JSCompartment::finalizeScriptArenaLists(JSContext *cx)
+{
+    arenas[FINALIZE_SCRIPT].finalizeNow<JSScript>(cx);
+}
+
 #ifdef JS_THREADSAFE
 
 namespace js {
 
 bool
 GCHelperThread::init(JSRuntime *rt)
 {
     if (!(wakeup = PR_NewCondVar(rt->gcLock)))
@@ -2112,16 +2098,25 @@ GCHelperThread::threadLoop(JSRuntime *rt
             AutoUnlockGC unlock(rt);
             doSweep();
         }
         sweeping = false;
         PR_NotifyAllCondVar(sweepingDone);
     }
 }
 
+bool
+GCHelperThread::prepareForBackgroundSweep(JSContext *context) {
+    size_t maxArenaLists = MAX_BACKGROUND_FINALIZE_KINDS * context->runtime->compartments.length();
+    if (!finalizeVector.reserve(maxArenaLists))
+        return false;
+    cx = context;
+    return true;
+}
+
 void
 GCHelperThread::startBackgroundSweep(JSRuntime *rt, JSGCInvocationKind gckind)
 {
     /* The caller takes the GC lock. */
     JS_ASSERT(!sweeping);
     lastGCKind = gckind;
     sweeping = true;
     PR_NotifyCondVar(wakeup);
@@ -2155,16 +2150,21 @@ GCHelperThread::replenishAndFreeLater(vo
     } while (false);
     Foreground::free_(ptr);
 }
 
 void
 GCHelperThread::doSweep()
 {
     JS_ASSERT(cx);
+
+    /*
+     * We must finalize in the insert order, see comments in
+     * finalizeObjectArenaLists.
+     */
     for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i)
         ArenaList::backgroundFinalize(cx, *i);
     finalizeVector.resize(0);
     ExpireGCChunks(cx->runtime, lastGCKind);
     cx = NULL;
 
     if (freeCursor) {
         void **array = freeCursorEnd - FREE_ARRAY_LENGTH;
@@ -2371,16 +2371,18 @@ MarkAndSweep(JSContext *cx, JSCompartmen
      */
     if (comp) {
         Probes::GCStartSweepPhase(comp);
         comp->sweep(cx, 0);
         comp->finalizeObjectArenaLists(cx);
         GCTIMESTAMP(sweepObjectEnd);
         comp->finalizeStringArenaLists(cx);
         GCTIMESTAMP(sweepStringEnd);
+        comp->finalizeScriptArenaLists(cx);
+        GCTIMESTAMP(sweepScriptEnd);
         comp->finalizeShapeArenaLists(cx);
         GCTIMESTAMP(sweepShapeEnd);
         Probes::GCEndSweepPhase(comp);
     } else {
         /*
          * Some sweeping is not compartment-specific. Start a NULL-compartment
          * phase to demarcate all of that. (The compartment sweeps will nest
          * within.)
@@ -2397,16 +2399,22 @@ MarkAndSweep(JSContext *cx, JSCompartmen
         GCTIMESTAMP(sweepObjectEnd);
 
         for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++)
             (*c)->finalizeStringArenaLists(cx);
 
         GCTIMESTAMP(sweepStringEnd);
 
         for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) {
+            (*c)->finalizeScriptArenaLists(cx);
+        }
+
+        GCTIMESTAMP(sweepScriptEnd);
+
+        for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) {
             (*c)->finalizeShapeArenaLists(cx);
             Probes::GCEndSweepPhase(*c);
         }
 
         GCTIMESTAMP(sweepShapeEnd);
     }
 
 #ifdef DEBUG
@@ -2548,16 +2556,17 @@ class AutoGCSession {
  * contains the rendezvous algorithm by which we stop the world for GC.
  *
  * This thread becomes the GC thread. Wait for all other threads to quiesce.
  * Then set rt->gcRunning and return.
  */
 AutoGCSession::AutoGCSession(JSContext *cx)
   : context(cx)
 {
+    JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck);
     JSRuntime *rt = cx->runtime;
 
 #ifdef JS_THREADSAFE
     if (rt->gcThread && rt->gcThread != cx->thread())
         LetOtherGCFinish(cx);
 #endif
 
     JS_ASSERT(!rt->gcRunning);
@@ -2674,18 +2683,18 @@ GCCycle(JSContext *cx, JSCompartment *co
 #ifdef JS_THREADSAFE
         /*
          * As we about to purge caches and clear the mark bits we must wait
          * for any background finalization to finish.
          */
         JS_ASSERT(!cx->gcBackgroundFree);
         rt->gcHelperThread.waitBackgroundSweepEnd(rt);
         if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) {
-            cx->gcBackgroundFree = &rt->gcHelperThread;
-            cx->gcBackgroundFree->setContext(cx);
+            if (rt->gcHelperThread.prepareForBackgroundSweep(cx))
+                cx->gcBackgroundFree = &rt->gcHelperThread;
         }
 #endif
         MarkAndSweep(cx, comp, gckind  GCTIMER_ARG);
     }
 
 #ifdef JS_THREADSAFE
     if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) {
         JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
@@ -2843,34 +2852,34 @@ TraceRuntime(JSTracer *trc)
     MarkRuntime(trc);
 }
 
 struct IterateArenaCallbackOp
 {
     JSContext *cx;
     void *data;
     IterateArenaCallback callback;
-    size_t traceKind;
+    JSGCTraceKind traceKind;
     size_t thingSize;
     IterateArenaCallbackOp(JSContext *cx, void *data, IterateArenaCallback callback,
-                           size_t traceKind, size_t thingSize)
+                           JSGCTraceKind traceKind, size_t thingSize)
         : cx(cx), data(data), callback(callback), traceKind(traceKind), thingSize(thingSize)
     {}
     void operator()(Arena *arena) { (*callback)(cx, data, arena, traceKind, thingSize); }
 };
 
 struct IterateCellCallbackOp
 {
     JSContext *cx;
     void *data;
     IterateCellCallback callback;
-    size_t traceKind;
+    JSGCTraceKind traceKind;
     size_t thingSize;
     IterateCellCallbackOp(JSContext *cx, void *data, IterateCellCallback callback,
-                          size_t traceKind, size_t thingSize)
+                          JSGCTraceKind traceKind, size_t thingSize)
         : cx(cx), data(data), callback(callback), traceKind(traceKind), thingSize(thingSize)
     {}
     void operator()(Cell *cell) { (*callback)(cx, data, cell, traceKind, thingSize); }
 };
 
 void
 IterateCompartmentsArenasCells(JSContext *cx, void *data,
                                IterateCompartmentCallback compartmentCallback,
@@ -2891,17 +2900,17 @@ IterateCompartmentsArenasCells(JSContext
     AutoUnlockGC unlock(rt);
 
     AutoCopyFreeListToArenas copy(rt);
     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
         JSCompartment *compartment = *c;
         (*compartmentCallback)(cx, data, compartment);
 
         for (unsigned thingKind = 0; thingKind < FINALIZE_LIMIT; thingKind++) {
-            size_t traceKind = GetFinalizableTraceKind(thingKind);
+            JSGCTraceKind traceKind = GetFinalizableTraceKind(thingKind);
             size_t thingSize = GCThingSizeMap[thingKind];
             IterateArenaCallbackOp arenaOp(cx, data, arenaCallback, traceKind, thingSize);
             IterateCellCallbackOp cellOp(cx, data, cellCallback, traceKind, thingSize);
 
             ForEachArenaAndCell(compartment, (FinalizeKind) thingKind, arenaOp, cellOp);
         }
     }
 }
@@ -2922,21 +2931,28 @@ IterateCells(JSContext *cx, JSCompartmen
     AutoGCSession gcsession(cx);
 #ifdef JS_THREADSAFE
     rt->gcHelperThread.waitBackgroundSweepEnd(rt, false);
 #endif
     AutoUnlockGC unlock(rt);
 
     AutoCopyFreeListToArenas copy(rt);
 
-    size_t traceKind = GetFinalizableTraceKind(thingKind);
+    JSGCTraceKind traceKind = GetFinalizableTraceKind(thingKind);
     size_t thingSize = GCThingSizeMap[thingKind];
-    IterateCellCallbackOp cellOp(cx, data, cellCallback, traceKind, thingSize);
-
-    ForEachArenaAndCell(compartment, thingKind, EmptyArenaOp, cellOp);
+
+    if (compartment) {
+        for (CellIterUnderGC i(compartment, thingKind); !i.done(); i.next())
+            cellCallback(cx, data, i.getCell(), traceKind, thingSize);
+    } else {
+        for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
+            for (CellIterUnderGC i(*c, thingKind); !i.done(); i.next())
+                cellCallback(cx, data, i.getCell(), traceKind, thingSize);
+        }
+    }
 }
 
 namespace gc {
 
 JSCompartment *
 NewCompartment(JSContext *cx, JSPrincipals *principals)
 {
     JSRuntime *rt = cx->runtime;
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -92,27 +92,34 @@ enum FinalizeKind {
     FINALIZE_OBJECT8_BACKGROUND,
     FINALIZE_OBJECT12,
     FINALIZE_OBJECT12_BACKGROUND,
     FINALIZE_OBJECT16,
     FINALIZE_OBJECT16_BACKGROUND,
     FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND,
     FINALIZE_FUNCTION,
     FINALIZE_FUNCTION_AND_OBJECT_LAST = FINALIZE_FUNCTION,
+    FINALIZE_SCRIPT,
     FINALIZE_SHAPE,
     FINALIZE_TYPE_OBJECT,
 #if JS_HAS_XML_SUPPORT
     FINALIZE_XML,
 #endif
     FINALIZE_SHORT_STRING,
     FINALIZE_STRING,
     FINALIZE_EXTERNAL_STRING,
     FINALIZE_LIMIT
 };
 
+/*
+ * This must be an upper bound, but we do not need the least upper bound, so
+ * we just exclude non-background objects.
+ */
+const size_t MAX_BACKGROUND_FINALIZE_KINDS = FINALIZE_LIMIT - (FINALIZE_OBJECT_LAST + 1) / 2;
+
 extern JS_FRIEND_DATA(const uint8) GCThingSizeMap[];
 
 const size_t ArenaShift = 12;
 const size_t ArenaSize = size_t(1) << ArenaShift;
 const size_t ArenaMask = ArenaSize - 1;
 
 /*
  * The mark bitmap has one bit per each GC cell. For multi-cell GC things this
@@ -751,73 +758,64 @@ Cell::unmark(uint32 color) const
 }
 
 JSCompartment *
 Cell::compartment() const
 {
     return arenaHeader()->compartment;
 }
 
-#define JSTRACE_TYPE_OBJECT 3
-#define JSTRACE_XML         4
-
-/*
- * One past the maximum trace kind.
- */
-#define JSTRACE_LIMIT       5
-
 /*
  * Lower limit after which we limit the heap growth
  */
 const size_t GC_ALLOCATION_THRESHOLD = 30 * 1024 * 1024;
 
 /*
  * A GC is triggered once the number of newly allocated arenas is
  * GC_HEAP_GROWTH_FACTOR times the number of live arenas after the last GC
  * starting after the lower limit of GC_ALLOCATION_THRESHOLD.
  */
 const float GC_HEAP_GROWTH_FACTOR = 3.0f;
 
 /* Perform a Full GC every 20 seconds if MaybeGC is called */
 static const int64 GC_IDLE_FULL_SPAN = 20 * 1000 * 1000;
 
-static inline size_t
+static inline JSGCTraceKind
 GetFinalizableTraceKind(size_t thingKind)
 {
-    JS_STATIC_ASSERT(JSExternalString::TYPE_LIMIT == 8);
-
-    static const uint8 map[FINALIZE_LIMIT] = {
+    static const JSGCTraceKind map[FINALIZE_LIMIT] = {
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT0 */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT0_BACKGROUND */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT2 */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT2_BACKGROUND */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT4 */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT4_BACKGROUND */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT8 */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT8_BACKGROUND */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT12 */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT12_BACKGROUND */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT16 */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT16_BACKGROUND */
         JSTRACE_OBJECT,     /* FINALIZE_FUNCTION */
+        JSTRACE_SCRIPT,     /* FINALIZE_SCRIPT */
         JSTRACE_SHAPE,      /* FINALIZE_SHAPE */
         JSTRACE_TYPE_OBJECT,/* FINALIZE_TYPE_OBJECT */
 #if JS_HAS_XML_SUPPORT      /* FINALIZE_XML */
         JSTRACE_XML,
 #endif
         JSTRACE_STRING,     /* FINALIZE_SHORT_STRING */
         JSTRACE_STRING,     /* FINALIZE_STRING */
         JSTRACE_STRING,     /* FINALIZE_EXTERNAL_STRING */
     };
 
     JS_ASSERT(thingKind < FINALIZE_LIMIT);
     return map[thingKind];
 }
 
-inline uint32
+inline JSGCTraceKind
 GetGCThingTraceKind(const void *thing);
 
 static inline JSRuntime *
 GetGCThingRuntime(void *thing)
 {
     return reinterpret_cast<Cell *>(thing)->chunk()->info.runtime;
 }
 
@@ -876,16 +874,20 @@ class ArenaList {
     template<typename T>
     inline void finalizeLater(JSContext *cx);
 
     static void backgroundFinalize(JSContext *cx, ArenaHeader *listHead);
 
     bool willBeFinalizedLater() const {
         return backgroundFinalizeState == BFS_RUN;
     }
+
+    bool doneBackgroundFinalize() const {
+        return backgroundFinalizeState == BFS_DONE;
+    }
 #endif
 
 #ifdef DEBUG
     bool markedThingsInArenaList() {
 # ifdef JS_THREADSAFE
         /* The background finalization must have stopped at this point. */
         JS_ASSERT(backgroundFinalizeState == BFS_DONE ||
                   backgroundFinalizeState == BFS_JUST_FINISHED);
@@ -961,41 +963,68 @@ struct FreeLists {
     }
 
     /*
      * Temporarily copy the free list heads to the arenas so the code can see
      * the proper value in ArenaHeader::freeList when accessing the latter
      * outside the GC.
      */
     void copyToArenas() {
-        for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) {
-            FreeSpan *list = &lists[i];
-            if (!list->isEmpty()) {
-                ArenaHeader *aheader = list->arenaHeader();
-                JS_ASSERT(!aheader->hasFreeThings());
-                aheader->setFirstFreeSpan(list);
-            }
+        for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i)
+            copyToArena(FinalizeKind(i));
+    }
+
+    void copyToArena(FinalizeKind thingKind) {
+        FreeSpan *list = &lists[thingKind];
+        if (!list->isEmpty()) {
+            ArenaHeader *aheader = list->arenaHeader();
+            JS_ASSERT(!aheader->hasFreeThings());
+            aheader->setFirstFreeSpan(list);
         }
     }
 
     /*
      * Clear the free lists in arenas that were temporarily set there using
      * copyToArenas.
      */
     void clearInArenas() {
-        for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) {
-            FreeSpan *list = &lists[i];
-            if (!list->isEmpty()) {
-                ArenaHeader *aheader = list->arenaHeader();
-                JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(list));
-                aheader->setAsFullyUsed();
-            }
+        for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) 
+            clearInArena(FinalizeKind(i));
+    }
+
+
+    void clearInArena(FinalizeKind thingKind) {
+        FreeSpan *list = &lists[thingKind];
+        if (!list->isEmpty()) {
+            ArenaHeader *aheader = list->arenaHeader();
+            JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(list));
+            aheader->setAsFullyUsed();
         }
     }
 
+    /*
+     * Check that the free list is either empty or were synchronized with the
+     * arena using copyToArena().
+     */
+    bool isSynchronizedWithArena(FinalizeKind thingKind) {
+        FreeSpan *list = &lists[thingKind];
+        if (list->isEmpty())
+            return true;
+        ArenaHeader *aheader = list->arenaHeader();
+        if (aheader->hasFreeThings()) {
+            /*
+             * If the arena has a free list, it must be the same as one in
+             * lists.
+             */ 
+            JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(list));
+            return true;
+        }
+        return false;
+    }
+
     JS_ALWAYS_INLINE void *getNext(unsigned thingKind, size_t thingSize) {
         return lists[thingKind].allocate(thingSize);
     }
 
     void *populate(ArenaHeader *aheader, unsigned thingKind, size_t thingSize) {
         FreeSpan *list = &lists[thingKind];
         *list = aheader->getFirstFreeSpan();
         aheader->setAsFullyUsed();
@@ -1074,17 +1103,17 @@ class AutoIdVector;
 
 } /* namespace js */
 
 #ifdef DEBUG
 extern bool
 CheckAllocation(JSContext *cx);
 #endif
 
-extern JS_FRIEND_API(uint32)
+extern JS_FRIEND_API(JSGCTraceKind)
 js_GetGCThingTraceKind(void *thing);
 
 extern JSBool
 js_InitGC(JSRuntime *rt, uint32 maxbytes);
 
 extern void
 js_FinishGC(JSRuntime *rt);
 
@@ -1189,20 +1218,16 @@ extern void
 js_WaitForGC(JSRuntime *rt);
 
 #else /* !JS_THREADSAFE */
 
 # define js_WaitForGC(rt)    ((void) 0)
 
 #endif
 
-extern void
-js_DestroyScriptsToGC(JSContext *cx, JSCompartment *comp);
-
-
 namespace js {
 
 #ifdef JS_THREADSAFE
 
 /*
  * During the finalization we do not free immediately. Rather we add the
  * corresponding pointers to a buffer which we later release on a separated
  * thread.
@@ -1268,17 +1293,17 @@ class GCHelperThread {
     void freeLater(void *ptr) {
         JS_ASSERT(!sweeping);
         if (freeCursor != freeCursorEnd)
             *freeCursor++ = ptr;
         else
             replenishAndFreeLater(ptr);
     }
 
-    void setContext(JSContext *context) { cx = context; }
+    bool prepareForBackgroundSweep(JSContext *context);
 };
 
 #endif /* JS_THREADSAFE */
 
 struct GCChunkHasher {
     typedef gc::Chunk *Lookup;
 
     /*
@@ -1469,53 +1494,50 @@ struct GCMarker : public JSTracer {
             delayMarkingChildren(xml);
     }
 };
 
 void
 MarkStackRangeConservatively(JSTracer *trc, Value *begin, Value *end);
 
 typedef void (*IterateCompartmentCallback)(JSContext *cx, void *data, JSCompartment *compartment);
-typedef void (*IterateArenaCallback)(JSContext *cx, void *data, gc::Arena *arena, size_t traceKind,
-                                     size_t thingSize);
-typedef void (*IterateCellCallback)(JSContext *cx, void *data, void *thing, size_t traceKind,
-                                    size_t thingSize);
+typedef void (*IterateArenaCallback)(JSContext *cx, void *data, gc::Arena *arena,
+                                     JSGCTraceKind traceKind, size_t thingSize);
+typedef void (*IterateCellCallback)(JSContext *cx, void *data, void *thing,
+                                    JSGCTraceKind traceKind, size_t thingSize);
 
 /*
  * This function calls |compartmentCallback| on every compartment,
  * |arenaCallback| on every in-use arena, and |cellCallback| on every in-use
  * cell in the GC heap.
  */
 extern JS_FRIEND_API(void)
 IterateCompartmentsArenasCells(JSContext *cx, void *data,
                                IterateCompartmentCallback compartmentCallback,
                                IterateArenaCallback arenaCallback,
                                IterateCellCallback cellCallback);
 
-/* Invoke cellCallback on every in-use object of the specified thing kind. */
-void
+/*
+ * Invoke cellCallback on every in-use object of the specified thing kind for
+ * the given compartment or for all compartments if it is null.
+ */
+extern JS_FRIEND_API(void)
 IterateCells(JSContext *cx, JSCompartment *compartment, gc::FinalizeKind thingKind,
              void *data, IterateCellCallback cellCallback);
 
 } /* namespace js */
 
 extern void
 js_FinalizeStringRT(JSRuntime *rt, JSString *str);
 
 /*
  * Macro to test if a traversal is the marking phase of the GC.
  */
 #define IS_GC_MARKING_TRACER(trc) ((trc)->callback == NULL)
 
-#if JS_HAS_XML_SUPPORT
-# define JS_IS_VALID_TRACE_KIND(kind) ((uint32)(kind) < JSTRACE_LIMIT)
-#else
-# define JS_IS_VALID_TRACE_KIND(kind) ((uint32)(kind) <= JSTRACE_TYPE_OBJECT)
-#endif
-
 namespace js {
 namespace gc {
 
 JSCompartment *
 NewCompartment(JSContext *cx, JSPrincipals *principals);
 
 /* Tries to run a GC no matter what (used for GC zeal). */
 void
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -107,17 +107,17 @@ JSAtom::isStatic(const void *ptr)
 }
 
 namespace js {
 
 struct Shape;
 
 namespace gc {
 
-inline uint32
+inline JSGCTraceKind
 GetGCThingTraceKind(const void *thing)
 {
     JS_ASSERT(thing);
     if (JSAtom::isStatic(thing))
         return JSTRACE_STRING;
     const Cell *cell = reinterpret_cast<const Cell *>(thing);
     return GetFinalizableTraceKind(cell->arenaHeader()->getThingKind());
 }
@@ -251,16 +251,128 @@ ForEachArenaAndCell(JSCompartment *compa
             } else {
                 Cell *t = reinterpret_cast<Cell *>(thing);
                 cellOp(t);
             }
         }
     }
 }
 
+class CellIterImpl
+{
+    size_t thingSize;
+    ArenaHeader *aheader;
+    FreeSpan firstSpan;
+    const FreeSpan *span;
+    uintptr_t thing;
+    Cell *cell;
+
+  protected:
+    CellIterImpl() {
+    }
+
+    void init(JSCompartment *comp, FinalizeKind thingKind) {
+        thingSize = GCThingSizeMap[thingKind];
+        aheader = comp->arenas[thingKind].getHead();
+        firstSpan.initAsEmpty();
+        span = &firstSpan;
+        thing = span->first;
+        next();
+    }
+
+  public:
+    bool done() const {
+        return !cell;
+    }
+
+    template<typename T> T *get() const {
+        JS_ASSERT(!done());
+        return static_cast<T *>(cell);
+    }
+
+    Cell *getCell() const {
+        JS_ASSERT(!done());
+        return cell;
+    }
+
+    void next() {
+        for (;;) {
+            if (thing != span->first)
+                break;
+            if (JS_LIKELY(span->hasNext())) {
+                thing = span->last + thingSize;
+                span = span->nextSpan();
+                break;
+            }
+            if (!aheader) {
+                cell = NULL;
+                return;
+            }
+            firstSpan = aheader->getFirstFreeSpan();
+            span = &firstSpan;
+            thing = aheader->getArena()->thingsStart(thingSize);
+            aheader = aheader->next;
+        }
+        cell = reinterpret_cast<Cell *>(thing);
+        thing += thingSize;
+    }
+};
+
+class CellIterUnderGC : public CellIterImpl {
+
+  public:
+    CellIterUnderGC(JSCompartment *comp, FinalizeKind thingKind) {
+        JS_ASSERT(comp->rt->gcRunning);
+        JS_ASSERT(comp->freeLists.lists[thingKind].isEmpty());
+        init(comp, thingKind);
+    }
+};
+
+/*
+ * When using the iterator outside the GC the caller must ensure that no GC or
+ * allocations of GC things are possible and that the background finalization
+ * for the given thing kind is not enabled or is done.
+ */
+class CellIter: public CellIterImpl
+{
+    FreeLists *lists;
+    FinalizeKind thingKind;
+#ifdef DEBUG
+    size_t *counter;
+#endif
+  public:
+    CellIter(JSContext *cx, JSCompartment *comp, FinalizeKind thingKind)
+      : lists(&comp->freeLists),
+        thingKind(thingKind) {
+#ifdef JS_THREADSAFE
+        JS_ASSERT(comp->arenas[thingKind].doneBackgroundFinalize());
+#endif
+        if (lists->isSynchronizedWithArena(thingKind)) {
+            lists = NULL;
+        } else {
+            JS_ASSERT(!comp->rt->gcRunning);
+            lists->copyToArena(thingKind);
+        }
+#ifdef DEBUG
+        counter = &JS_THREAD_DATA(cx)->noGCOrAllocationCheck;
+        ++*counter;
+#endif
+        init(comp, thingKind);
+    }
+
+    ~CellIter() {
+#ifdef DEBUG
+        JS_ASSERT(*counter > 0);
+        --*counter;
+#endif
+        if (lists)
+            lists->clearInArena(thingKind);
+    }
+};
+
 /* Signatures for ArenaOp and CellOp above. */
 
 inline void EmptyArenaOp(Arena *arena) {}
 inline void EmptyCellOp(Cell *t) {}
 
 } /* namespace gc */
 } /* namespace js */
 
@@ -278,16 +390,17 @@ NewGCThing(JSContext *cx, unsigned thing
     JS_ASSERT(thingKind < js::gc::FINALIZE_LIMIT);
     JS_ASSERT(thingSize == js::gc::GCThingSizeMap[thingKind]);
 #ifdef JS_THREADSAFE
     JS_ASSERT_IF((cx->compartment == cx->runtime->atomsCompartment),
                  (thingKind == js::gc::FINALIZE_STRING) ||
                  (thingKind == js::gc::FINALIZE_SHORT_STRING));
 #endif
     JS_ASSERT(!cx->runtime->gcRunning);
+    JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck);
 
 #ifdef JS_GC_ZEAL
     if (cx->runtime->needZealousGC())
         js::gc::RunDebugGC(cx);
 #endif
 
     void *t = cx->compartment->freeLists.getNext(thingKind, thingSize);
     return static_cast<T *>(t ? t : js::gc::RefillFinalizableFreeList(cx, thingKind));
@@ -328,16 +441,22 @@ js_NewGCFunction(JSContext *cx)
     JSFunction *fun = NewGCThing<JSFunction>(cx, js::gc::FINALIZE_FUNCTION, sizeof(JSFunction));
     if (fun) {
         fun->capacity = JSObject::FUN_CLASS_RESERVED_SLOTS;
         fun->lastProp = NULL; /* Stops fun from being scanned until initializated. */
     }
     return fun;
 }
 
+inline JSScript *
+js_NewGCScript(JSContext *cx)
+{
+    return NewGCThing<JSScript>(cx, js::gc::FINALIZE_SCRIPT, sizeof(JSScript));
+}
+
 inline js::Shape *
 js_NewGCShape(JSContext *cx)
 {
     return NewGCThing<js::Shape>(cx, js::gc::FINALIZE_SHAPE, sizeof(js::Shape));
 }
 
 #if JS_HAS_XML_SUPPORT
 extern JSXML *
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -41,16 +41,17 @@
 #include "jsprf.h"
 #include "jsscope.h"
 #include "jsstr.h"
 
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 
 #include "vm/String-inl.h"
+#include "methodjit/MethodJIT.h"
 
 /*
  * There are two mostly separate mark paths. The first is a fast path used
  * internally in the GC. The second is a slow path used for root marking and
  * for API consumers like the cycle collector.
  *
  * The fast path uses explicit stacks. The basic marking process during a GC is
  * that all roots are pushed on to a mark stack, and then each item on the
@@ -87,33 +88,35 @@ PushMarkStack(GCMarker *gcmarker, JSXML 
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSObject *thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSFunction *thing);
 
 static inline void
+PushMarkStack(GCMarker *gcmarker, JSScript *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);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, types::TypeObject *thing);
 
 template<typename T>
 static inline void
 CheckMarkedThing(JSTracer *trc, T *thing)
 {
     JS_ASSERT(thing);
-    JS_ASSERT(JS_IS_VALID_TRACE_KIND(GetGCThingTraceKind(thing)));
     JS_ASSERT(trc->debugPrinter || trc->debugPrintArg);
     JS_ASSERT_IF(trc->context->runtime->gcCurrentCompartment, IS_GC_MARKING_TRACER(trc));
 
     JS_ASSERT(!JSAtom::isStatic(thing));
     JS_ASSERT(thing->isAligned());
 
     JS_ASSERT(thing->compartment());
     JS_ASSERT(thing->compartment()->rt == trc->context->runtime);
@@ -190,16 +193,25 @@ MarkObjectWithPrinter(JSTracer *trc, JSO
 {
     JS_ASSERT(trc);
     JS_ASSERT(&obj);
     JS_SET_TRACING_DETAILS(trc, printer, arg, index);
     Mark(trc, &obj);
 }
 
 void
+MarkScript(JSTracer *trc, JSScript *script, const char *name)
+{
+    JS_ASSERT(trc);
+    JS_ASSERT(script);
+    JS_SET_TRACING_NAME(trc, name);
+    Mark(trc, script);
+}
+
+void
 MarkShape(JSTracer *trc, const Shape *shape, const char *name)
 {
     JS_ASSERT(trc);
     JS_ASSERT(shape);
     JS_SET_TRACING_NAME(trc, name);
     Mark(trc, shape);
 }
 
@@ -218,17 +230,17 @@ MarkTypeObject(JSTracer *trc, types::Typ
      * only used for marking tracers; for tracers with a callback, if we
      * reenter through JS_TraceChildren then MarkChildren will *not* skip these
      * members, and we don't need to handle them here.
      */
     if (IS_GC_MARKING_TRACER(trc)) {
         if (type->singleton)
             MarkObject(trc, *type->singleton, "type_singleton");
         if (type->functionScript)
-            js_TraceScript(trc, type->functionScript, NULL);
+            MarkScript(trc, type->functionScript, "functionScript");
     }
 }
 
 #if JS_HAS_XML_SUPPORT
 void
 MarkXML(JSTracer *trc, JSXML *xml, const char *name)
 {
     JS_ASSERT(trc);
@@ -282,31 +294,46 @@ void
 PushMarkStack(GCMarker *gcmarker, JSShortString *thing)
 {
     JS_OPT_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment,
                      thing->compartment() == gcmarker->context->runtime->gcCurrentCompartment);
 
     (void) thing->markIfUnmarked(gcmarker->getMarkColor());
 }
 
+void
+PushMarkStack(GCMarker *gcmarker, JSScript *thing)
+{
+    JS_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment,
+                 thing->compartment() == gcmarker->context->runtime->gcCurrentCompartment);
+
+    /*
+     * 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
 ScanShape(GCMarker *gcmarker, const Shape *shape);
 
 void
 PushMarkStack(GCMarker *gcmarker, const Shape *thing)
 {
     JS_OPT_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
+static 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);
         }
@@ -365,40 +392,41 @@ MarkIdRange(JSTracer *trc, jsid *beg, js
 
 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)
+MarkKind(JSTracer *trc, void *thing, JSGCTraceKind 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;
-        case JSTRACE_TYPE_OBJECT:
-            Mark(trc, reinterpret_cast<types::TypeObject *>(thing));
-            break;
+      case JSTRACE_OBJECT:
+        Mark(trc, reinterpret_cast<JSObject *>(thing));
+        break;
+      case JSTRACE_STRING:
+        MarkString(trc, reinterpret_cast<JSString *>(thing));
+        break;
+      case JSTRACE_SCRIPT:
+        Mark(trc, static_cast<JSScript *>(thing));
+        break;
+      case JSTRACE_SHAPE:
+        Mark(trc, reinterpret_cast<Shape *>(thing));
+        break;
+      case JSTRACE_TYPE_OBJECT:
+        Mark(trc, reinterpret_cast<types::TypeObject *>(thing));
+        break;
 #if JS_HAS_XML_SUPPORT
-        case JSTRACE_XML:
-            Mark(trc, reinterpret_cast<JSXML *>(thing));
-            break;
+      case JSTRACE_XML:
+        Mark(trc, static_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()) {
@@ -457,17 +485,17 @@ MarkShapeRange(JSTracer *trc, const Shap
 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)
+MarkGCThing(JSTracer *trc, void *thing, JSGCTraceKind kind)
 {
     if (!thing)
         return;
 
     MarkKind(trc, thing, kind);
 }
 
 void
@@ -488,17 +516,17 @@ MarkGCThing(JSTracer *trc, void *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)
+Mark(JSTracer *trc, void *thing, JSGCTraceKind 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)
@@ -508,16 +536,22 @@ MarkRoot(JSTracer *trc, JSObject *thing,
 
 void
 MarkRoot(JSTracer *trc, JSString *thing, const char *name)
 {
     MarkString(trc, thing, name);
 }
 
 void
+MarkRoot(JSTracer *trc, JSScript *thing, const char *name)
+{
+    MarkScript(trc, thing, name);
+}
+
+void
 MarkRoot(JSTracer *trc, const Shape *thing, const char *name)
 {
     MarkShape(trc, thing, name);
 }
 
 void
 MarkRoot(JSTracer *trc, types::TypeObject *thing, const char *name)
 {
@@ -563,29 +597,24 @@ PrintPropertyMethod(JSTracer *trc, char 
     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: {
+        JSGCTraceKind kind = v.gcKind();
+        if (kind == JSTRACE_STRING) {
             JSString *str = (JSString *)v.toGCThing();
             if (!str->isStaticAtom())
                 PushMarkStack(gcmarker, str);
-            break;
-          }
-          case JSTRACE_OBJECT:
+        } else {
+            JS_ASSERT(kind == 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:
@@ -787,16 +816,62 @@ MarkChildren(JSTracer *trc, JSString *st
         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, JSScript *script)
+{
+    CheckScript(script, NULL);
+
+#ifdef JS_CRASH_DIAGNOSTICS
+    JSRuntime *rt = trc->context->runtime;
+    JS_OPT_ASSERT_IF(rt->gcCheckCompartment, script->compartment() == rt->gcCheckCompartment);
+#endif
+    
+    MarkAtomRange(trc, script->natoms, script->atoms, "atoms");
+
+    if (JSScript::isValidOffset(script->objectsOffset)) {
+        JSObjectArray *objarray = script->objects();
+        MarkObjectRange(trc, objarray->length, objarray->vector, "objects");
+    }
+
+    if (JSScript::isValidOffset(script->regexpsOffset)) {
+        JSObjectArray *objarray = script->regexps();
+        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->isCachedEval && script->u.object)
+        MarkObject(trc, *script->u.object, "object");
+    if (script->hasFunction)
+        MarkObject(trc, *script->function(), "script_fun");
+
+    if (IS_GC_MARKING_TRACER(trc) && script->filename)
+        js_MarkScriptFilename(script->filename);
+
+    script->bindings.trace(trc);
+
+#ifdef JS_METHODJIT
+    if (script->jitNormal)
+        script->jitNormal->trace(trc);
+    if (script->jitCtor)
+        script->jitCtor->trace(trc);
+#endif
+}
+
 void
 MarkChildren(JSTracer *trc, const Shape *shape)
 {
 restart:
     MarkId(trc, shape->propid, "propid");
 
     if (shape->hasGetterValue() && shape->getter())
         MarkObjectWithPrinter(trc, *shape->getterObject(), PrintPropertyGetterOrSetter, shape, 0);
@@ -833,17 +908,17 @@ ScanTypeObject(GCMarker *gcmarker, types
                 PushMarkStack(gcmarker, type->emptyShapes[i]);
         }
     }
 
     if (type->proto)
         PushMarkStack(gcmarker, type->proto);
 
     if (type->newScript) {
-        js_TraceScript(gcmarker, type->newScript->script, NULL);
+        PushMarkStack(gcmarker, type->newScript->script);
         PushMarkStack(gcmarker, type->newScript->shape);
     }
 
     /*
      * Don't need to trace singleton or functionScript, an object with this
      * type must have already been traced and it will also hold a reference
      * on the script (singleton and functionScript types cannot be the newType
      * of another object). Attempts to mark type objects directly must use
@@ -873,22 +948,22 @@ MarkChildren(JSTracer *trc, types::TypeO
 
     if (type->proto)
         MarkObject(trc, *type->proto, "type_proto");
 
     if (type->singleton)
         MarkObject(trc, *type->singleton, "type_singleton");
 
     if (type->newScript) {
-        js_TraceScript(trc, type->newScript->script, NULL);
+        MarkScript(trc, type->newScript->script, "type_new_script");
         MarkShape(trc, type->newScript->shape, "type_new_shape");
     }
 
     if (type->functionScript)
-        js_TraceScript(trc, type->functionScript, NULL);
+        MarkScript(trc, type->functionScript, "functionScript");
 }
 
 #ifdef JS_HAS_XML_SUPPORT
 void
 MarkChildren(JSTracer *trc, JSXML *xml)
 {
     js_TraceXML(trc, xml);
 }
@@ -931,38 +1006,45 @@ GCMarker::drainMarkStack()
     }
 
     rt->gcCheckCompartment = NULL;
 }
 
 } /* namespace js */
 
 JS_PUBLIC_API(void)
-JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind)
+JS_TraceChildren(JSTracer *trc, void *thing, uint32 kindIndex)
 {
+    JS_ASSERT(kindIndex <= JSTRACE_LAST);
+    JSGCTraceKind kind = JSGCTraceKind(kindIndex);
     switch (kind) {
-      case JSTRACE_OBJECT:
-	MarkChildren(trc, (JSObject *)thing);
+      default:
+        JS_ASSERT(kind == JSTRACE_OBJECT);
+	MarkChildren(trc, static_cast<JSObject *>(thing));
         break;
 
       case JSTRACE_STRING:
-	MarkChildren(trc, (JSString *)thing);
+	MarkChildren(trc, static_cast<JSString *>(thing));
+        break;
+
+      case JSTRACE_SCRIPT:
+	MarkChildren(trc, static_cast<JSScript *>(thing));
         break;
 
       case JSTRACE_SHAPE:
-	MarkChildren(trc, (js::Shape *)thing);
+	MarkChildren(trc, static_cast<Shape *>(thing));
         break;
 
       case JSTRACE_TYPE_OBJECT:
         MarkChildren(trc, (types::TypeObject *)thing);
         break;
 
 #if JS_HAS_XML_SUPPORT
       case JSTRACE_XML:
-        MarkChildren(trc, (JSXML *)thing);
+        MarkChildren(trc, static_cast<JSXML *>(thing));
         break;
 #endif
     }
 }
 
 inline void
 JSObject::scanSlots(GCMarker *gcmarker)
 {
--- a/js/src/jsgcmark.h
+++ b/js/src/jsgcmark.h
@@ -69,28 +69,28 @@ MarkObject(JSTracer *trc, JSObject &obj,
 void
 MarkCrossCompartmentObject(JSTracer *trc, JSObject &obj, const char *name);
 
 void
 MarkObjectWithPrinter(JSTracer *trc, JSObject &obj, JSTraceNamePrinter printer,
 		      const void *arg, size_t index);
 
 void
+MarkScript(JSTracer *trc, JSScript *script, const char *name);
+
+void
 MarkShape(JSTracer *trc, const Shape *shape, const char *name);
 
 void
 MarkTypeObject(JSTracer *trc, types::TypeObject *type, 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);
 
@@ -99,17 +99,17 @@ MarkId(JSTracer *trc, jsid id, const cha
 
 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);
+MarkKind(JSTracer *trc, void *thing, JSGCTraceKind kind);
 
 void
 MarkValueRaw(JSTracer *trc, const js::Value &v);
 
 void
 MarkValue(JSTracer *trc, const js::Value &v, const char *name);
 
 /*
@@ -149,16 +149,19 @@ Mark(JSTracer *trc, void *thing, uint32 
 
 void
 MarkRoot(JSTracer *trc, JSObject *thing, const char *name);
 
 void
 MarkRoot(JSTracer *trc, JSString *thing, const char *name);
 
 void
+MarkRoot(JSTracer *trc, JSScript *thing, const char *name);
+
+void
 MarkRoot(JSTracer *trc, const Shape *thing, const char *name);
 
 void
 MarkRoot(JSTracer *trc, types::TypeObject *thing, const char *name);
 
 void
 MarkRoot(JSTracer *trc, JSXML *thing, const char *name);
 
@@ -167,14 +170,17 @@ MarkChildren(JSTracer *trc, JSObject *ob
 
 void
 MarkChildren(JSTracer *trc, JSString *str);
 
 void
 MarkChildren(JSTracer *trc, const Shape *shape);
 
 void
+MarkChildren(JSTracer *trc, JSScript *script);
+
+void
 MarkChildren(JSTracer *trc, JSXML *xml);
 
 }
 }
 
 #endif
--- a/js/src/jsgcstats.cpp
+++ b/js/src/jsgcstats.cpp
@@ -150,39 +150,44 @@ GCMarker::dumpConservativeRoots()
 
     conservativeStats.dump(fp);
 
     for (void **thingp = conservativeRoots.begin(); thingp != conservativeRoots.end(); ++thingp) {
         void *thing = thingp;
         fprintf(fp, "  %p: ", thing);
         
         switch (GetGCThingTraceKind(thing)) {
-          default:
-            JS_NOT_REACHED("Unknown trace kind");
-
           case JSTRACE_OBJECT: {
             JSObject *obj = (JSObject *) thing;
             fprintf(fp, "object %s", obj->getClass()->name);
             break;
           }
-          case JSTRACE_SHAPE: {
-            fprintf(fp, "shape");
-            break;
-          }
           case JSTRACE_STRING: {
             JSString *str = (JSString *) thing;
             if (str->isLinear()) {
                 char buf[50];
                 PutEscapedString(buf, sizeof buf, &str->asLinear(), '"');
                 fprintf(fp, "string %s", buf);
             } else {
                 fprintf(fp, "rope: length %d", (int)str->length());
             }
             break;
           }
+          case JSTRACE_SCRIPT: {
+            fprintf(fp, "shape");
+            break;
+          }
+          case JSTRACE_SHAPE: {
+            fprintf(fp, "shape");
+            break;
+          }
+          case JSTRACE_TYPE_OBJECT: {
+            fprintf(fp, "type_object");
+            break;
+          }
 # if JS_HAS_XML_SUPPORT
           case JSTRACE_XML: {
             JSXML *xml = (JSXML *) thing;
             fprintf(fp, "xml %u", (unsigned)xml->xml_class);
             break;
           }
 # endif
         }
@@ -253,33 +258,35 @@ GCTimer::finish(bool lastGC)
     if (startMark > 0) {
         double appTime = TIMEDIFF(getFirstEnter(), enter);
         double gcTime = TIMEDIFF(enter, end);
         double waitTime = TIMEDIFF(enter, startMark);
         double markTime = TIMEDIFF(startMark, startSweep);
         double sweepTime = TIMEDIFF(startSweep, sweepDestroyEnd);
         double sweepObjTime = TIMEDIFF(startSweep, sweepObjectEnd);
         double sweepStringTime = TIMEDIFF(sweepObjectEnd, sweepStringEnd);
-        double sweepShapeTime = TIMEDIFF(sweepStringEnd, sweepShapeEnd);
+        double sweepScriptTime = TIMEDIFF(sweepStringEnd, sweepScriptEnd);
+        double sweepShapeTime = TIMEDIFF(sweepScriptEnd, sweepShapeEnd);
         double destroyTime = TIMEDIFF(sweepShapeEnd, sweepDestroyEnd);
         double endTime = TIMEDIFF(sweepDestroyEnd, end);
 
 #if defined(JSGC_TESTPILOT)
         GCData &data = rt->gcData;
         size_t oldLimit = (data.start + data.count) % GCData::INFO_LIMIT;
         data.count += 1;
 
         JSGCInfo &info = data.info[oldLimit];
         info.appTime = appTime;
         info.gcTime = gcTime;
         info.waitTime = waitTime;
         info.markTime = markTime;
         info.sweepTime = sweepTime;
         info.sweepObjTime = sweepObjTime;
         info.sweepStringTime = sweepStringTime;
+        info.sweepScriptTime = sweepScriptTime;
         info.sweepShapeTime = sweepShapeTime;
         info.destroyTime = destroyTime;
         info.endTime = endTime;
         info.isCompartmental = isCompartmental;
 #endif
 
 #if defined(MOZ_GCTIMER)
         static FILE *gcFile;
@@ -292,30 +299,30 @@ GCTimer::finish(bool lastGC)
             } else if (!strcmp(gcTimerStatPath, "stderr")) {
                 gcFile = stderr;
                 fullFormat = false;
             } else {
                 gcFile = fopen(gcTimerStatPath, "a");
                 JS_ASSERT(gcFile);
                 fullFormat = true;
                 fprintf(gcFile, "     AppTime,  Total,   Wait,   Mark,  Sweep, FinObj,"
-                        " FinStr, SwShapes, Destroy,    End, +Chu, -Chu, T, Reason\n");
+                        " FinStr, SwScripts, SwShapes, Destroy,    End, +Chu, -Chu, T, Reason\n");
             }
         }
 
         if (!fullFormat) {
             fprintf(stderr, "%f %f %f\n",
                     TIMEDIFF(enter, end),
                     TIMEDIFF(startMark, startSweep),
                     TIMEDIFF(startSweep, sweepDestroyEnd));
         } else {
-            /*               App   , Tot  , Wai  , Mar  , Swe  , FiO  , FiS  , SwS  , Des   , End */
-            fprintf(gcFile, "%12.0f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %8.1f,  %6.1f, %6.1f, ",
+            /*               App   , Tot  , Wai  , Mar  , Swe  , FiO  , FiS  , SwScr , SwS  , Des   , End */
+            fprintf(gcFile, "%12.0f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %8.1f,  %6.1f, %6.1f, ",
                     appTime, gcTime, waitTime, markTime, sweepTime, sweepObjTime, sweepStringTime,
-                    sweepShapeTime, destroyTime, endTime);
+                    sweepScriptTime, sweepShapeTime, destroyTime, endTime);
             fprintf(gcFile, "%4d, %4d,", newChunkCount, destroyChunkCount);
             fprintf(gcFile, " %s, %s\n", isCompartmental ? "C" : "G", gcReasons[gcReason]);
         }
         fflush(gcFile);
         
         if (lastGC && gcFile != stdout && gcFile != stderr)
             fclose(gcFile);
 #endif
--- a/js/src/jsgcstats.h
+++ b/js/src/jsgcstats.h
@@ -44,17 +44,18 @@
 #endif
 
 #ifdef JSGC_TESTPILOT
 JS_BEGIN_EXTERN_C
 
 struct JSGCInfo
 {
     double appTime, gcTime, waitTime, markTime, sweepTime;
-    double sweepObjTime, sweepStringTime, sweepShapeTime, destroyTime, endTime;
+    double sweepObjTime, sweepStringTime, sweepScriptTime, sweepShapeTime;
+    double destroyTime, endTime;
     bool isCompartmental;
 };
 
 extern JS_PUBLIC_API(void)
 JS_SetGCInfoEnabled(JSRuntime *rt, bool enabled);
 
 extern JS_PUBLIC_API(bool)
 JS_GetGCInfoEnabled(JSRuntime *rt);
@@ -129,16 +130,17 @@ struct GCTimer
 {
     JSRuntime *rt;
 
     uint64 enter;
     uint64 startMark;
     uint64 startSweep;
     uint64 sweepObjectEnd;
     uint64 sweepStringEnd;
+    uint64 sweepScriptEnd;
     uint64 sweepShapeEnd;
     uint64 sweepDestroyEnd;
     uint64 end;
 
     bool isCompartmental;
     bool enabled; /* Disabled timers should cause no PRMJ calls. */
 
     GCTimer(JSRuntime *rt, JSCompartment *comp);
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2058,26 +2058,24 @@ TypeCompartment::nukeTypes(JSContext *cx
     }
 
 #ifdef JS_METHODJIT
 
     JSCompartment *compartment = cx->compartment;
     mjit::ExpandInlineFrames(compartment);
 
     /* Throw away all JIT code in the compartment, but leave everything else alone. */
-    for (JSCList *cursor = compartment->scripts.next;
-         cursor != &compartment->scripts;
-         cursor = cursor->next) {
-        JSScript *script = reinterpret_cast<JSScript *>(cursor);
+
+    for (gc::CellIter i(cx, cx->compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+        JSScript *script = i.get<JSScript>();
         if (script->hasJITCode()) {
             mjit::Recompiler recompiler(cx, script);
             recompiler.recompile();
         }
     }
-
 #endif /* JS_METHODJIT */
 
 }
 
 void
 TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script)
 {
 #ifdef JS_METHODJIT
@@ -2135,80 +2133,56 @@ TypeCompartment::monitorBytecode(JSConte
 
     cx->compartment->types.addPendingRecompile(cx, script);
 
     /* Trigger recompilation of any inline callers. */
     if (script->hasFunction && !script->function()->hasLazyType())
         ObjectStateChange(cx, script->function()->type(), false, true);
 }
 
-/*
- * State for keeping track of which property type sets contain an object we are
- * scrubbing from all properties in the compartment. We make a list of
- * properties to update and fix them afterwards, as adding types can't be done
- * with the GC locked (as is done in IterateCells), and can potentially make
- * new type objects as well.
- */
-struct MarkSetsUnknownState
-{
-    TypeObject *target;
-    Vector<TypeSet *> pending;
-
-    MarkSetsUnknownState(JSContext *cx, TypeObject *target)
-        : target(target), pending(cx)
-    {}
-};
-
-static void
-MarkObjectSetsUnknownCallback(JSContext *cx, void *data, void *thing,
-                              size_t traceKind, size_t thingSize)
-{
-    MarkSetsUnknownState *state = (MarkSetsUnknownState *) data;
-    TypeObject *object = (TypeObject *) thing;
-
-    unsigned count = object->getPropertyCount();
-    for (unsigned i = 0; i < count; i++) {
-        Property *prop = object->getProperty(i);
-        if (prop && prop->types.hasType(Type::ObjectType(state->target))) {
-            if (!state->pending.append(&prop->types))
-                cx->compartment->types.setPendingNukeTypes(cx);
-        }
-    }
-}
-
 void
 TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
 {
     JS_ASSERT(this == &cx->compartment->types);
     JS_ASSERT(!(target->flags & OBJECT_FLAG_SETS_MARKED_UNKNOWN));
     JS_ASSERT(!target->singleton);
     JS_ASSERT(target->unknownProperties());
     target->flags |= OBJECT_FLAG_SETS_MARKED_UNKNOWN;
 
     AutoEnterTypeInference enter(cx);
 
     /*
      * Mark both persistent and transient type sets which contain obj as having
      * a generic object type. It is not sufficient to mark just the persistent
      * sets, as analysis of individual opcodes can pull type objects from
      * static information (like initializer objects at various offsets).
+     * 
+     * We make a list of properties to update and fix them afterwards, as adding
+     * types can't be done while iterating over cells as it can potentially make
+     * new type objects as well or trigger GC.
      */
-
-    MarkSetsUnknownState state(cx, target);
-
-    IterateCells(cx, cx->compartment, gc::FINALIZE_TYPE_OBJECT,
-                 (void *) &state, MarkObjectSetsUnknownCallback);
-
-    for (unsigned i = 0; i < state.pending.length(); i++)
-        state.pending[i]->addType(cx, Type::AnyObjectType());
-
-    for (JSCList *cursor = cx->compartment->scripts.next;
-         cursor != &cx->compartment->scripts;
-         cursor = cursor->next) {
-        JSScript *script = reinterpret_cast<JSScript *>(cursor);
+    Vector<TypeSet *> pending(cx);
+    for (gc::CellIter i(cx, cx->compartment, gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
+        TypeObject *object = i.get<TypeObject>();
+
+        unsigned count = object->getPropertyCount();
+        for (unsigned i = 0; i < count; i++) {
+            Property *prop = object->getProperty(i);
+            if (prop && prop->types.hasType(Type::ObjectType(target))) {
+                if (!pending.append(&prop->types))
+                    cx->compartment->types.setPendingNukeTypes(cx);
+            }
+        }
+    }
+
+    for (unsigned i = 0; i < pending.length(); i++)
+        pending[i]->addType(cx, Type::AnyObjectType());
+
+    for (gc::CellIter i(cx, cx->compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
+        JSScript *script = i.get<JSScript>();
         if (script->types) {
             unsigned count = TypeScript::NumTypeSets(script);
             TypeSet *typeArray = script->types->typeArray();
             for (unsigned i = 0; i < count; i++) {
                 if (typeArray[i].hasType(Type::ObjectType(target)))
                     typeArray[i].addType(cx, Type::AnyObjectType());
             }
         }
@@ -2301,42 +2275,49 @@ ScriptAnalysis::addSingletonTypeBarrier(
     TypeBarrier *barrier =
         ArenaNew<TypeBarrier>(cx->compartment->pool, target, Type::UndefinedType(),
                               singleton, singletonId);
 
     barrier->next = code.typeBarriers;
     code.typeBarriers = barrier;
 }
 
+static void
+PrintScriptTypeCallback(JSContext *cx, void *data, void *thing,
+                        JSGCTraceKind traceKind, size_t thingSize)
+{
+    JS_ASSERT(!data);
+    JS_ASSERT(traceKind == JSTRACE_SCRIPT);
+    JSScript *script = static_cast<JSScript *>(thing);
+    if (script->hasAnalysis() && script->analysis()->ranInference())
+        script->analysis()->printTypes(cx);
+}
+
 #ifdef DEBUG
 static void
 PrintObjectCallback(JSContext *cx, void *data, void *thing,
-                    size_t traceKind, size_t thingSize)
+                    JSGCTraceKind traceKind, size_t thingSize)
 {
+    JS_ASSERT(traceKind == JSTRACE_OBJECT);
     TypeObject *object = (TypeObject *) thing;
     object->print(cx);
 }
 #endif
 
 void
 TypeCompartment::print(JSContext *cx, bool force)
 {
     JSCompartment *compartment = this->compartment();
 
-    if (JS_CLIST_IS_EMPTY(&compartment->scripts))
-        return;
-
     if (!force && !InferSpewActive(ISpewResult))
         return;
 
-    for (JSScript *script = (JSScript *)compartment->scripts.next;
-         &script->links != &compartment->scripts;
-         script = (JSScript *)script->links.next) {
-        if (script->hasAnalysis() && script->analysis()->ranInference())
-            script->analysis()->printTypes(cx);
+    {
+        AutoUnlockGC unlock(cx->runtime);
+        IterateCells(cx, compartment, gc::FINALIZE_SCRIPT, cx, PrintScriptTypeCallback);
     }
 
 #ifdef DEBUG
     {
         AutoUnlockGC unlock(cx->runtime);
         IterateCells(cx, compartment, gc::FINALIZE_TYPE_OBJECT, NULL, PrintObjectCallback);
     }
 #endif
@@ -4468,17 +4449,17 @@ CheckNewScriptProperties(JSContext *cx, 
 /////////////////////////////////////////////////////////////////////
 // Printing
 /////////////////////////////////////////////////////////////////////
 
 void
 ScriptAnalysis::printTypes(JSContext *cx)
 {
     AutoEnterAnalysis enter(cx);
-    TypeCompartment *compartment = &script->compartment->types;
+    TypeCompartment *compartment = &script->compartment()->types;
 
     /*
      * Check if there are warnings for used values with unknown types, and build
      * statistics about the size of type sets found for stack values.
      */
     for (unsigned offset = 0; offset < script->length; offset++) {
         if (!maybeCode(offset))
             continue;
@@ -5006,17 +4987,16 @@ JSScript::typeSetFunction(JSContext *cx,
     if (singleton) {
         if (!fun->setSingletonType(cx))
             return false;
     } else {
         TypeObject *type = cx->compartment->types.newTypeObject(cx, this,
                                                                 JSProto_Function, fun->getProto());
         if (!type)
             return false;
-        AutoTypeRooter root(cx, type);
 
         fun->setType(type);
         type->functionScript = this;
     }
 
     return true;
 }
 
@@ -5526,17 +5506,17 @@ TypeCompartment::~TypeCompartment()
 
     if (allocationSiteTable)
         Foreground::delete_(allocationSiteTable);
 }
 
 /* static */ void
 TypeScript::Sweep(JSContext *cx, JSScript *script)
 {
-    JSCompartment *compartment = script->compartment;
+    JSCompartment *compartment = script->compartment();
     JS_ASSERT(compartment->types.inferenceEnabled);
 
     unsigned num = NumTypeSets(script);
     TypeSet *typeArray = script->types->typeArray();
 
     if (script->isAboutToBeFinalized(cx)) {
         /* Release all memory associated with the persistent type sets. */
         for (unsigned i = 0; i < num; i++)
@@ -5612,17 +5592,17 @@ TypeObject::dynamicSize()
 }
 
 static void
 GetScriptMemoryStats(JSScript *script, TypeInferenceMemoryStats *stats)
 {
     if (!script->types)
         return;
 
-    if (!script->compartment->types.inferenceEnabled) {
+    if (!script->compartment()->types.inferenceEnabled) {
         stats->scripts += sizeof(TypeScript);
         return;
     }
 
     unsigned count = TypeScript::NumTypeSets(script);
     stats->scripts += sizeof(TypeScript) + count * sizeof(TypeSet);
 
     TypeResult *result = script->types->dynamicList;
@@ -5648,22 +5628,18 @@ JS_GetTypeInferenceMemoryStats(JSContext
      * by being copied to the replacement pool. This memory will be counted too
      * and deducted from the amount of temporary data.
      */
     stats->temporary += ArenaAllocatedSize(compartment->pool);
 
     /* Pending arrays are cleared on GC along with the analysis pool. */
     stats->temporary += sizeof(TypeCompartment::PendingWork) * compartment->types.pendingCapacity;
 
-    for (JSCList *cursor = compartment->scripts.next;
-         cursor != &compartment->scripts;
-         cursor = cursor->next) {
-        JSScript *script = reinterpret_cast<JSScript *>(cursor);
-        GetScriptMemoryStats(script, stats);
-    }
+    for (gc::CellIter i(cx, compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next())
+        GetScriptMemoryStats(i.get<JSScript>(), stats);
 
     if (compartment->types.allocationSiteTable)
         stats->tables += compartment->types.allocationSiteTable->allocatedSize();
 
     if (compartment->types.arrayTypeTable)
         stats->tables += compartment->types.arrayTypeTable->allocatedSize();
 
     if (compartment->types.objectTypeTable) {
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -1211,33 +1211,16 @@ TypeObject::setFlagsFromKey(JSContext *c
               | OBJECT_FLAG_NON_PACKED_ARRAY;
         break;
     }
 
     if (!hasAllFlags(flags))
         setFlags(cx, flags);
 }
 
-class AutoTypeRooter : private AutoGCRooter {
-  public:
-    AutoTypeRooter(JSContext *cx, TypeObject *type
-                   JS_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, TYPE), type(type)
-    {
-        JS_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    friend void AutoGCRooter::trace(JSTracer *trc);
-    friend void MarkRuntime(JSTracer *trc);
-
-  private:
-    TypeObject *type;
-    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
 } } /* namespace js::types */
 
 inline bool
 JSScript::isAboutToBeFinalized(JSContext *cx)
 {
     return isCachedEval ||
         (u.object && IsAboutToBeFinalized(cx, u.object)) ||
         (hasFunction && IsAboutToBeFinalized(cx, function()));
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -909,17 +909,16 @@ ExecuteKernel(JSContext *cx, JSScript *s
 
     if (script->isEmpty()) {
         if (result)
             result->setUndefined();
         return true;
     }
 
     LeaveTrace(cx);
-    AutoScriptRooter root(cx, script);
 
     ExecuteFrameGuard efg;
     if (!cx->stack.pushExecuteFrame(cx, script, thisv, scopeChain, type, evalInFrame, &efg))
         return false;
 
     /* Give strict mode eval its own fresh lexical environment. */
     StackFrame *fp = efg.fp();
     if (fp->isStrictEvalFrame() && !CreateEvalCallObject(cx, fp))
@@ -1851,24 +1850,23 @@ Interpret(JSContext *cx, StackFrame *ent
 
 #define ENABLE_INTERRUPTS() (interruptEnabler.enableInterrupts())
 
 #define LOAD_ATOM(PCOFF, atom)                                                \
     JS_BEGIN_MACRO                                                            \
         JS_ASSERT(regs.fp()->hasImacropc()                                    \
                   ? atoms == rt->atomState.commonAtomsStart() &&              \
                     GET_INDEX(regs.pc + PCOFF) < js_common_atom_count         \
-                  : (size_t)(atoms - script->atomMap.vector) <                \
-                    (size_t)(script->atomMap.length -                         \
-                             GET_INDEX(regs.pc + PCOFF)));                    \
+                  : (size_t)(atoms - script->atoms) <                         \
+                    (size_t)(script->natoms - GET_INDEX(regs.pc + PCOFF)));   \
         atom = atoms[GET_INDEX(regs.pc + PCOFF)];                             \
     JS_END_MACRO
 
 #define GET_FULL_INDEX(PCOFF)                                                 \
-    (atoms - script->atomMap.vector + GET_INDEX(regs.pc + (PCOFF)))
+    (atoms - script->atoms + GET_INDEX(regs.pc + (PCOFF)))
 
 #define LOAD_OBJECT(PCOFF, obj)                                               \
     (obj = script->getObject(GET_FULL_INDEX(PCOFF)))
 
 #define LOAD_FUNCTION(PCOFF)                                                  \
     (fun = script->getFunction(GET_FULL_INDEX(PCOFF)))
 
 #define LOAD_DOUBLE(PCOFF, dbl)                                               \
@@ -2030,17 +2028,17 @@ Interpret(JSContext *cx, StackFrame *ent
 
     /*
      * Initialize the index segment register used by LOAD_ATOM and
      * GET_FULL_INDEX macros below. As a register we use a pointer based on
      * the atom map to turn frequently executed LOAD_ATOM into simple array
      * access. For less frequent object and regexp loads we have to recover
      * the segment from atoms pointer first.
      */
-    JSAtom **atoms = script->atomMap.vector;
+    JSAtom **atoms = script->atoms;
 
 #if JS_HAS_GENERATORS
     if (JS_UNLIKELY(regs.fp()->isGeneratorFrame())) {
         JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);
         JS_ASSERT((size_t) (regs.sp - regs.fp()->base()) <= StackDepth(script));
 
         /*
          * To support generator_throw and to catch ignored exceptions,
@@ -2438,17 +2436,17 @@ BEGIN_CASE(JSOP_STOP)
         JS_ASSERT(op == JSOP_STOP);
         JS_ASSERT((uintN)(regs.sp - regs.fp()->slots()) <= script->nslots);
         jsbytecode *imacpc = regs.fp()->imacropc();
         regs.pc = imacpc + js_CodeSpec[*imacpc].length;
         if (js_CodeSpec[*imacpc].format & JOF_DECOMPOSE)
             regs.pc += GetDecomposeLength(imacpc, js_CodeSpec[*imacpc].length);
         regs.fp()->clearImacropc();
         LEAVE_ON_SAFE_POINT();
-        atoms = script->atomMap.vector;
+        atoms = script->atoms;
         op = JSOp(*regs.pc);
         DO_OP();
     }
 #endif
 
     interpReturnOK = true;
     if (entryFrame != regs.fp())
   inline_return:
@@ -4354,31 +4352,31 @@ BEGIN_CASE(JSOP_INT8)
 END_CASE(JSOP_INT8)
 
 BEGIN_CASE(JSOP_INT32)
     PUSH_INT32(GET_INT32(regs.pc));
 END_CASE(JSOP_INT32)
 
 BEGIN_CASE(JSOP_INDEXBASE)
     /*
-     * Here atoms can exceed script->atomMap.length as we use atoms as a
+     * Here atoms can exceed script->natoms as we use atoms as a
      * segment register for object literals as well.
      */
     atoms += GET_INDEXBASE(regs.pc);
 END_CASE(JSOP_INDEXBASE)
 
 BEGIN_CASE(JSOP_INDEXBASE1)
 BEGIN_CASE(JSOP_INDEXBASE2)
 BEGIN_CASE(JSOP_INDEXBASE3)
     atoms += (op - JSOP_INDEXBASE1 + 1) << 16;
 END_CASE(JSOP_INDEXBASE3)
 
 BEGIN_CASE(JSOP_RESETBASE0)
 BEGIN_CASE(JSOP_RESETBASE)
-    atoms = script->atomMap.vector;
+    atoms = script->atoms;
 END_CASE(JSOP_RESETBASE)
 
 BEGIN_CASE(JSOP_DOUBLE)
 {
     JS_ASSERT(!regs.fp()->hasImacropc());
     double dbl;
     LOAD_DOUBLE(0, dbl);
     PUSH_DOUBLE(dbl);
@@ -4530,17 +4528,17 @@ BEGIN_CASE(JSOP_LOOKUPSWITCH)
     off = JUMP_OFFSET_LEN;
 
   do_lookup_switch:
     /*
      * JSOP_LOOKUPSWITCH and JSOP_LOOKUPSWITCHX are never used if any atom
      * index in it would exceed 64K limit.
      */
     JS_ASSERT(!regs.fp()->hasImacropc());
-    JS_ASSERT(atoms == script->atomMap.vector);
+    JS_ASSERT(atoms == script->atoms);
     jsbytecode *pc2 = regs.pc;
 
     Value lval = regs.sp[-1];
     regs.sp--;
 
     if (!lval.isPrimitive())
         goto end_lookup_switch;
 
@@ -5494,26 +5492,26 @@ BEGIN_CASE(JSOP_SHARPINIT)
 }
 END_CASE(JSOP_SHARPINIT)
 
 #endif /* JS_HAS_SHARP_VARS */
 
 {
 BEGIN_CASE(JSOP_GOSUB)
     PUSH_BOOLEAN(false);
-    jsint i = (regs.pc - script->main) + JSOP_GOSUB_LENGTH;
+    jsint i = (regs.pc - script->code) + JSOP_GOSUB_LENGTH;
     PUSH_INT32(i);
     len = GET_JUMP_OFFSET(regs.pc);
 END_VARLEN_CASE
 }
 
 {
 BEGIN_CASE(JSOP_GOSUBX)
     PUSH_BOOLEAN(false);
-    jsint i = (regs.pc - script->main) + JSOP_GOSUBX_LENGTH;
+    jsint i = (regs.pc - script->code) + JSOP_GOSUBX_LENGTH;
     len = GET_JUMPX_OFFSET(regs.pc);
     PUSH_INT32(i);
 END_VARLEN_CASE
 }
 
 {
 BEGIN_CASE(JSOP_RETSUB)
     /* Pop [exception or hole, retsub pc-index]. */
@@ -5528,17 +5526,17 @@ BEGIN_CASE(JSOP_RETSUB)
          * be necessary, but it seems clearer.  And it points out a FIXME:
          * 350509, due to Igor Bukanov.
          */
         cx->setPendingException(rval);
         goto error;
     }
     JS_ASSERT(rval.isInt32());
     len = rval.toInt32();
-    regs.pc = script->main;
+    regs.pc = script->code;
 END_VARLEN_CASE
 }
 
 BEGIN_CASE(JSOP_EXCEPTION)
     PUSH_COPY(cx->getPendingException());
     cx->clearPendingException();
 #if defined(JS_TRACER) && defined(JS_METHODJIT)
     if (interpMode == JSINTERP_PROFILE) {
@@ -6157,17 +6155,17 @@ END_CASE(JSOP_ARRAYPUSH)
         /* This is an error, not a catchable exception, quit the frame ASAP. */
         interpReturnOK = JS_FALSE;
     } else {
         JSThrowHook handler;
         JSTryNote *tn, *tnlimit;
         uint32 offset;
 
         /* Restore atoms local in case we will resume. */
-        atoms = script->atomMap.vector;
+        atoms = script->atoms;
 
         /* Call debugger throw hook if set. */
         if (cx->debugHooks->throwHook || !cx->compartment->getDebuggees().empty()) {
             Value rval;
             JSTrapStatus st = Debugger::onExceptionUnwind(cx, &rval);
             if (st == JSTRAP_CONTINUE) {
                 handler = cx->debugHooks->throwHook;
                 if (handler)
@@ -6192,17 +6190,17 @@ END_CASE(JSOP_ARRAYPUSH)
         }
 
         /*
          * Look for a try block in script that can catch this exception.
          */
         if (!JSScript::isValidOffset(script->trynotesOffset))
             goto no_catch;
 
-        offset = (uint32)(regs.pc - script->main);
+        offset = (uint32)(regs.pc - script->main());
         tn = script->trynotes()->vector;
         tnlimit = tn + script->trynotes()->length;
         do {
             if (offset - tn->start >= tn->length)
                 continue;
 
             /*
              * We have a note that covers the exception pc but we must check
@@ -6226,17 +6224,17 @@ END_CASE(JSOP_ARRAYPUSH)
             if (tn->stackDepth > regs.sp - regs.fp()->base())
                 continue;
 
             /*
              * Set pc to the first bytecode after the the try note to point
              * to the beginning of catch or finally or to [enditer] closing
              * the for-in loop.
              */
-            regs.pc = (script)->main + tn->start + tn->length;
+            regs.pc = (script)->main() + tn->start + tn->length;
 
             JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE);
             JS_ASSERT(regs.sp == regs.fp()->base() + tn->stackDepth);
             if (!ok) {
                 /*
                  * Restart the handler search with updated pc and stack depth
                  * to properly notify the debugger.
                  */
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -979,17 +979,17 @@ EvalCacheHash(JSContext *cx, JSLinearStr
     if (n > 100)
         n = 100;
     uint32 h;
     for (h = 0; n; s++, n--)
         h = JS_ROTATE_LEFT32(h, 4) ^ *s;
 
     h *= JS_GOLDEN_RATIO;
     h >>= 32 - JS_EVAL_CACHE_SHIFT;
-    return &JS_SCRIPTS_TO_GC(cx)[h];
+    return &cx->compartment->evalCache[h];
 }
 
 static JS_ALWAYS_INLINE JSScript *
 EvalCacheLookup(JSContext *cx, JSLinearString *str, StackFrame *caller, uintN staticLevel,
                 JSPrincipals *principals, JSObject &scopeobj, JSScript **bucket)
 {
     /*
      * Cache local eval scripts indexed by source qualified by scope.
@@ -1027,17 +1027,17 @@ EvalCacheLookup(JSContext *cx, JSLinearS
              */
             JSFunction *fun = script->getCallerFunction();
 
             if (fun == caller->fun()) {
                 /*
                  * Get the source string passed for safekeeping in the
                  * atom map by the prior eval to Compiler::compileScript.
                  */
-                JSAtom *src = script->atomMap.vector[0];
+                JSAtom *src = script->atoms[0];
 
                 if (src == str || EqualStrings(src, str)) {
                     /*
                      * Source matches, qualify by comparing scopeobj to the
                      * COMPILE_N_GO-memoized parent of the first literal
                      * function or regexp object if any. If none, then this
                      * script has no compiled-in dependencies on the prior
                      * eval's scopeobj.
@@ -1051,41 +1051,41 @@ EvalCacheLookup(JSContext *cx, JSLinearS
                             i = 0;
                         } else {
                             i = -1;
                         }
                     }
                     if (i < 0 ||
                         objarray->vector[i]->getParent() == &scopeobj) {
                         JS_ASSERT(staticLevel == script->staticLevel);
-                        *scriptp = script->u.nextToGC;
-                        script->u.nextToGC = NULL;
+                        *scriptp = script->u.evalHashLink;
+                        script->u.evalHashLink = NULL;
                         return script;
                     }
                 }
             }
         }
 
         if (++count == EVAL_CACHE_CHAIN_LIMIT)
             return NULL;
-        scriptp = &script->u.nextToGC;
+        scriptp = &script->u.evalHashLink;
     }
     return NULL;
 }
 
 /*
  * There are two things we want to do with each script executed in EvalKernel:
  *  1. notify jsdbgapi about script creation/destruction
  *  2. add the script to the eval cache when EvalKernel is finished
  *
  * NB: Although the eval cache keeps a script alive wrt to the JS engine, from
  * a jsdbgapi user's perspective, we want each eval() to create and destroy a
  * script. This hides implementation details and means we don't have to deal
  * with calls to JS_GetScriptObject for scripts in the eval cache (currently,
- * script->u.object aliases script->u.nextToGC).
+ * script->u.object aliases script->u.evalHashLink).
  */
 class EvalScriptGuard
 {
     JSContext *cx_;
     JSLinearString *str_;
     JSScript **bucket_;
     JSScript *script_;
 
@@ -1097,21 +1097,18 @@ class EvalScriptGuard
         bucket_ = EvalCacheHash(cx, str);
     }
 
     ~EvalScriptGuard() {
         if (script_) {
             js_CallDestroyScriptHook(cx_, script_);
             script_->isActiveEval = false;
             script_->isCachedEval = true;
-            script_->u.nextToGC = *bucket_;
+            script_->u.evalHashLink = *bucket_;
             *bucket_ = script_;
-#ifdef CHECK_SCRIPT_OWNER
-            script_->owner = NULL;
-#endif
         }
     }
 
     void lookupInEvalCache(StackFrame *caller, uintN staticLevel,
                            JSPrincipals *principals, JSObject &scopeobj) {
         if (JSScript *found = EvalCacheLookup(cx_, str_, caller, staticLevel,
                                               principals, scopeobj, bucket_)) {
             js_CallNewScriptHook(cx_, found, NULL);
@@ -5573,22 +5570,18 @@ js_NativeGetInline(JSContext *cx, JSObje
         return true;
 
     if (JS_UNLIKELY(shape->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER)) {
         JS_ASSERT(shape->methodObject() == vp->toObject());
         return true;
     }
 
     sample = cx->runtime->propertyRemovals;
-    {
-        AutoShapeRooter tvr(cx, shape);
-        AutoObjectRooter tvr2(cx, pobj);
-        if (!shape->get(cx, receiver, obj, pobj, vp))
-            return false;
-    }
+    if (!shape->get(cx, receiver, obj, pobj, vp))
+        return false;
 
     if (pobj->containsSlot(slot) &&
         (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
          pobj->nativeContains(*shape))) {
         if (!pobj->methodWriteBarrier(cx, *shape, *vp))
             return false;
         pobj->nativeSetSlot(slot, *vp);
     }
@@ -5641,24 +5634,21 @@ js_NativeSet(JSContext *cx, JSObject *ob
          * not writable, so attempting to set such a property should do nothing
          * or throw if we're in strict mode.
          */
         if (!shape->hasGetterValue() && shape->hasDefaultSetter())
             return js_ReportGetterOnlyAssignment(cx);
     }
 
     sample = cx->runtime->propertyRemovals;
-    {
-        AutoShapeRooter tvr(cx, shape);
-        if (!shape->set(cx, obj, strict, vp))
-            return false;
-
-        JS_ASSERT_IF(!obj->inDictionaryMode(), shape->slot == slot);
-        slot = shape->slot;
-    }
+    if (!shape->set(cx, obj, strict, vp))
+        return false;
+    
+    JS_ASSERT_IF(!obj->inDictionaryMode(), shape->slot == slot);
+    slot = shape->slot;
 
     if (obj->containsSlot(slot) &&
         (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
          obj->nativeContains(*shape))) {
         if (!added) {
             AbortRecordingIfUnexpectedGlobalWrite(cx, obj, slot);
             if (!obj->methodWriteBarrier(cx, *shape, *vp))
                 return false;
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1127,16 +1127,19 @@ struct JSObject : js::gc::Cell {
     inline JSFunction *getFunctionPrivate() const;
 
     inline js::Value *getFlatClosureUpvars() const;
     inline js::Value getFlatClosureUpvar(uint32 i) const;
     inline const js::Value &getFlatClosureUpvar(uint32 i);
     inline void setFlatClosureUpvar(uint32 i, const js::Value &v);
     inline void setFlatClosureUpvars(js::Value *upvars);
 
+    /* See comments in fun_finalize. */
+    inline void finalizeUpvarsIfFlatClosure();
+
     inline bool hasMethodObj(const JSObject& obj) const;
     inline void setMethodObj(JSObject& obj);
 
     inline bool initBoundFunction(JSContext *cx, const js::Value &thisArg,
                                   const js::Value *args, uintN argslen);
 
     inline JSObject *getBoundFunctionTarget() const;
     inline const js::Value &getBoundFunctionThis() const;
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -650,16 +650,47 @@ JSObject::getFlatClosureUpvars() const
 #ifdef DEBUG
     JSFunction *fun = getFunctionPrivate();
     JS_ASSERT(fun->isFlatClosure());
     JS_ASSERT(fun->script()->bindings.countUpvars() == fun->script()->upvars()->length);
 #endif
     return (js::Value *) getFixedSlot(JSSLOT_FLAT_CLOSURE_UPVARS).toPrivate();
 }
 
+inline void
+JSObject::finalizeUpvarsIfFlatClosure()
+{
+    /*
+     * Cloned function objects may be flat closures with upvars to free.
+     *
+     * We do not record in the closure objects any flags. Rather we use flags
+     * stored in the compiled JSFunction that we get via getFunctionPrivate()
+     * to distinguish between closure types. Then during finalization we must
+     * ensure that the compiled JSFunction always finalized after the closures
+     * so we can safely access it here. Currently the GC ensures that through
+     * finalizing JSFunction instances after finalizing any other objects even
+     * during the background finalization.
+     *
+     * But we must not access JSScript here that is stored in JSFunction. The
+     * script can be finalized before the function or closure instances. So we
+     * just check if JSSLOT_FLAT_CLOSURE_UPVARS holds a private value encoded
+     * as a double. We must also ignore newborn closures that do not have the
+     * private pointer set.
+     *
+     * FIXME bug 648320 - allocate upvars on the GC heap to avoid doing it
+     * here explicitly.
+     */
+    JSFunction *fun = getFunctionPrivate();
+    if (fun && fun != this && fun->isFlatClosure()) {
+        const js::Value &v = getSlot(JSSLOT_FLAT_CLOSURE_UPVARS);
+        if (v.isDouble())
+            js::Foreground::free_(v.toPrivate());
+    }
+}
+
 inline js::Value
 JSObject::getFlatClosureUpvar(uint32 i) const
 {
     JS_ASSERT(i < getFunctionPrivate()->script()->bindings.countUpvars());
     return getFlatClosureUpvars()[i];
 }
 
 inline const js::Value &
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -253,33 +253,31 @@ public:
           , assertionBefore(false)
 #endif
     {
         jsbytecode *newCode = js_UntrapScriptCode(cx, script);
         if (newCode == script->code) {
             // No change needed
             newPC = origPC;
         } else {
-            script->main += newCode - script->code;
             *pc = newPC = origPC + (newCode - script->code);
             script->code = newCode;
 #ifdef DEBUG
             assertionBefore = cx->stackIterAssertionEnabled;
             cx->stackIterAssertionEnabled = false;
 #endif
         }
     }
     ~AutoScriptUntrapper()
     {
         ptrdiff_t delta = newPC - origPC;
         if (delta) {
             jsbytecode *oldCode = script->code - delta;
             cx->free_(script->code);
             script->code = oldCode;
-            script->main -= delta;
 #ifdef DEBUG
             cx->stackIterAssertionEnabled = assertionBefore;
 #endif
         }
     }
 };
 
 #ifdef DEBUG
@@ -308,17 +306,17 @@ js_DisassembleAtPC(JSContext *cx, JSScri
     }
     if (lines)
         SprintCString(sp, "----");
     SprintCString(sp, "  --\n");
 
     next = script->code;
     end = next + script->length;
     while (next < end) {
-        if (next == script->main)
+        if (next == script->main())
             SprintCString(sp, "main:\n");
         if (pc != NULL) {
             if (pc == next)
                 SprintCString(sp, "--> ");
             else
                 SprintCString(sp, "    ");
         }
         len = js_Disassemble1(cx, script, next,
@@ -2018,18 +2016,18 @@ Decompile(SprintStack *ss, jsbytecode *p
 
 #define LOAD_REGEXP(PCOFF)                                                    \
     GET_REGEXP_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
 
 #define GET_SOURCE_NOTE_ATOM(sn, atom)                                        \
     JS_BEGIN_MACRO                                                            \
         jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0);        \
                                                                               \
-        LOCAL_ASSERT(atomIndex_ < jp->script->atomMap.length);                \
-        (atom) = jp->script->atomMap.vector[atomIndex_];                      \
+        LOCAL_ASSERT(atomIndex_ < jp->script->natoms);                        \
+        (atom) = jp->script->atoms[atomIndex_];                               \
     JS_END_MACRO
 
 /*
  * Get atom from jp->script's atom map, quote/escape its string appropriately
  * into rval, and select fmt from the quoted and unquoted alternatives.
  */
 #define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval)                              \
     JS_BEGIN_MACRO                                                            \
@@ -4105,18 +4103,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                     jp->script = inner;
                     jp->fun = fun;
                     jp->localNames = innerLocalNames;
 
                     /*
                      * Decompile only the main bytecode, to avoid tripping over
                      * new prolog ops that have stack effects.
                      */
-                    ok = Decompile(&ss2, inner->main,
-                                   inner->length - (inner->main - inner->code),
+                    ok = Decompile(&ss2, inner->main(), inner->length - inner->mainOffset,
                                    JSOP_NOP)
                          != NULL;
                     jp->script = outer;
                     jp->fun = outerfun;
                     jp->localNames = outerLocalNames;
                     if (!ok) {
                         JS_ARENA_RELEASE(&cx->tempPool, mark);
                         return NULL;
@@ -4992,17 +4989,17 @@ js_DecompileFunction(JSPrinter *jp)
     } else {
         JSScript *script = fun->script();
 #if JS_HAS_DESTRUCTURING
         SprintStack ss;
         void *mark;
 #endif
 
         /* Print the parameters. */
-        pc = script->main;
+        pc = script->main();
         AutoScriptUntrapper untrapper(jp->sprinter.context, script, &pc);
         endpc = pc + script->length;
         ok = JS_TRUE;
 
 #if JS_HAS_DESTRUCTURING
         ss.printer = NULL;
         jp->script = script;
         mark = JS_ARENA_MARK(&jp->sprinter.context->tempPool);
@@ -5101,17 +5098,17 @@ js_DecompileValueGenerator(JSContext *cx
     if (!cx->hasfp() || !cx->fp()->isScriptFrame())
         goto do_fallback;
 
     fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL);
     script = fp->script();
     pc = fp->hasImacropc() ? fp->imacropc() : cx->regs().pc;
     JS_ASSERT(script->code <= pc && pc < script->code + script->length);
 
-    if (pc < script->main)
+    if (pc < script->main())
         goto do_fallback;
     
     if (spindex != JSDVG_IGNORE_STACK) {
         jsbytecode **pcstack;
 
         /*
          * Prepare computing pcstack containing pointers to opcodes that
          * populated interpreter's stack with its current content.
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -966,17 +966,17 @@ Compiler::compileScript(JSContext *cx, J
 
 #ifdef DEBUG
     bool savedCallerFun;
     savedCallerFun = false;
 #endif
     if (tcflags & TCF_COMPILE_N_GO) {
         if (source) {
             /*
-             * Save eval program source in script->atomMap.vector[0] for the
+             * Save eval program source in script->atoms[0] for the
              * eval cache (see EvalCacheLookup in jsobj.cpp).
              */
             JSAtom *atom = js_AtomizeString(cx, source);
             jsatomid _;
             if (!atom || !cg.makeAtomIndex(atom, &_))
                 goto out;
         }
 
@@ -1109,35 +1109,27 @@ Compiler::compileScript(JSContext *cx, J
     JS_ASSERT(cg.version() == version);
 
     script = JSScript::NewScriptFromCG(cx, &cg);
     if (!script)
         goto out;
 
     JS_ASSERT(script->savedCallerFun == savedCallerFun);
 
-    {
-        AutoScriptRooter root(cx, script);
-        if (!defineGlobals(cx, globalScope, script))
-            goto late_error;
-    }
+    if (!defineGlobals(cx, globalScope, script))
+        script = NULL;
 
   out:
     JS_FinishArenaPool(&codePool);
     JS_FinishArenaPool(&notePool);
     Probes::compileScriptEnd(cx, script, filename, lineno);
     return script;
 
   too_many_slots:
     parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
-    /* Fall through. */
-
-  late_error:
-    if (script && !script->u.object)
-        js_DestroyScript(cx, script, 7);
     script = NULL;
     goto out;
 }
 
 bool
 Compiler::defineGlobals(JSContext *cx, GlobalScope &globalScope, JSScript *script)
 {
     if (!globalScope.defs.length())
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -87,17 +87,16 @@ typedef struct JSParseNode          JSPa
 typedef struct JSProperty           JSProperty;
 typedef struct JSScript             JSScript;
 typedef struct JSSharpObjectMap     JSSharpObjectMap;
 typedef struct JSThread             JSThread;
 typedef struct JSTreeContext        JSTreeContext;
 typedef struct JSTryNote            JSTryNote;
 
 /* Friend "Advanced API" typedefs. */
-typedef struct JSAtomMap            JSAtomMap;
 typedef struct JSAtomState          JSAtomState;
 typedef struct JSCodeSpec           JSCodeSpec;
 typedef struct JSPrinter            JSPrinter;
 typedef struct JSStackHeader        JSStackHeader;
 typedef struct JSSubString          JSSubString;
 typedef struct JSNativeTraceInfo    JSNativeTraceInfo;
 typedef struct JSSpecializedNative  JSSpecializedNative;
 typedef struct JSXML                JSXML;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -286,40 +286,40 @@ Bindings::makeImmutable()
 
 void
 Bindings::trace(JSTracer *trc)
 {
     if (lastBinding)
         MarkShape(trc, lastBinding, "shape");
 }
 
-} /* namespace js */
+#ifdef JS_CRASH_DIAGNOSTICS
 
-static void
+void
 CheckScript(JSScript *script, JSScript *prev)
 {
-#ifdef JS_CRASH_DIAGNOSTICS
-    if (script->cookie1 != JS_SCRIPT_COOKIE || script->cookie2 != JS_SCRIPT_COOKIE) {
+    if (script->cookie1[0] != JS_SCRIPT_COOKIE || script->cookie2[0] != JS_SCRIPT_COOKIE) {
         crash::StackBuffer<sizeof(JSScript), 0x87> buf1(script);
         crash::StackBuffer<sizeof(JSScript), 0x88> buf2(prev);
         JS_OPT_ASSERT(false);
     }
-#endif
 }
 
-static void
+void
 CheckScriptOwner(JSScript *script, JSObject *owner)
 {
-#ifdef JS_CRASH_DIAGNOSTICS
     JS_OPT_ASSERT(script->ownerObject == owner);
     if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT)
-        JS_OPT_ASSERT(script->compartment == owner->compartment());
-#endif
+        JS_OPT_ASSERT(script->compartment() == owner->compartment());
 }
 
+#endif /* JS_CRASH_DIAGNOSTICS */
+
+} /* namespace js */
+
 #if JS_HAS_XDR
 
 enum ScriptBits {
     NoScriptRval,
     SavedCallerFun,
     HasSharps,
     StrictModeCode,
     UsesEval,
@@ -377,17 +377,16 @@ js_XDRScript(JSXDRState *xdr, JSScript *
         JS_ASSERT((paddingUpvars >> 16) == 0);
         nupvars = paddingUpvars & 0xFFFF;
     }
     JS_ASSERT(nargs != Bindings::BINDING_COUNT_LIMIT);
     JS_ASSERT(nvars != Bindings::BINDING_COUNT_LIMIT);
     JS_ASSERT(nupvars != Bindings::BINDING_COUNT_LIMIT);
 
     Bindings bindings(cx);
-    AutoBindingsRooter rooter(cx, bindings);
     uint32 nameCount = nargs + nvars + nupvars;
     if (nameCount > 0) {
         struct AutoMark {
           JSArenaPool * const pool;
           void * const mark;
           AutoMark(JSArenaPool *pool) : pool(pool), mark(JS_ARENA_MARK(pool)) { }
           ~AutoMark() {
             JS_ARENA_RELEASE(pool, mark);
@@ -467,23 +466,23 @@ js_XDRScript(JSXDRState *xdr, JSScript *
     }
 
     if (xdr->mode == JSXDR_ENCODE)
         length = script->length;
     if (!JS_XDRUint32(xdr, &length))
         return JS_FALSE;
 
     if (xdr->mode == JSXDR_ENCODE) {
-        prologLength = script->main - script->code;
+        prologLength = script->mainOffset;
         JS_ASSERT(script->getVersion() != JSVERSION_UNKNOWN);
         version = (uint32)script->getVersion() | (script->nfixed << 16);
         lineno = (uint32)script->lineno;
         nslots = (uint32)script->nslots;
         nslots = (uint32)((script->staticLevel << 16) | script->nslots);
-        natoms = (uint32)script->atomMap.length;
+        natoms = script->natoms;
 
         notes = script->notes();
         nsrcnotes = script->numNotes();
 
         if (JSScript::isValidOffset(script->objectsOffset))
             nobjects = script->objects()->length;
         if (JSScript::isValidOffset(script->upvarsOffset))
             JS_ASSERT(script->bindings.countUpvars() == script->upvars()->length);
@@ -539,40 +538,37 @@ js_XDRScript(JSXDRState *xdr, JSScript *
         return JS_FALSE;
     if (!JS_XDRUint32(xdr, &encodedClosedCount))
         return JS_FALSE;
     if (!JS_XDRUint32(xdr, &nTypeSets))
         return JS_FALSE;
     if (!JS_XDRUint32(xdr, &scriptBits))
         return JS_FALSE;
 
-    AutoScriptRooter tvr(cx, NULL);
-
     if (xdr->mode == JSXDR_DECODE) {
         nClosedArgs = encodedClosedCount >> 16;
         nClosedVars = encodedClosedCount & 0xFFFF;
 
         /* Note: version is packed into the 32b space with another 16b value. */
         JSVersion version_ = JSVersion(version & JS_BITMASK(16));
         JS_ASSERT((version_ & VersionFlags::FULL_MASK) == uintN(version_));
         script = JSScript::NewScript(cx, length, nsrcnotes, natoms, nobjects, nupvars,
                                      nregexps, ntrynotes, nconsts, 0, nClosedArgs,
                                      nClosedVars, nTypeSets, version_);
         if (!script)
             return JS_FALSE;
 
         script->bindings.transfer(cx, &bindings);
-
-        script->main += prologLength;
+        JS_ASSERT(!script->mainOffset);
+        script->mainOffset = prologLength;
         script->nfixed = uint16(version >> 16);
 
         /* If we know nsrcnotes, we allocated space for notes in script. */
         notes = script->notes();
         *scriptp = script;
-        tvr.setScript(script);
 
         if (scriptBits & (1 << NoScriptRval))
             script->noScriptRval = true;
         if (scriptBits & (1 << SavedCallerFun))
             script->savedCallerFun = true;
         if (scriptBits & (1 << HasSharps))
             script->hasSharps = true;
         if (scriptBits & (1 << StrictModeCode))
@@ -652,17 +648,17 @@ js_XDRScript(JSXDRState *xdr, JSScript *
 
     if (xdr->mode == JSXDR_DECODE) {
         script->lineno = (uintN)lineno;
         script->nslots = (uint16)nslots;
         script->staticLevel = (uint16)(nslots >> 16);
     }
 
     for (i = 0; i != natoms; ++i) {
-        if (!js_XDRAtom(xdr, &script->atomMap.vector[i]))
+        if (!js_XDRAtom(xdr, &script->atoms[i]))
             goto error;
     }
 
     /*
      * Here looping from 0-to-length to xdr objects is essential. It ensures
      * that block objects from the script->objects array will be written and
      * restored in the outer-to-inner order. js_XDRBlockObject relies on this
      * to restore the parent chain.
@@ -739,20 +735,18 @@ js_XDRScript(JSXDRState *xdr, JSScript *
         if (!JS_XDRValue(xdr, Jsvalify(&script->consts()->vector[i])))
             goto error;
     }
 
     xdr->script = oldscript;
     return JS_TRUE;
 
   error:
-    if (xdr->mode == JSXDR_DECODE) {
-        js_DestroyScript(cx, script, 1);
+    if (xdr->mode == JSXDR_DECODE)
         *scriptp = NULL;
-    }
     xdr->script = oldscript;
     return JS_FALSE;
 }
 
 #endif /* JS_HAS_XDR */
 
 bool
 JSPCCounters::init(JSContext *cx, size_t numBytecodes)
@@ -770,43 +764,37 @@ JSPCCounters::destroy(JSContext *cx)
 {
     if (counts) {
         cx->free_(counts);
         counts = NULL;
     }
 }
 
 static void
-script_finalize(JSContext *cx, JSObject *obj)
-{
-    JSScript *script = (JSScript *) obj->getPrivate();
-    if (script)
-        js_DestroyScriptFromGC(cx, script, obj);
-}
-
-static void
 script_trace(JSTracer *trc, JSObject *obj)
 {
     JSScript *script = (JSScript *) obj->getPrivate();
-    if (script)
-        js_TraceScript(trc, script, obj);
+    if (script) {
+        CheckScriptOwner(script, obj);
+        MarkScript(trc, script, "script");
+    }
 }
 
 Class js_ScriptClass = {
     "Script",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
     PropertyStub,         /* addProperty */
     PropertyStub,         /* delProperty */
     PropertyStub,         /* getProperty */
     StrictPropertyStub,   /* setProperty */
     EnumerateStub,
     ResolveStub,
     ConvertStub,
-    script_finalize,
+    NULL,                 /* finalize */
     NULL,                 /* reserved0   */
     NULL,                 /* checkAccess */
     NULL,                 /* call        */
     NULL,                 /* construct   */
     NULL,                 /* xdrObject   */
     NULL,                 /* hasInstance */
     script_trace
 };
@@ -872,18 +860,18 @@ js_SweepScriptFilenames(JSCompartment *c
  *
  * JSScript
  * JSObjectArray    script objects' descriptor if JSScript.objectsOffset != 0,
  *                    use script->objects() to access it.
  * JSObjectArray    script regexps' descriptor if JSScript.regexpsOffset != 0,
  *                    use script->regexps() to access it.
  * JSTryNoteArray   script try notes' descriptor if JSScript.tryNotesOffset
  *                    != 0, use script->trynotes() to access it.
- * JSAtom *a[]      array of JSScript.atomMap.length atoms pointed by
- *                    JSScript.atomMap.vector if any.
+ * JSAtom *a[]      array of JSScript.natoms atoms pointed by
+ *                    JSScript.atoms if any.
  * JSObject *o[]    array of script->objects()->length objects if any
  *                    pointed by script->objects()->vector.
  * JSObject *r[]    array of script->regexps()->length regexps if any
  *                    pointed by script->regexps()->vector.
  * JSTryNote t[]    array of script->trynotes()->length try notes if any
  *                    pointed by script->trynotes()->vector.
  * jsbytecode b[]   script bytecode pointed by JSScript.code.
  * jssrcnote  s[]   script source notes, use script->notes() to access it
@@ -920,211 +908,208 @@ JS_STATIC_ASSERT(sizeof(JSObjectArray) +
 JS_STATIC_ASSERT(JSScript::INVALID_OFFSET <= 255);
 
 JSScript *
 JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
                     uint32 nobjects, uint32 nupvars, uint32 nregexps,
                     uint32 ntrynotes, uint32 nconsts, uint32 nglobals,
                     uint16 nClosedArgs, uint16 nClosedVars, uint32 nTypeSets, JSVersion version)
 {
-    size_t size, vectorSize;
-    JSScript *script;
-    uint8 *cursor;
-    unsigned constPadding = 0;
-
-    uint32 totalClosed = nClosedArgs + nClosedVars;
-
-    size = sizeof(JSScript) +
-           sizeof(JSAtom *) * natoms;
-
+    size_t size = sizeof(JSAtom *) * natoms;
     if (nobjects != 0)
         size += sizeof(JSObjectArray) + nobjects * sizeof(JSObject *);
     if (nupvars != 0)
         size += sizeof(JSUpvarArray) + nupvars * sizeof(uint32);
     if (nregexps != 0)
         size += sizeof(JSObjectArray) + nregexps * sizeof(JSObject *);
     if (ntrynotes != 0)
         size += sizeof(JSTryNoteArray) + ntrynotes * sizeof(JSTryNote);
     if (nglobals != 0)
         size += sizeof(GlobalSlotArray) + nglobals * sizeof(GlobalSlotArray::Entry);
+    uint32 totalClosed = nClosedArgs + nClosedVars;
     if (totalClosed != 0)
         size += totalClosed * sizeof(uint32);
 
-    if (nconsts != 0) {
-        size += sizeof(JSConstArray);
+    /*
+     * To esnure jsval alignment for the const array we place it immediately
+     * after JSSomethingArray structs as their sizes all divide sizeof(jsval).
+     * This works as long as the data itself is allocated with proper
+     * alignment which we ensure below.
+     */
+    JS_STATIC_ASSERT(sizeof(JSObjectArray) % sizeof(jsval) == 0);
+    JS_STATIC_ASSERT(sizeof(JSUpvarArray) % sizeof(jsval) == 0);
+    JS_STATIC_ASSERT(sizeof(JSTryNoteArray) % sizeof(jsval) == 0);
+    JS_STATIC_ASSERT(sizeof(GlobalSlotArray) % sizeof(jsval) == 0);
+    JS_STATIC_ASSERT(sizeof(JSConstArray) % sizeof(jsval) == 0);
+    if (nconsts != 0)
+        size += sizeof(JSConstArray) + nconsts * sizeof(Value);
+
+    size += length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote);
+
+    uint8 *data = NULL;
+#if JS_SCRIPT_INLINE_DATA_LIMIT
+    if (size <= JS_SCRIPT_INLINE_DATA_LIMIT) {
         /*
-         * Calculate padding assuming that consts go after the other arrays,
-         * but before the bytecode and source notes.
+         * Check that if inlineData is big enough to store const values, we
+         * can do that without any special alignment requirements given that
+         * the script as a GC thing is always aligned on Cell::CellSize.
          */
-        constPadding = (8 - (size % 8)) % 8;
-        size += constPadding + nconsts * sizeof(Value);
+        JS_STATIC_ASSERT(Cell::CellSize % sizeof(Value) == 0);
+        JS_STATIC_ASSERT(JS_SCRIPT_INLINE_DATA_LIMIT < sizeof(Value) ||
+                         offsetof(JSScript, inlineData) % sizeof(Value) == 0);
+    } else
+#endif
+    {
+        /*
+         * We assume that calloc aligns on sizeof(Value) if the size we ask to
+         * allocate divides sizeof(Value).
+         */
+        JS_STATIC_ASSERT(sizeof(Value) == sizeof(jsdouble));
+        data = static_cast<uint8 *>(cx->calloc_(JS_ROUNDUP(size, sizeof(Value))));
+        if (!data)
+            return NULL;
     }
 
-    size += length * sizeof(jsbytecode) +
-            nsrcnotes * sizeof(jssrcnote);
-
-    script = (JSScript *) cx->malloc_(size);
-    if (!script)
+    JSScript *script = js_NewGCScript(cx);
+    if (!script) {
+        Foreground::free_(data);
         return NULL;
+    }
 
     PodZero(script);
 #ifdef JS_CRASH_DIAGNOSTICS
-    script->cookie1 = script->cookie2 = JS_SCRIPT_COOKIE;
+    script->cookie1[0] = script->cookie2[0] = JS_SCRIPT_COOKIE;
     script->ownerObject = JS_NEW_SCRIPT;
 #endif
+#if JS_SCRIPT_INLINE_DATA_LIMIT
+    if (!data)
+        data = script->inlineData;
+#endif
+    script->data  = data;
     script->length = length;
     script->version = version;
     new (&script->bindings) Bindings(cx);
 
     if (cx->hasRunOption(JSOPTION_PCCOUNT))
         (void) script->pcCounters.init(cx, length);
 
-    uint8 *scriptEnd = reinterpret_cast<uint8 *>(script + 1);
-
-    cursor = scriptEnd;
+    uint8 *cursor = data;
     if (nobjects != 0) {
-        script->objectsOffset = (uint8)(cursor - scriptEnd);
+        script->objectsOffset = (uint8)(cursor - data);
         cursor += sizeof(JSObjectArray);
     } else {
         script->objectsOffset = JSScript::INVALID_OFFSET;
     }
     if (nupvars != 0) {
-        script->upvarsOffset = (uint8)(cursor - scriptEnd);
+        script->upvarsOffset = (uint8)(cursor - data);
         cursor += sizeof(JSUpvarArray);
     } else {
         script->upvarsOffset = JSScript::INVALID_OFFSET;
     }
     if (nregexps != 0) {
-        script->regexpsOffset = (uint8)(cursor - scriptEnd);
+        script->regexpsOffset = (uint8)(cursor - data);
         cursor += sizeof(JSObjectArray);
     } else {
         script->regexpsOffset = JSScript::INVALID_OFFSET;
     }
     if (ntrynotes != 0) {
-        script->trynotesOffset = (uint8)(cursor - scriptEnd);
+        script->trynotesOffset = (uint8)(cursor - data);
         cursor += sizeof(JSTryNoteArray);
     } else {
         script->trynotesOffset = JSScript::INVALID_OFFSET;
     }
     if (nglobals != 0) {
-        script->globalsOffset = (uint8)(cursor - scriptEnd);
+        script->globalsOffset = (uint8)(cursor - data);
         cursor += sizeof(GlobalSlotArray);
     } else {
         script->globalsOffset = JSScript::INVALID_OFFSET;
     }
-    JS_ASSERT(cursor - scriptEnd < 0xFF);
+    JS_ASSERT(cursor - data < 0xFF);
     if (nconsts != 0) {
-        script->constOffset = (uint8)(cursor - scriptEnd);
+        script->constOffset = (uint8)(cursor - data);
         cursor += sizeof(JSConstArray);
     } else {
         script->constOffset = JSScript::INVALID_OFFSET;
     }
 
     JS_STATIC_ASSERT(sizeof(JSObjectArray) +
                      sizeof(JSUpvarArray) +
                      sizeof(JSObjectArray) +
                      sizeof(JSTryNoteArray) +
                      sizeof(GlobalSlotArray) < 0xFF);
 
-    if (natoms != 0) {
-        script->atomMap.length = natoms;
-        script->atomMap.vector = (JSAtom **)cursor;
-        vectorSize = natoms * sizeof(script->atomMap.vector[0]);
 
-        /*
-         * Clear object map's vector so the GC tracing can run when not yet
-         * all atoms are copied to the array.
-         */
-        memset(cursor, 0, vectorSize);
-        cursor += vectorSize;
+    if (nconsts != 0) {
+        JS_ASSERT(reinterpret_cast<jsuword>(cursor) % sizeof(jsval) == 0);
+        script->consts()->length = nconsts;
+        script->consts()->vector = reinterpret_cast<Value *>(cursor);
+        cursor += nconsts * sizeof(script->consts()->vector[0]);
+    }
+
+    if (natoms != 0) {
+        script->natoms = natoms;
+        script->atoms = reinterpret_cast<JSAtom **>(cursor);
+        cursor += natoms * sizeof(script->atoms[0]);
     }
 
     if (nobjects != 0) {
         script->objects()->length = nobjects;
-        script->objects()->vector = (JSObject **)cursor;
-        vectorSize = nobjects * sizeof(script->objects()->vector[0]);
-        memset(cursor, 0, vectorSize);
-        cursor += vectorSize;
+        script->objects()->vector = reinterpret_cast<JSObject **>(cursor);
+        cursor += nobjects * sizeof(script->objects()->vector[0]);
     }
 
     if (nregexps != 0) {
         script->regexps()->length = nregexps;
-        script->regexps()->vector = (JSObject **)cursor;
-        vectorSize = nregexps * sizeof(script->regexps()->vector[0]);
-        memset(cursor, 0, vectorSize);
-        cursor += vectorSize;
+        script->regexps()->vector = reinterpret_cast<JSObject **>(cursor);
+        cursor += nregexps * sizeof(script->regexps()->vector[0]);
     }
 
     if (ntrynotes != 0) {
         script->trynotes()->length = ntrynotes;
-        script->trynotes()->vector = (JSTryNote *)cursor;
-        vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]);
+        script->trynotes()->vector = reinterpret_cast<JSTryNote *>(cursor);
+        size_t vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]);
 #ifdef DEBUG
         memset(cursor, 0, vectorSize);
 #endif
         cursor += vectorSize;
     }
 
     if (nglobals != 0) {
         script->globals()->length = nglobals;
-        script->globals()->vector = (GlobalSlotArray::Entry *)cursor;
-        vectorSize = nglobals * sizeof(script->globals()->vector[0]);
-        cursor += vectorSize;
+        script->globals()->vector = reinterpret_cast<GlobalSlotArray::Entry *>(cursor);
+        cursor += nglobals * sizeof(script->globals()->vector[0]);
     }
 
     if (totalClosed != 0) {
         script->nClosedArgs = nClosedArgs;
         script->nClosedVars = nClosedVars;
-        script->closedSlots = (uint32 *)cursor;
+        script->closedSlots = reinterpret_cast<uint32 *>(cursor);
         cursor += totalClosed * sizeof(uint32);
     }
 
     JS_ASSERT(nTypeSets <= UINT16_MAX);
     script->nTypeSets = uint16(nTypeSets);
 
     /*
      * NB: We allocate the vector of uint32 upvar cookies after all vectors of
      * pointers, to avoid misalignment on 64-bit platforms. See bug 514645.
      */
     if (nupvars != 0) {
         script->upvars()->length = nupvars;
         script->upvars()->vector = reinterpret_cast<UpvarCookie *>(cursor);
-        vectorSize = nupvars * sizeof(script->upvars()->vector[0]);
-        memset(cursor, 0, vectorSize);
-        cursor += vectorSize;
+        cursor += nupvars * sizeof(script->upvars()->vector[0]);
     }
 
-    /* Must go after other arrays; see constPadding definition. */
-    if (nconsts != 0) {
-        cursor += constPadding;
-        script->consts()->length = nconsts;
-        script->consts()->vector = (Value *)cursor;
-        JS_ASSERT((size_t)cursor % sizeof(double) == 0);
-        vectorSize = nconsts * sizeof(script->consts()->vector[0]);
-        memset(cursor, 0, vectorSize);
-        cursor += vectorSize;
-    }
-
-    script->code = script->main = (jsbytecode *)cursor;
-    JS_ASSERT(cursor +
-              length * sizeof(jsbytecode) +
-              nsrcnotes * sizeof(jssrcnote) ==
-              (uint8 *)script + size);
-
-    script->compartment = cx->compartment;
-#ifdef CHECK_SCRIPT_OWNER
-    script->owner = cx->thread();
-#endif
+    script->code = (jsbytecode *)cursor;
+    JS_ASSERT(cursor + length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote) == data + size);
 
 #ifdef DEBUG
     script->id_ = ++cx->compartment->types.scriptCount;
 #endif
 
-    JS_APPEND_LINK(&script->links, &cx->compartment->scripts);
-
     JS_ASSERT(script->getVersion() == version);
     return script;
 }
 
 JSScript *
 JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
 {
     uint32 mainLength, prologLength, nsrcnotes, nfixed;
@@ -1154,50 +1139,49 @@ JSScript::NewScriptFromCG(JSContext *cx,
                        upvarIndexCount, cg->regexpList.length,
                        cg->ntrynotes, cg->constList.length(),
                        cg->globalUses.length(), nClosedArgs, nClosedVars,
                        cg->typesetCount, cg->version());
     if (!script)
         return NULL;
 
     cg->bindings.makeImmutable();
-    AutoShapeRooter shapeRoot(cx, cg->bindings.lastShape());
 
-    /* Now that we have script, error control flow must go to label bad. */
-    script->main += prologLength;
+    JS_ASSERT(script->mainOffset == 0);
+    script->mainOffset = prologLength;
     memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode));
-    memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode));
+    memcpy(script->main(), CG_BASE(cg), mainLength * sizeof(jsbytecode));
     nfixed = cg->inFunction()
              ? cg->bindings.countVars()
              : cg->sharpSlots();
     JS_ASSERT(nfixed < SLOTNO_LIMIT);
     script->nfixed = (uint16) nfixed;
-    js_InitAtomMap(cx, &script->atomMap, cg->atomIndices.getMap());
+    js_InitAtomMap(cx, cg->atomIndices.getMap(), script->atoms);
 
     filename = cg->parser->tokenStream.getFilename();
     if (filename) {
         script->filename = SaveScriptFilename(cx, filename);
         if (!script->filename)
-            goto bad;
+            return NULL;
     }
     script->lineno = cg->firstLine;
     if (script->nfixed + cg->maxStackDepth >= JS_BIT(16)) {
         ReportCompileErrorNumber(cx, CG_TS(cg), NULL, JSREPORT_ERROR, JSMSG_NEED_DIET, "script");
-        goto bad;
+        return NULL;
     }
     script->nslots = script->nfixed + cg->maxStackDepth;
     script->staticLevel = uint16(cg->staticLevel);
     script->principals = cg->parser->principals;
     if (script->principals)
         JSPRINCIPALS_HOLD(cx, script->principals);
 
     script->sourceMap = (jschar *) cg->parser->tokenStream.releaseSourceMap();
 
     if (!js_FinishTakingSrcNotes(cx, cg, script->notes()))
-        goto bad;
+        return NULL;
     if (cg->ntrynotes != 0)
         js_FinishTakingTryNotes(cg, script->trynotes());
     if (cg->objectList.length != 0)
         cg->objectList.finish(script->objects());
     if (cg->regexpList.length != 0)
         cg->regexpList.finish(script->regexps());
     if (cg->constList.length() != 0)
         cg->constList.finish(script->consts());
@@ -1261,66 +1245,62 @@ JSScript::NewScriptFromCG(JSContext *cx,
         JS_ASSERT(fun->isInterpreted());
         JS_ASSERT(!fun->script());
 #ifdef DEBUG
         if (JSScript::isValidOffset(script->upvarsOffset))
             JS_ASSERT(script->upvars()->length == script->bindings.countUpvars());
         else
             JS_ASSERT(script->bindings.countUpvars() == 0);
 #endif
-#ifdef CHECK_SCRIPT_OWNER
-        script->owner = NULL;
-#endif
         if (cg->flags & TCF_FUN_HEAVYWEIGHT)
             fun->flags |= JSFUN_HEAVYWEIGHT;
 
         /* Watch for scripts whose functions will not be cloned. These are singletons. */
         bool singleton =
             cx->typeInferenceEnabled() && cg->parent && cg->parent->compiling() &&
             cg->parent->asCodeGenerator()->checkSingletonContext();
 
         if (!script->typeSetFunction(cx, fun, singleton))
-            goto bad;
+            return NULL;
 
         fun->u.i.script = script;
         script->setOwnerObject(fun);
     } else {
         /*
          * Initialize script->object, if necessary, so that the debugger has a
          * valid holder object.
          */
         if ((cg->flags & TCF_NEED_SCRIPT_OBJECT) && !js_NewScriptObject(cx, script))
-            goto bad;
+            return NULL;
     }
 
     /* Tell the debugger about this compiled script. */
     js_CallNewScriptHook(cx, script, fun);
     if (!cg->parent) {
         Debugger::onNewScript(cx, script,
                               fun ? fun : (script->u.object ? script->u.object : cg->scopeChain()),
                               (fun || script->u.object)
                               ? Debugger::NewHeldScript
                               : Debugger::NewNonHeldScript);
     }
 
     return script;
-
-bad:
-    if (!script->u.object)
-        js_DestroyScript(cx, script, 2);
-    return NULL;
 }
 
 size_t
-JSScript::totalSize()
+JSScript::dataSize()
 {
-    return code +
-           length * sizeof(jsbytecode) +
-           numNotes() * sizeof(jssrcnote) -
-           (uint8 *) this;
+#if JS_SCRIPT_INLINE_DATA_LIMIT
+    if (data == inlineData)
+        return 0;
+#endif
+
+    uint8 *dataEnd = code + length * sizeof(jsbytecode) + numNotes() * sizeof(jssrcnote);
+    JS_ASSERT(dataEnd >= data);
+    return dataEnd - data;
 }
 
 void
 JSScript::setOwnerObject(JSObject *owner)
 {
 #ifdef JS_CRASH_DIAGNOSTICS
     CheckScriptOwner(this, JS_NEW_SCRIPT);
     ownerObject = owner;
@@ -1339,229 +1319,92 @@ JSScript::numNotes()
     for (sn = notes_; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
         continue;
     return sn - notes_ + 1;    /* +1 for the terminator */
 }
 
 JS_FRIEND_API(void)
 js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
 {
-    JSNewScriptHook hook;
-
-    hook = cx->debugHooks->newScriptHook;
-    if (hook) {
+    JS_ASSERT(!script->callDestroyHook);
+    if (JSNewScriptHook hook = cx->debugHooks->newScriptHook) {
         AutoKeepAtoms keep(cx->runtime);
         hook(cx, script->filename, script->lineno, script, fun,
              cx->debugHooks->newScriptHookData);
     }
+    script->callDestroyHook = true;
 }
 
 void
 js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
 {
-    JSDestroyScriptHook hook;
+    if (!script->callDestroyHook)
+        return;
 
-    hook = cx->debugHooks->destroyScriptHook;
-    if (hook)
+    if (JSDestroyScriptHook hook = cx->debugHooks->destroyScriptHook)
         hook(cx, script, cx->debugHooks->destroyScriptHookData);
+    script->callDestroyHook = false;
     Debugger::onDestroyScript(script);
     JS_ClearScriptTraps(cx, script);
 }
 
-static void
-DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller)
+void
+JSScript::finalize(JSContext *cx)
 {
-    CheckScript(script, NULL);
-    CheckScriptOwner(script, owner);
-
-    if (script->principals)
-        JSPRINCIPALS_DROP(cx, script->principals);
-
-    GSNCache *gsnCache = GetGSNCache(cx);
-    if (gsnCache->code == script->code)
-        gsnCache->purge();
+    CheckScript(this, NULL);
 
-    /*
-     * Worry about purging the property cache and any compiled traces related
-     * to its bytecode if this script is being destroyed from JS_DestroyScript
-     * or equivalent according to a mandatory "New/Destroy" protocol.
-     *
-     * The GC purges all property caches when regenerating shapes upon shape
-     * generator overflow, so no need in that event to purge just the entries
-     * for this script.
-     *
-     * The GC purges trace-JITted code on every GC activation, not just when
-     * regenerating shapes, so we don't have to purge fragments if the GC is
-     * currently running.
-     *
-     * JS_THREADSAFE note: The code below purges only the current thread's
-     * property cache, so a script not owned by a function or object, which
-     * hands off lifetime management for that script to the GC, must be used by
-     * only one thread over its lifetime.
-     *
-     * This should be an API-compatible change, since a script is never safe
-     * against premature GC if shared among threads without a rooted object
-     * wrapping it to protect the script's mapped atoms against GC. We use
-     * script->owner to enforce this requirement via assertions.
-     */
-#ifdef CHECK_SCRIPT_OWNER
-    JS_ASSERT_IF(cx->runtime->gcRunning, !script->owner);
-#endif
+    js_CallDestroyScriptHook(cx, this);
 
-    /* FIXME: bug 506341; would like to do this only if regenerating shapes. */
-    if (!cx->runtime->gcRunning) {
-        JS_PROPERTY_CACHE(cx).purgeForScript(cx, script);
-
-#ifdef CHECK_SCRIPT_OWNER
-        JS_ASSERT(script->owner == cx->thread());
-#endif
-    }
+    if (principals)
+        JSPRINCIPALS_DROP(cx, principals);
 
 #ifdef JS_TRACER
-    if (script->compartment->hasTraceMonitor())
-        PurgeScriptFragments(script->compartment->traceMonitor(), script);
-#endif
-
-    if (script->types)
-        script->types->destroy();
-
-#ifdef JS_METHODJIT
-    mjit::ReleaseScriptCode(cx, script);
+    if (compartment()->hasTraceMonitor())
+        PurgeScriptFragments(compartment()->traceMonitor(), this);
 #endif
 
-    JS_REMOVE_LINK(&script->links);
-
-    script->pcCounters.destroy(cx);
-
-    if (script->sourceMap)
-        cx->free_(script->sourceMap);
-
-    JS_POISON(script, 0xdb, sizeof(JSScript));
-    *(uint32 *)script = caller;
-    cx->free_(script);
-}
-
-void
-js_DestroyScript(JSContext *cx, JSScript *script, uint32 caller)
-{
-    JS_ASSERT(!cx->runtime->gcRunning);
-    js_CallDestroyScriptHook(cx, script);
-    DestroyScript(cx, script, JS_NEW_SCRIPT, caller);
-}
-
-void
-js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner)
-{
-    JS_ASSERT(cx->runtime->gcRunning);
+    if (types)
+        types->destroy();
 
 #ifdef JS_METHODJIT
-    /* Keep the hook from trying to recompile while the GC is running. */
-    mjit::ReleaseScriptCode(cx, script);
+    mjit::ReleaseScriptCode(cx, this);
 #endif
 
-    js_CallDestroyScriptHook(cx, script);
-    DestroyScript(cx, script, owner, 100);
-}
-
-void
-js_DestroyCachedScript(JSContext *cx, JSScript *script)
-{
-    JS_ASSERT(cx->runtime->gcRunning);
-    DestroyScript(cx, script, JS_CACHED_SCRIPT, 101);
-}
-
-void
-js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner)
-{
-    JS_ASSERT_IF(trc->context->runtime->gcCurrentCompartment, IS_GC_MARKING_TRACER(trc));
+    pcCounters.destroy(cx);
 
-    CheckScript(script, NULL);
-    if (owner)
-        CheckScriptOwner(script, owner);
-
-    JSRuntime *rt = trc->context->runtime;
-
-    /*
-     * During per-compartment GCs we may attempt to trace scripts that are out
-     * of the target compartment. Ignore such attempts, marking the children is
-     * wasted work and if we mark external type objects they will not get
-     * unmarked at the end of the GC cycle.
-     */
-    if (rt->gcCurrentCompartment && rt->gcCurrentCompartment != script->compartment)
-        return;
-
-#ifdef JS_CRASH_DIAGNOSTICS
-    JS_OPT_ASSERT_IF(rt->gcCheckCompartment, script->compartment == rt->gcCheckCompartment);
-#endif
+    if (sourceMap)
+        cx->free_(sourceMap);
 
-    JSAtomMap *map = &script->atomMap;
-    MarkAtomRange(trc, map->length, map->vector, "atomMap");
-
-    if (JSScript::isValidOffset(script->objectsOffset)) {
-        JSObjectArray *objarray = script->objects();
-        MarkObjectRange(trc, objarray->length, objarray->vector, "objects");
-    }
-
-    if (JSScript::isValidOffset(script->regexpsOffset)) {
-        JSObjectArray *objarray = script->regexps();
-        MarkObjectRange(trc, objarray->length, objarray->vector, "objects");
-    }
-
-    if (JSScript::isValidOffset(script->constOffset)) {
-        JSConstArray *constarray = script->consts();
-        MarkValueRange(trc, constarray->length, constarray->vector, "consts");
+#if JS_SCRIPT_INLINE_DATA_LIMIT
+    if (data != inlineData)
+#endif
+    {
+        JS_POISON(data, 0xdb, dataSize());
+        cx->free_(data);
     }
-
-    /*
-     * Mark the object keeping this script alive. The script can be traced
-     * separately if, e.g. we are GC'ing while type inference code is active,
-     * and we need to make sure both the script and the object survive the GC.
-     */
-    if (!script->isCachedEval && script->u.object)
-        MarkObject(trc, *script->u.object, "object");
-    if (script->hasFunction)
-        MarkObject(trc, *script->function(), "script_fun");
-
-    if (IS_GC_MARKING_TRACER(trc) && script->filename)
-        js_MarkScriptFilename(script->filename);
-
-    script->bindings.trace(trc);
-
-#ifdef JS_METHODJIT
-    if (script->jitNormal)
-        script->jitNormal->trace(trc);
-    if (script->jitCtor)
-        script->jitCtor->trace(trc);
-#endif
 }
 
 JSObject *
 js_NewScriptObject(JSContext *cx, JSScript *script)
 {
-    AutoScriptRooter root(cx, script);
-
     JS_ASSERT(!script->u.object);
 
     JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_ScriptClass, NULL, NULL);
     if (!obj)
         return NULL;
     obj->setPrivate(script);
     script->u.object = obj;
     script->setOwnerObject(obj);
 
     /*
      * Clear the object's type/proto, to avoid entraining stuff. Once we no longer use the parent
      * for security checks, then we can clear the parent, too.
      */
     obj->clearType();
 
-#ifdef CHECK_SCRIPT_OWNER
-    script->owner = NULL;
-#endif
-
     return obj;
 }
 
 namespace js {
 
 static const uint32 GSN_CACHE_THRESHOLD = 100;
 static const uint32 GSN_CACHE_MAP_INIT_SIZE = 20;
 
@@ -1701,17 +1544,17 @@ js_LineNumberToPC(JSScript *script, uint
     best = -1;
     lineno = script->lineno;
     bestdiff = SN_LINE_LIMIT;
     for (sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
         /*
          * Exact-match only if offset is not in the prolog; otherwise use
          * nearest greater-or-equal line number match.
          */
-        if (lineno == target && script->code + offset >= script->main)
+        if (lineno == target && offset >= ptrdiff_t(script->mainOffset))
             goto out;
         if (lineno >= target) {
             diff = lineno - target;
             if (diff < bestdiff) {
                 bestdiff = diff;
                 best = offset;
             }
         }
@@ -1833,18 +1676,17 @@ public:
 private:
     JSXDRState *const xdr;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 JSScript *
 js_CloneScript(JSContext *cx, JSScript *script)
 {
-    JS_ASSERT(cx->compartment != script->compartment);
-    JS_ASSERT(script->compartment);
+    JS_ASSERT(cx->compartment != script->compartment());
 
     // serialize script
     AutoJSXDRState w(JS_XDRNewMem(cx, JSXDR_ENCODE));
     if (!w)
         return NULL;
 
     // we don't want gecko to transcribe our principals for us
     DisablePrincipalsTranscoding disable(cx);
@@ -1874,17 +1716,17 @@ js_CloneScript(JSContext *cx, JSScript *
     XDRScriptState rstate(r);
     rstate.filename = script->filename;
     rstate.filenameSaved = true;
 
     if (!js_XDRScript(r, &script))
         return NULL;
 
     // set the proper principals for the script
-    script->principals = script->compartment->principals;
+    script->principals = script->compartment()->principals;
     if (script->principals)
         JSPRINCIPALS_HOLD(cx, script->principals);
 
     return script;
 }
 
 void
 JSScript::copyClosedSlotsTo(JSScript *other)
@@ -1937,12 +1779,12 @@ JSScript::setStepModeFlag(JSContext *cx,
 bool
 JSScript::changeStepModeCount(JSContext *cx, int delta)
 {
     assertSameCompartment(cx, this);
     JS_ASSERT_IF(delta > 0, cx->compartment->debugMode());
 
     uint32 count = stepMode & stepCountMask;
     JS_ASSERT(((count + delta) & stepCountMask) == count + delta);
-    return tryNewStepMode(cx, 
+    return tryNewStepMode(cx,
                           (stepMode & stepFlagMask) |
                           ((count + delta) & stepCountMask));
 }
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -1,9 +1,9 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=4 sw=4 et tw=79 ft=cpp:
  *
  * ***** 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
@@ -357,20 +357,16 @@ class Bindings {
     void trace(JSTracer *trc);
 };
 
 } /* namespace js */
 
 #define JS_OBJECT_ARRAY_SIZE(length)                                          \
     (offsetof(JSObjectArray, vector) + sizeof(JSObject *) * (length))
 
-#if defined DEBUG && defined JS_THREADSAFE
-# define CHECK_SCRIPT_OWNER 1
-#endif
-
 #ifdef JS_METHODJIT
 namespace JSC {
     class ExecutablePool;
 }
 
 #define JS_UNJITTABLE_SCRIPT (reinterpret_cast<void*>(1))
 
 enum JITScriptStatus {
@@ -427,17 +423,17 @@ class JSPCCounters {
     }
 };
 
 static const uint32 JS_SCRIPT_COOKIE = 0xc00cee;
 
 static JSObject * const JS_NEW_SCRIPT = (JSObject *)0x12345678;
 static JSObject * const JS_CACHED_SCRIPT = (JSObject *)0x12341234;
 
-struct JSScript {
+struct JSScript : public js::gc::Cell {
     /*
      * Two successively less primitive ways to make a new JSScript.  The first
      * does *not* call a non-null cx->runtime->newScriptHook -- only the second,
      * NewScriptFromCG, calls this optional debugger hook.
      *
      * The NewScript function can't know whether the script it creates belongs
      * to a function, or is top-level or eval code, but the debugger wants access
      * to the newly made script's function, if any -- so callers of NewScript
@@ -447,63 +443,64 @@ struct JSScript {
     static JSScript *NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
                                uint32 nobjects, uint32 nupvars, uint32 nregexps,
                                uint32 ntrynotes, uint32 nconsts, uint32 nglobals,
                                uint16 nClosedArgs, uint16 nClosedVars, uint32 nTypeSets,
                                JSVersion version);
 
     static JSScript *NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg);
 
-    /* FIXME: bug 586181 */
-    JSCList         links;      /* Links for compartment script list */
+#ifdef JS_CRASH_DIAGNOSTICS
+    /*
+     * Make sure that the cookie size does not affect the GC alignment
+     * requirements.
+     */
+    uint32          cookie1[Cell::CellSize / sizeof(uint32)];
+#endif
     jsbytecode      *code;      /* bytecodes and their immediate operands */
-    uint32          length;     /* length of code vector */
+    uint8           *data;      /* pointer to variable-length data array */
 
-#ifdef JS_CRASH_DIAGNOSTICS
-    uint32          cookie1;
-#endif
-
+    uint32          length;     /* length of code vector */
   private:
-    size_t          useCount_;  /* Number of times the script has been called
-                                 * or has had backedges taken. Reset if the
-                                 * script's JIT code is forcibly discarded. */
-
     uint16          version;    /* JS version under which script was compiled */
 
   public:
     uint16          nfixed;     /* number of slots besides stack operands in
                                    slot array */
-    uint16          nTypeSets;  /* number of type sets used in this script for
-                                   dynamic type monitoring */
-
-    /*
-     * When non-zero, compile script in single-step mode. The top bit is set and
-     * cleared by setStepMode, as used by JSD. The lower bits are a count,
-     * adjusted by changeStepModeCount, used by the Debugger object. Only
-     * when the bit is clear and the count is zero may we compile the script
-     * without single-step support.
-     */
-    uint32          stepMode;
-
     /*
      * Offsets to various array structures from the end of this script, or
      * JSScript::INVALID_OFFSET if the array has length 0.
      */
-  public:
     uint8           objectsOffset;  /* offset to the array of nested function,
                                        block, scope, xml and one-time regexps
                                        objects */
     uint8           upvarsOffset;   /* offset of the array of display ("up")
                                        closure vars */
     uint8           regexpsOffset;  /* offset to the array of to-be-cloned
                                        regexps  */
     uint8           trynotesOffset; /* offset to the array of try notes */
     uint8           globalsOffset;  /* offset to the array of global slots */
     uint8           constOffset;    /* offset to the array of constants */
 
+    uint16          nTypeSets;      /* number of type sets used in this script for
+                                       dynamic type monitoring */
+
+    /*
+     * When non-zero, compile script in single-step mode. The top bit is set and
+     * cleared by setStepMode, as used by JSD. The lower bits are a count,
+     * adjusted by changeStepModeCount, used by the Debugger object. Only
+     * when the bit is clear and the count is zero may we compile the script
+     * without single-step support.
+     */
+    uint32          stepMode;
+
+    uint32          lineno;     /* base line number of script */
+
+    uint32          mainOffset; /* offset of main entry point from code, after
+                                   predef'ing prolog */
     bool            noScriptRval:1; /* no need for result value of last
                                        expression statement */
     bool            savedCallerFun:1; /* can call getCallerFunction() */
     bool            hasSharps:1;      /* script uses sharp variables */
     bool            strictModeCode:1; /* code is in strict mode */
     bool            compileAndGo:1;   /* script was compiled with TCF_COMPILE_N_GO */
     bool            usesEval:1;       /* script uses eval() */
     bool            usesArguments:1;  /* script uses arguments */
@@ -519,97 +516,107 @@ struct JSScript {
     bool            isCachedEval:1;   /* script came from eval(), and is in eval cache */
     bool            usedLazyArgs:1;   /* script has used lazy arguments at some point */
     bool            createdArgs:1;    /* script has had arguments objects created */
     bool            uninlineable:1;   /* script is considered uninlineable by analysis */
 #ifdef JS_METHODJIT
     bool            debugMode:1;      /* script was compiled in debug mode */
     bool            failedBoundsCheck:1; /* script has had hoisted bounds checks fail */
 #endif
+    bool            callDestroyHook:1;/* need to call destroy hook */
 
-    jsbytecode      *main;      /* main entry point, after predef'ing prolog */
-    JSAtomMap       atomMap;    /* maps immediate index to literal struct */
-    JSCompartment   *compartment; /* compartment the script was compiled for */
-    const char      *filename;  /* source filename or null */
-    uint32          lineno;     /* base line number of script */
+    uint32          natoms;     /* length of atoms array */
     uint16          nslots;     /* vars plus maximum stack depth */
     uint16          staticLevel;/* static level for display maintenance */
+
     uint16          nClosedArgs; /* number of args which are closed over. */
     uint16          nClosedVars; /* number of vars which are closed over. */
+
+    /*
+     * To ensure sizeof(JSScript) % gc::Cell::CellSize  == 0 on we must pad
+     * the script with 4 bytes. We use them to store tiny scripts like empty
+     * scripts.
+     */
+#define JS_SCRIPT_INLINE_DATA_LIMIT 4
+    uint8           inlineData[JS_SCRIPT_INLINE_DATA_LIMIT];
+
+    const char      *filename;  /* source filename or null */
+    JSAtom          **atoms;    /* maps immediate index to literal struct */
+  private:
+    size_t          useCount;  /* Number of times the script has been called
+                                 * or has had backedges taken. Reset if the
+                                 * script's JIT code is forcibly discarded. */
+  public:
     js::Bindings    bindings;   /* names of top-level variables in this script
                                    (and arguments if this is a function script) */
     JSPrincipals    *principals;/* principals for this script */
     jschar          *sourceMap; /* source map file or null */
 
-#ifdef JS_CRASH_DIAGNOSTICS
-    JSObject        *ownerObject;
-#endif
-
-    void setOwnerObject(JSObject *owner);
-
     union {
         /*
          * A script object of class js_ScriptClass, to ensure the script is GC'd.
          * - All scripts returned by JSAPI functions (JS_CompileScript,
          *   JS_CompileFile, etc.) have these objects.
          * - Function scripts never have script objects; such scripts are owned
          *   by their function objects.
          * - Temporary scripts created by obj_eval, JS_EvaluateScript, and
          *   similar functions never have these objects; such scripts are
          *   explicitly destroyed by the code that created them.
          */
         JSObject    *object;
-        JSScript    *nextToGC;  /* next to GC in rt->scriptsToGC list */
+
+        /* Hash table chaining for JSCompartment::evalCache. */
+        JSScript    *evalHashLink;
     } u;
 
-#ifdef CHECK_SCRIPT_OWNER
-    JSThread        *owner;     /* for thread-safe life-cycle assertions */
-#endif
-
     uint32          *closedSlots; /* vector of closed slots; args first, then vars. */
 
     /* array of execution counters for every JSOp in the script, by runmode */
     JSPCCounters    pcCounters;
 
-#ifdef JS_CRASH_DIAGNOSTICS
-    uint32          cookie2;
-#endif
-
-  public:
-
     union {
         /* Function this script is the body for, if there is one. */
         JSFunction *fun;
 
         /* Global object for this script, if compileAndGo. */
         js::GlobalObject *global;
     } where;
 
     inline JSFunction *function() const {
         JS_ASSERT(hasFunction);
         return where.fun;
     }
 
+#ifdef JS_CRASH_DIAGNOSTICS
+    JSObject        *ownerObject;
+
+    /* All diagnostic fields must be multiples of Cell::CellSize. */
+    uint32          cookie2[sizeof(JSObject *) == 4 ? 1 : 2];
+#endif
+
+    void setOwnerObject(JSObject *owner);
+
     /*
      * Associates this script with a specific function, constructing a new type
      * object for the function.
      */
     bool typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton = false);
 
     inline bool hasGlobal() const;
     inline js::GlobalObject *global() const;
 
     inline bool hasClearedGlobal() const;
 
 #ifdef DEBUG
     /*
      * Unique identifier within the compartment for this script, used for
      * printing analysis information.
      */
-    unsigned id_;
+    uint32 id_;
+    uint32 idpad;
     unsigned id() { return id_; }
 #else
     unsigned id() { return 0; }
 #endif
 
     /* Persistent type information retained across GCs. */
     js::types::TypeScript *types;
 
@@ -634,90 +641,96 @@ struct JSScript {
     // quickly test whether there is JIT code; a NULL value means no
     // compilation has been attempted. A JS_UNJITTABLE_SCRIPT value means
     // compilation failed. Any value is the arity-check entry point.
     void *jitArityCheckNormal;
     void *jitArityCheckCtor;
 
     js::mjit::JITScript *jitNormal;   /* Extra JIT info for normal scripts */
     js::mjit::JITScript *jitCtor;     /* Extra JIT info for constructors */
+#endif
 
+#ifdef JS_METHODJIT
     bool hasJITCode() {
         return jitNormal || jitCtor;
     }
 
     // These methods are implemented in MethodJIT.h.
     inline void **nativeMap(bool constructing);
     inline void *maybeNativeCodeForPC(bool constructing, jsbytecode *pc);
     inline void *nativeCodeForPC(bool constructing, jsbytecode *pc);
 
     js::mjit::JITScript *getJIT(bool constructing) {
         return constructing ? jitCtor : jitNormal;
     }
 
-    size_t useCount() const  { return useCount_; }
-    size_t incUseCount() { return ++useCount_; }
-    size_t *addressOfUseCount() { return &useCount_; }
-    void resetUseCount() { useCount_ = 0; }
+    size_t getUseCount() const  { return useCount; }
+    size_t incUseCount() { return ++useCount; }
+    size_t *addressOfUseCount() { return &useCount; }
+    void resetUseCount() { useCount = 0; }
 
     JITScriptStatus getJITStatus(bool constructing) {
         void *addr = constructing ? jitArityCheckCtor : jitArityCheckNormal;
         if (addr == NULL)
             return JITScript_None;
         if (addr == JS_UNJITTABLE_SCRIPT)
             return JITScript_Invalid;
         return JITScript_Valid;
     }
 
     // This method is implemented in MethodJIT.h.
     JS_FRIEND_API(size_t) jitDataSize();/* Size of the JITScript and all sections */
 #endif
 
-    JS_FRIEND_API(size_t) totalSize();  /* Size of the JSScript and all sections */
+    jsbytecode *main() {
+        return code + mainOffset;
+    }
+
+    JS_FRIEND_API(size_t) dataSize();   /* Size of all data sections */
     uint32 numNotes();                  /* Number of srcnote slots in the srcnotes section */
 
     /* Script notes are allocated right after the code. */
     jssrcnote *notes() { return (jssrcnote *)(code + length); }
 
     static const uint8 INVALID_OFFSET = 0xFF;
     static bool isValidOffset(uint8 offset) { return offset != INVALID_OFFSET; }
 
     JSObjectArray *objects() {
         JS_ASSERT(isValidOffset(objectsOffset));
-        return reinterpret_cast<JSObjectArray *>(uintptr_t(this + 1) + objectsOffset);
+        return reinterpret_cast<JSObjectArray *>(data + objectsOffset);
     }
 
     JSUpvarArray *upvars() {
         JS_ASSERT(isValidOffset(upvarsOffset));
-        return reinterpret_cast<JSUpvarArray *>(uintptr_t(this + 1) + upvarsOffset);
+        return reinterpret_cast<JSUpvarArray *>(data + upvarsOffset);
     }
 
     JSObjectArray *regexps() {
         JS_ASSERT(isValidOffset(regexpsOffset));
-        return reinterpret_cast<JSObjectArray *>(uintptr_t(this + 1) + regexpsOffset);
+        return reinterpret_cast<JSObjectArray *>(data + regexpsOffset);
     }
 
     JSTryNoteArray *trynotes() {
         JS_ASSERT(isValidOffset(trynotesOffset));
-        return reinterpret_cast<JSTryNoteArray *>(uintptr_t(this + 1) + trynotesOffset);
+        return reinterpret_cast<JSTryNoteArray *>(data + trynotesOffset);
     }
 
     js::GlobalSlotArray *globals() {
         JS_ASSERT(isValidOffset(globalsOffset));
-        return reinterpret_cast<js::GlobalSlotArray *>(uintptr_t(this + 1) + globalsOffset);
+        return reinterpret_cast<js::GlobalSlotArray *>(data + globalsOffset);
     }
 
     JSConstArray *consts() {
         JS_ASSERT(isValidOffset(constOffset));
-        return reinterpret_cast<JSConstArray *>(uintptr_t(this + 1) + constOffset);
+        return reinterpret_cast<JSConstArray *>(data + constOffset);
     }
 
     JSAtom *getAtom(size_t index) {
-        JS_ASSERT(index < atomMap.length);
-        return atomMap.vector[index];
+        JS_ASSERT(index < natoms);
+        return atoms[index];
     }
 
     JSObject *getObject(size_t index) {
         JSObjectArray *arr = objects();
         JS_ASSERT(index < arr->length);
         return arr->vector[index];
     }
 
@@ -797,21 +810,24 @@ struct JSScript {
      */
     bool changeStepModeCount(JSContext *cx, int delta);
 
     bool stepModeEnabled() { return !!stepMode; }
 
 #ifdef DEBUG
     uint32 stepModeCount() { return stepMode & stepCountMask; }
 #endif
+
+    void finalize(JSContext *cx);
 };
 
+JS_STATIC_ASSERT(sizeof(JSScript) % js::gc::Cell::CellSize == 0);
+
 #define SHARP_NSLOTS            2       /* [#array, #depth] slots if the script
                                            uses sharp variables */
-
 static JS_INLINE uintN
 StackDepth(JSScript *script)
 {
     return script->nslots - script->nfixed;
 }
 
 /*
  * If pc_ does not point within script_'s bytecode, then it must point into an
@@ -847,37 +863,41 @@ js_SweepScriptFilenames(JSCompartment *c
  * of any owning function (the fun parameter) or script object (null fun).
  */
 extern JS_FRIEND_API(void)
 js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun);
 
 extern void
 js_CallDestroyScriptHook(JSContext *cx, JSScript *script);
 
-/*
- * The function must be used only outside the GC for a script that was run
- * only on the current thread.
- */
-extern void
-js_DestroyScript(JSContext *cx, JSScript *script, uint32 caller);
+namespace js {
+
+#ifdef JS_CRASH_DIAGNOSTICS
 
-extern void
-js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner);
+void
+CheckScriptOwner(JSScript *script, JSObject *owner);
+
+void
+CheckScript(JSScript *script, JSScript *prev);
+
+#else
 
-/*
- * Script objects may be cached and reused, in which case their JSD-visible
- * lifetimes may be shorter than their actual lifetimes. Destroy one such
- * script for real as part of a GC pass. From JSD's point of view, the script
- * is already dead.
- */
-extern void
-js_DestroyCachedScript(JSContext *cx, JSScript *script);
+inline void
+CheckScriptOwner(JSScript *script, JSObject *owner)
+{
+}
 
-extern void
-js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner);
+inline void
+CheckScript(JSScript *script, JSScript *prev)
+{
+}
+
+#endif /* !JS_CRASH_DIAGNOSTICS */
+
+} /* namespace js */
 
 extern JSObject *
 js_NewScriptObject(JSContext *cx, JSScript *script);
 
 /*
  * To perturb as little code as possible, we introduce a js_GetSrcNote lookup
  * cache without adding an explicit cx parameter.  Thus js_GetSrcNote becomes
  * a macro that uses cx from its calls' lexical environments.
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -8092,17 +8092,17 @@ TraceRecorder::updateAtoms()
              ? 0
              : script->consts()->vector;
     strictModeCode_ins = w.name(w.immi(script->strictModeCode), "strict");
 }
 
 JS_REQUIRES_STACK void
 TraceRecorder::updateAtoms(JSScript *script)
 {
-    atoms = script->atomMap.vector;
+    atoms = script->atoms;
     consts = JSScript::isValidOffset(script->constOffset) ? script->consts()->vector : 0;
     strictModeCode_ins = w.name(w.immi(script->strictModeCode), "strict");
 }
 
 /*
  * Generate LIR to compute the scope chain.
  */
 JS_REQUIRES_STACK LIns*
@@ -14511,17 +14511,17 @@ TraceRecorder::record_JSOP_STRICTNE()
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_OBJECT()
 {
     StackFrame* const fp = cx->fp();
     JSScript* script = fp->script();
-    unsigned index = atoms - script->atomMap.vector + GET_INDEX(cx->regs().pc);
+    unsigned index = atoms - script->atoms + GET_INDEX(cx->regs().pc);
 
     JSObject* obj;
     obj = script->getObject(index);
     stack(0, w.immpObjGC(obj));
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
@@ -15439,17 +15439,17 @@ TraceRecorder::record_JSOP_DEFVAR()
 {
     return ARECORD_STOP;
 }
 
 jsatomid
 TraceRecorder::getFullIndex(ptrdiff_t pcoff)
 {
     jsatomid index = GET_INDEX(cx->regs().pc + pcoff);
-    index += atoms - cx->fp()->script()->atomMap.vector;
+    index += atoms - cx->fp()->script()->atoms;
     return index;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_LAMBDA()
 {
     JSFunction* fun;
     fun = cx->fp()->script()->getFunction(getFullIndex());
@@ -15826,17 +15826,17 @@ TraceRecorder::record_JSOP_RETRVAL()
     return ARECORD_STOP;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_REGEXP()
 {
     StackFrame* const fp = cx->fp();
     JSScript* script = fp->script();
-    unsigned index = atoms - script->atomMap.vector + GET_INDEX(cx->regs().pc);
+    unsigned index = atoms - script->atoms + GET_INDEX(cx->regs().pc);
 
     LIns* proto_ins;
     CHECK_STATUS_A(getClassPrototype(JSProto_RegExp, proto_ins));
 
     LIns* args[] = {
         proto_ins,
         w.immpObjGC(script->getRegExp(index)),
         cx_ins
@@ -16974,17 +16974,17 @@ PCWithinLoop(StackFrame *fp, jsbytecode 
 }
 
 LoopProfile::ProfileAction
 LoopProfile::profileOperation(JSContext* cx, JSOp op)
 {
     TraceMonitor* tm = JS_TRACE_MONITOR_FROM_CONTEXT(cx);
 
     JS_ASSERT(tm == traceMonitor);
-    JS_ASSERT(entryScript->compartment->traceMonitor() == tm);
+    JS_ASSERT(entryScript->compartment()->traceMonitor() == tm);
 
     if (profiled) {
         stopProfiling(cx);
         return ProfComplete;
     }
 
     jsbytecode *pc = cx->regs().pc;
     StackFrame *fp = cx->fp();
--- a/js/src/jsvalue.h
+++ b/js/src/jsvalue.h
@@ -560,19 +560,19 @@ class Value
 #endif
 
     JS_ALWAYS_INLINE
     bool isMarkable() const {
         return JSVAL_IS_TRACEABLE_IMPL(data);
     }
 
     JS_ALWAYS_INLINE
-    int32 gcKind() const {
+    JSGCTraceKind gcKind() const {
         JS_ASSERT(isMarkable());
-        return JSVAL_TRACE_KIND_IMPL(data);
+        return JSGCTraceKind(JSVAL_TRACE_KIND_IMPL(data));
     }
 
     JS_ALWAYS_INLINE
     JSWhyMagic whyMagic() const {
         JS_ASSERT(isMagic());
         return data.s.payload.why;
     }
 
--- a/js/src/jsxdrapi.cpp
+++ b/js/src/jsxdrapi.cpp
@@ -714,20 +714,18 @@ JS_XDRScriptObject(JSXDRState *xdr, JSOb
     if (!JS_XDRCStringOrNull(xdr, (char **) &state.filename))
         return false;
 
     if (!js_XDRScript(xdr, &script))
         return false;
 
     if (xdr->mode == JSXDR_DECODE) {
         *scriptObjp = js_NewScriptObject(xdr->cx, script);
-        if (!*scriptObjp) {
-            js_DestroyScript(xdr->cx, script, 8);
+        if (!*scriptObjp)
             return false;
-        }
         js_CallNewScriptHook(xdr->cx, script, NULL);
         Debugger::onNewScript(xdr->cx, script, *scriptObjp, Debugger::NewHeldScript);
     }
 
     return true;
 }
 
 #define CLASS_REGISTRY_MIN      8
--- a/js/src/methodjit/BaseCompiler.h
+++ b/js/src/methodjit/BaseCompiler.h
@@ -143,17 +143,17 @@ class LinkerHelper : public JSC::LinkBuf
     bool verifyRange(JITScript *jit) {
         return verifyRange(JSC::JITCode(jit->code.m_code.executableAddress(), jit->code.m_size));
     }
 
     JSC::ExecutablePool *init(JSContext *cx) {
         // The pool is incref'd after this call, so it's necessary to release()
         // on any failure.
         JSScript *script = cx->fp()->script();
-        JSC::ExecutableAllocator *allocator = script->compartment->jaegerCompartment()->execAlloc();
+        JSC::ExecutableAllocator *allocator = script->compartment()->jaegerCompartment()->execAlloc();
         JSC::ExecutablePool *pool;
         m_code = executableAllocAndCopy(masm, allocator, &pool);
         if (!m_code) {
             js_ReportOutOfMemory(cx);
             return NULL;
         }
         m_size = masm.size();   // must come after call to executableAllocAndCopy()!
         return pool;
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -137,17 +137,17 @@ mjit::Compiler::Compiler(JSContext *cx, 
     pcLengths(NULL)
 {
     /* :FIXME: bug 637856 disabling traceJit if inference is enabled */
     if (cx->typeInferenceEnabled())
         addTraceHints = false;
 
     /* Once a script starts getting really hot we will inline calls in it. */
     if (!debugMode() && cx->typeInferenceEnabled() && globalObj &&
-        (outerScript->useCount() >= USES_BEFORE_INLINING ||
+        (outerScript->getUseCount() >= USES_BEFORE_INLINING ||
          cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS))) {
         inlining_ = true;
     }
 }
 
 CompileStatus
 mjit::Compiler::compile()
 {
@@ -855,17 +855,17 @@ mjit::Compiler::finishThisUp(JITScript *
 
     size_t codeSize = masm.size() +
                       stubcc.size() +
                       (masm.numDoubles() * sizeof(double)) +
                       (stubcc.masm.numDoubles() * sizeof(double)) +
                       jumpTableOffsets.length() * sizeof(void *);
 
     JSC::ExecutablePool *execPool;
-    uint8 *result = (uint8 *)script->compartment->jaegerCompartment()->execAlloc()->
+    uint8 *result = (uint8 *)script->compartment()->jaegerCompartment()->execAlloc()->
                     alloc(codeSize, &execPool, JSC::METHOD_CODE);
     if (!result) {
         js_ReportOutOfMemory(cx);
         return Compile_Error;
     }
     JS_ASSERT(execPool);
     JSC::ExecutableAllocator::makeWritable(result, codeSize);
     masm.executableCopy(result);
@@ -2812,17 +2812,17 @@ mjit::Compiler::labelOf(jsbytecode *pc, 
 
 uint32
 mjit::Compiler::fullAtomIndex(jsbytecode *pc)
 {
     return GET_SLOTNO(pc);
 
     /* If we ever enable INDEXBASE garbage, use this below. */
 #if 0
-    return GET_SLOTNO(pc) + (atoms - script->atomMap.vector);
+    return GET_SLOTNO(pc) + (atoms - script->atoms);
 #endif
 }
 
 bool
 mjit::Compiler::knownJump(jsbytecode *pc)
 {
     return pc < PC;
 }
@@ -5492,17 +5492,17 @@ mjit::Compiler::iter(uintN flags)
     frame.pinReg(reg);
     RegisterID ioreg = frame.allocReg();  /* Will hold iterator JSObject */
     RegisterID nireg = frame.allocReg();  /* Will hold NativeIterator */
     RegisterID T1 = frame.allocReg();
     RegisterID T2 = frame.allocReg();
     frame.unpinReg(reg);
 
     /* Fetch the most recent iterator. */
-    masm.loadPtr(&script->compartment->nativeIterCache.last, ioreg);
+    masm.loadPtr(&script->compartment()->nativeIterCache.last, ioreg);
 
     /* Test for NULL. */
     Jump nullIterator = masm.branchTest32(Assembler::Zero, ioreg, ioreg);
     stubcc.linkExit(nullIterator, Uses(1));
 
     /* Get NativeIterator from iter obj. */
     masm.loadObjPrivate(ioreg, nireg);
 
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -82,17 +82,17 @@ static jsbytecode *
 FindExceptionHandler(JSContext *cx)
 {
     StackFrame *fp = cx->fp();
     JSScript *script = fp->script();
 
 top:
     if (cx->isExceptionPending() && JSScript::isValidOffset(script->trynotesOffset)) {
         // The PC is updated before every stub call, so we can use it here.
-        unsigned offset = cx->regs().pc - script->main;
+        unsigned offset = cx->regs().pc - script->main();
 
         JSTryNoteArray *tnarray = script->trynotes();
         for (unsigned i = 0; i < tnarray->length; ++i) {
             JSTryNote *tn = &tnarray->vector[i];
 
             // The following if condition actually tests two separate conditions:
             //   (1) offset - tn->start >= tn->length
             //       means the PC is not in the range of this try note, so we
@@ -108,17 +108,17 @@ top:
             //       exception, in which case this would not be the right handler.
             //       But the first ops of exception handlers generated by our
             //       bytecode compiler cannot throw, so this is not possible.
             if (offset - tn->start > tn->length)
                 continue;
             if (tn->stackDepth > cx->regs().sp - fp->base())
                 continue;
 
-            jsbytecode *pc = script->main + tn->start + tn->length;
+            jsbytecode *pc = script->main() + tn->start + tn->length;
             cx->regs().pc = pc;
             JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE);
             JS_ASSERT(cx->regs().sp == fp->base() + tn->stackDepth);
 
             switch (tn->kind) {
                 case JSTRY_CATCH:
                   JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == JSOP_ENTERBLOCK);
 
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -127,25 +127,25 @@ StackFrame::methodjitStaticAsserts()
 #ifdef JS_METHODJIT_PROFILE_STUBS
 static const size_t STUB_CALLS_FOR_OP_COUNT = 255;
 static uint32 StubCallsForOp[STUB_CALLS_FOR_OP_COUNT];
 #endif
 
 extern "C" void JS_FASTCALL
 PushActiveVMFrame(VMFrame &f)
 {
-    f.entryfp->script()->compartment->jaegerCompartment()->pushActiveFrame(&f);
+    f.entryfp->script()->compartment()->jaegerCompartment()->pushActiveFrame(&f);
     f.entryfp->setNativeReturnAddress(JS_FUNC_TO_DATA_PTR(void*, JaegerTrampolineReturn));
     f.regs.clearInlined();
 }
 
 extern "C" void JS_FASTCALL
 PopActiveVMFrame(VMFrame &f)
 {
-    f.entryfp->script()->compartment->jaegerCompartment()->popActiveFrame();
+    f.entryfp->script()->compartment()->jaegerCompartment()->popActiveFrame();
 }
 
 extern "C" void JS_FASTCALL
 SetVMFrameRegs(VMFrame &f)
 {
     f.oldregs = &f.cx->stack.regs();
 
     /* Restored on exit from EnterMethodJIT. */
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -432,17 +432,17 @@ Recompiler::recompile(bool resetUses)
      *    code.
      * 2) If an address corresponds to a call site registered by |callSite| during
      *    the last compilation, patch it to go to the interpoline.
      * 3) Purge the old compiled state.
      */
 
     // Find all JIT'd stack frames to account for return addresses that will
     // need to be patched after recompilation.
-    for (VMFrame *f = script->compartment->jaegerCompartment()->activeFrame();
+    for (VMFrame *f = script->compartment()->jaegerCompartment()->activeFrame();
          f != NULL;
          f = f->previous) {
 
         // Scan all frames owned by this VMFrame.
         StackFrame *end = f->entryfp->prev();
         StackFrame *next = NULL;
         for (StackFrame *fp = f->fp(); fp != end; fp = fp->prev()) {
             if (fp->script() != script) {
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1930,17 +1930,17 @@ SrcNotes(JSContext *cx, JSScript *script
                 Sprint(sp, " first case offset %u", caseOff);
             UpdateSwitchTableBounds(cx, script, offset,
                                     &switchTableStart, &switchTableEnd);
             break;
           }
           case SRC_CATCH:
             delta = (uintN) js_GetSrcNoteOffset(sn, 0);
             if (delta) {
-                if (script->main[offset] == JSOP_LEAVEBLOCK)
+                if (script->main()[offset] == JSOP_LEAVEBLOCK)
                     Sprint(sp, " stack depth %u", delta);
                 else
                     Sprint(sp, " guard delta %u", delta);
             }
             break;
           default:;
         }
         Sprint(sp, "\n");
@@ -3907,53 +3907,59 @@ Parse(JSContext *cx, uintN argc, jsval *
     return JS_TRUE;
 }
 
 struct FreeOnReturn {
     JSContext *cx;
     const char *ptr;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 
-    FreeOnReturn(JSContext *cx, const char *ptr JS_GUARD_OBJECT_NOTIFIER_PARAM)
+    FreeOnReturn(JSContext *cx, const char *ptr = NULL JS_GUARD_OBJECT_NOTIFIER_PARAM)
       : cx(cx), ptr(ptr) {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
+    void init(const char *ptr) {
+        JS_ASSERT(!this->ptr);
+        this->ptr = ptr;
+    }
+
     ~FreeOnReturn() {
         JS_free(cx, (void*)ptr);
     }
 };
 
 static JSBool
 Snarf(JSContext *cx, uintN argc, jsval *vp)
 {
     JSString *str;
-    const char *pathname;
 
     if (!argc)
         return JS_FALSE;
 
     str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
     if (!str)
         return JS_FALSE;
     JSAutoByteString filename(cx, str);
     if (!filename)
         return JS_FALSE;
 
     /* Get the currently executing script's name. */
     JSStackFrame *fp = JS_GetScriptedCaller(cx, NULL);
     JSScript *script = JS_GetFrameScript(cx, fp);
     JS_ASSERT(fp && script->filename);
+    const char *pathname = filename.ptr();
 #ifdef XP_UNIX
-    pathname = MakeAbsolutePathname(cx, script->filename, filename.ptr());
-    if (!pathname)
-        return JS_FALSE;
-    FreeOnReturn pnGuard(cx, pathname);
-#else
-    pathname = filename.ptr();
+    FreeOnReturn pnGuard(cx);
+    if (pathname[0] != '/') {
+        pathname = MakeAbsolutePathname(cx, script->filename, pathname);
+        if (!pathname)
+            return JS_FALSE;
+        pnGuard.init(pathname);
+    }
 #endif
 
     if (argc > 1) {
         JSString *opt = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]);
         if (!opt)
             return JS_FALSE;
         JSBool match;
         if (!JS_StringEqualsAscii(cx, opt, "binary", &match))
@@ -4055,31 +4061,36 @@ MJitCodeStats(JSContext *cx, uintN argc,
     }
     JS_SET_RVAL(cx, vp, INT_TO_JSVAL(n));
 #else
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
 #endif
     return true;
 }
 
+#ifdef JS_METHODJIT
+
+static void
+SumJitDataSizeCallabck(JSContext *cx, void *data, void *thing,
+                       JSGCTraceKind traceKind, size_t thingSize)
+{
+    size_t *sump = static_cast<size_t *>(data);
+    JS_ASSERT(traceKind == JSTRACE_SCRIPT);
+    JSScript *script = static_cast<JSScript *>(thing);
+    *sump += script->jitDataSize();
+}
+
+#endif
+
 JSBool
 MJitDataStats(JSContext *cx, uintN argc, jsval *vp)
 {
 #ifdef JS_METHODJIT
-    JSRuntime *rt = cx->runtime;
-    AutoLockGC lock(rt);
     size_t n = 0;
-    for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
-        for (JSScript *script = (JSScript *)(*c)->scripts.next;
-             &script->links != &(*c)->scripts;
-             script = (JSScript *)script->links.next)
-        {
-            n += script->jitDataSize(); 
-        }
-    }
+    IterateCells(cx, NULL, gc::FINALIZE_TYPE_OBJECT, &n, SumJitDataSizeCallabck);
     JS_SET_RVAL(cx, vp, INT_TO_JSVAL(n));
 #else
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
 #endif
     return true;
 }
 
 JSBool
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -232,17 +232,17 @@ BreakpointSite::clearTrap(JSContext *cx,
 
 void
 BreakpointSite::destroyIfEmpty(JSRuntime *rt, BreakpointSiteMap::Enum *e)
 {
     if (JS_CLIST_IS_EMPTY(&breakpoints) && !trapHandler) {
         if (e)
             e->removeFront();
         else
-            script->compartment->breakpointSites.remove(pc);
+            script->compartment()->breakpointSites.remove(pc);
         rt->delete_(this);
     }
 }
 
 Breakpoint *
 BreakpointSite::firstBreakpoint() const
 {
     if (JS_CLIST_IS_EMPTY(&breakpoints))
@@ -474,17 +474,17 @@ Debugger::slowPathOnLeaveFrame(JSContext
     }
 
     /*
      * If this is an eval frame, then from the debugger's perspective the
      * script is about to be destroyed. Remove any breakpoints in it.
      */
     if (fp->isEvalFrame()) {
         JSScript *script = fp->script();
-        script->compartment->clearBreakpointsIn(cx, NULL, script, NULL);
+        script->compartment()->clearBreakpointsIn(cx, NULL, script, NULL);
     }
 }
 
 bool
 Debugger::wrapDebuggeeValue(JSContext *cx, Value *vp)
 {
     assertSameCompartment(cx, object);
 
@@ -832,17 +832,17 @@ Debugger::slowPathOnNewScript(JSContext 
     if (script->compileAndGo) {
         global = obj->getGlobal();
         if (GlobalObject::DebuggerVector *debuggers = global->getDebuggers()) {
             if (!AddNewScriptRecipients(debuggers, &triggered))
                 return;
         }
     } else {
         global = NULL;
-        GlobalObjectSet &debuggees = script->compartment->getDebuggees();
+        GlobalObjectSet &debuggees = script->compartment()->getDebuggees();
         for (GlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) {
             if (!AddNewScriptRecipients(r.front()->getDebuggers(), &triggered))
                 return;
         }
     }
 
     /*
      * Deliver the event to each debugger, checking again as in
@@ -1893,18 +1893,18 @@ Debugger::newDebuggerScript(JSContext *c
 
     return scriptobj;
 }
 
 JSObject *
 Debugger::wrapHeldScript(JSContext *cx, JSScript *script, JSObject *obj)
 {
     assertSameCompartment(cx, object);
-    JS_ASSERT(cx->compartment != script->compartment);
-    JS_ASSERT(script->compartment == obj->compartment());
+    JS_ASSERT(cx->compartment != script->compartment());
+    JS_ASSERT(script->compartment() == obj->compartment());
 
     ScriptWeakMap::AddPtr p = heldScripts.lookupForAdd(obj);
     if (!p) {
         JSObject *scriptobj = newDebuggerScript(cx, script, obj);
 
         /* The allocation may have caused a GC, which can remove table entries. */
         if (!scriptobj || !heldScripts.relookupOrAdd(p, obj, scriptobj))
             return NULL;
@@ -1926,17 +1926,17 @@ Debugger::wrapJSAPIScript(JSContext *cx,
     JS_ASSERT(obj->isScript());
     return wrapHeldScript(cx, obj->getScript(), obj);
 }
 
 JSObject *
 Debugger::wrapNonHeldScript(JSContext *cx, JSScript *script)
 {
     assertSameCompartment(cx, object);
-    JS_ASSERT(cx->compartment != script->compartment);
+    JS_ASSERT(cx->compartment != script->compartment());
 
     ScriptMap::AddPtr p = nonHeldScripts.lookupForAdd(script);
     if (!p) {
         JSObject *scriptobj = newDebuggerScript(cx, script, NULL);
 
         /* The allocation may have caused a GC, which can remove table entries. */
         if (!scriptobj || !nonHeldScripts.relookupOrAdd(p, script, scriptobj))
             return NULL;
@@ -1945,17 +1945,17 @@ Debugger::wrapNonHeldScript(JSContext *c
     JS_ASSERT(GetScriptReferent(p->value) == script);
     return p->value;
 }
 
 void
 Debugger::slowPathOnDestroyScript(JSScript *script)
 {
     /* Find all debuggers that might have Debugger.Script referring to this script. */
-    js::GlobalObjectSet *debuggees = &script->compartment->getDebuggees();
+    js::GlobalObjectSet *debuggees = &script->compartment()->getDebuggees();
     for (GlobalObjectSet::Range r = debuggees->all(); !r.empty(); r.popFront()) {
         GlobalObject::DebuggerVector *debuggers = r.front()->getDebuggers();
         for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++)
             (*p)->destroyNonHeldScript(script);
     }
 }
 
 void
@@ -2390,17 +2390,17 @@ DebuggerScript_setBreakpoint(JSContext *
     size_t offset;
     if (!ScriptOffset(cx, script, args[0], &offset))
         return false;
 
     JSObject *handler = NonNullObject(cx, args[1]);
     if (!handler)
         return false;
 
-    JSCompartment *comp = script->compartment;
+    JSCompartment *comp = script->compartment();
     jsbytecode *pc = script->code + offset;
     BreakpointSite *site = comp->getOrCreateBreakpointSite(cx, script, pc, holder);
     if (!site)
         return false;
     if (site->inc(cx)) {
         if (cx->runtime->new_<Breakpoint>(dbg, site, handler)) {
             args.rval().setUndefined();
             return true;
@@ -2425,17 +2425,17 @@ DebuggerScript_getBreakpoints(JSContext 
         pc = script->code + offset;
     } else {
         pc = NULL;
     }
 
     JSObject *arr = NewDenseEmptyArray(cx);
     if (!arr)
         return false;
-    JSCompartment *comp = script->compartment;
+    JSCompartment *comp = script->compartment();
     for (BreakpointSiteMap::Range r = comp->breakpointSites.all(); !r.empty(); r.popFront()) {
         BreakpointSite *site = r.front().value;
         if (site->script == script && (!pc || site->pc == pc)) {
             for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = bp->nextInSite()) {
                 if (bp->debugger == dbg &&
                     !js_NewbornArrayPush(cx, arr, ObjectValue(*bp->getHandler())))
                 {
                     return false;
@@ -2453,27 +2453,27 @@ DebuggerScript_clearBreakpoint(JSContext
     REQUIRE_ARGC("Debugger.Script.clearBreakpoint", 1);
     THIS_DEBUGSCRIPT_LIVE_SCRIPT(cx, argc, vp, "clearBreakpoint", args, obj, script);
     Debugger *dbg = Debugger::fromChildJSObject(obj);
 
     JSObject *handler = NonNullObject(cx, args[0]);
     if (!handler)
         return false;
 
-    script->compartment->clearBreakpointsIn(cx, dbg, script, handler);
+    script->compartment()->clearBreakpointsIn(cx, dbg, script, handler);
     args.rval().setUndefined();
     return true;
 }
 
 static JSBool
 DebuggerScript_clearAllBreakpoints(JSContext *cx, uintN argc, Value *vp)
 {
     THIS_DEBUGSCRIPT_LIVE_SCRIPT(cx, argc, vp, "clearBreakpoint", args, obj, script);
     Debugger *dbg = Debugger::fromChildJSObject(obj);
-    script->compartment->clearBreakpointsIn(cx, dbg, script, NULL);
+    script->compartment()->clearBreakpointsIn(cx, dbg, script, NULL);
     args.rval().setUndefined();
     return true;
 }
 
 static JSBool
 DebuggerScript_construct(JSContext *cx, uintN argc, Value *vp)
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR, "Debugger.Script");
@@ -2876,18 +2876,17 @@ EvaluateInScope(JSContext *cx, JSObject 
                                                TCF_COMPILE_N_GO | TCF_NEED_SCRIPT_OBJECT,
                                                chars, length,
                                                filename, lineno, cx->findVersion(),
                                                NULL, UpvarCookie::UPVAR_LEVEL_LIMIT);
 
     if (!script)
         return false;
 
-    bool ok = ExecuteKernel(cx, script, *scobj, fp->thisValue(), EXECUTE_DEBUG, fp, rval);
-    return ok;
+    return ExecuteKernel(cx, script, *scobj, fp->thisValue(), EXECUTE_DEBUG, fp, rval);
 }
 
 }
 
 enum EvalBindingsMode { WithoutBindings, WithBindings };
 
 static JSBool
 DebuggerFrameEval(JSContext *cx, uintN argc, Value *vp, EvalBindingsMode mode)
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -563,24 +563,24 @@ Debugger::onExceptionUnwind(JSContext *c
            ? JSTRAP_CONTINUE
            : dispatchHook(cx, vp, OnExceptionUnwind);
 }
 
 void
 Debugger::onNewScript(JSContext *cx, JSScript *script, JSObject *obj, NewScriptKind kind)
 {
     JS_ASSERT_IF(kind == NewHeldScript || script->compileAndGo, obj);
-    if (!script->compartment->getDebuggees().empty())
+    if (!script->compartment()->getDebuggees().empty())
         slowPathOnNewScript(cx, script, obj, kind);
 }
 
 void
 Debugger::onDestroyScript(JSScript *script)
 {
-    if (!script->compartment->getDebuggees().empty())
+    if (!script->compartment()->getDebuggees().empty())
         slowPathOnDestroyScript(script);
 }
 
 extern JSBool
 EvaluateInScope(JSContext *cx, JSObject *scobj, StackFrame *fp, const jschar *chars,
                 uintN length, const char *filename, uintN lineno, Value *rval);
 
 }
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -579,26 +579,26 @@ ContextStack::currentScript(jsbytecode *
         return NULL;
 
 #ifdef JS_METHODJIT
     mjit::CallSite *inlined = regs->inlined();
     if (inlined) {
         JS_ASSERT(inlined->inlineIndex < fp->jit()->nInlineFrames);
         mjit::InlineFrame *frame = &fp->jit()->inlineFrames()[inlined->inlineIndex];
         JSScript *script = frame->fun->script();
-        if (script->compartment != cx_->compartment)
+        if (script->compartment() != cx_->compartment)
             return NULL;
         if (ppc)
             *ppc = script->code + inlined->pcOffset;
         return script;
     }
 #endif
 
     JSScript *script = fp->script();
-    if (script->compartment != cx_->compartment)
+    if (script->compartment() != cx_->compartment)
         return NULL;
 
     if (ppc)
         *ppc = fp->pcQuadratic(*this);
     return script;
 }
 
 inline JSObject *
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -887,17 +887,17 @@ class StackFrame
     /*
      * Frame compartment
      *
      * A stack frame's compartment is the frame's containing context's
      * compartment when the frame was pushed.
      */
 
     JSCompartment *compartment() const {
-        JS_ASSERT_IF(isScriptFrame(), scopeChain().compartment() == script()->compartment);
+        JS_ASSERT_IF(isScriptFrame(), scopeChain().compartment() == script()->compartment());
         return scopeChain().compartment();
     }
 
     /*
      * Imacropc
      *
      * A frame's IMacro pc is the bytecode address when an imacro started
      * executing (guaranteed non-null). An imacro does not push a frame, so
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -840,21 +840,25 @@ nsXPConnect::Traverse(void *p, nsCycleCo
             else
             {
                 JS_snprintf(name, sizeof(name), "JS Object (%s)",
                             clazz->name);
             }
         }
         else
         {
-            static const char trace_types[JSTRACE_LIMIT][7] = {
+            static const char trace_types[][11] = {
                 "Object",
                 "String",
-                "Xml"
+                "Script",
+                "Xml",
+                "Shape",
+                "TypeObject",
             };
+            JS_STATIC_ASSERT(JS_ARRAY_LENGTH(trace_types) == JSTRACE_LAST + 1);
             JS_snprintf(name, sizeof(name), "JS %s", trace_types[traceKind]);
         }
 
         if(traceKind == JSTRACE_OBJECT) {
             JSObject *global = static_cast<JSObject*>(p), *parent;
             while((parent = global->getParent()))
                 global = parent;
             char fullname[100];
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -221,17 +221,17 @@ DyingProtoKiller(JSDHashTable *table, JS
     delete proto;
     return JS_DHASH_REMOVE;
 }
 
 static JSDHashOperator
 DetachedWrappedNativeProtoMarker(JSDHashTable *table, JSDHashEntryHdr *hdr,
                                  uint32 number, void *arg)
 {
-    XPCWrappedNativeProto* proto = 
+    XPCWrappedNativeProto* proto =
         (XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key;
 
     proto->Mark();
     return JS_DHASH_NEXT;
 }
 
 // GCCallback calls are chained
 static JSBool
@@ -505,17 +505,17 @@ NoteJSHolder(JSDHashTable *table, JSDHas
 // static
 void
 XPCJSRuntime::SuspectWrappedNative(JSContext *cx, XPCWrappedNative *wrapper,
                                    nsCycleCollectionTraversalCallback &cb)
 {
     if(!wrapper->IsValid() || wrapper->IsWrapperExpired())
         return;
 
-    NS_ASSERTION(NS_IsMainThread() || NS_IsCycleCollectorThread(), 
+    NS_ASSERTION(NS_IsMainThread() || NS_IsCycleCollectorThread(),
                  "Suspecting wrapped natives from non-CC thread");
 
     // Only suspect wrappedJSObjects that are in a compartment that
     // participates in cycle collection.
     JSObject* obj = wrapper->GetFlatJSObjectPreserveColor();
     if(!xpc::ParticipatesInCycleCollection(cx, obj))
         return;
 
@@ -846,17 +846,17 @@ JSBool XPCJSRuntime::GCCallback(JSContex
             // Skip this part if XPConnect is shutting down. We get into
             // bad locking problems with the thread iteration otherwise.
             if(!self->GetXPConnect()->IsShuttingDown())
             {
                 Mutex* threadLock = XPCPerThreadData::GetLock();
                 if(threadLock)
                 {
                     // Do the marking...
-                    
+
                     { // scoped lock
                         MutexAutoLock lock(*threadLock);
 
                         XPCPerThreadData* iterp = nsnull;
                         XPCPerThreadData* thread;
 
                         while(nsnull != (thread =
                                  XPCPerThreadData::IterateThreads(&iterp)))
@@ -865,17 +865,17 @@ JSBool XPCJSRuntime::GCCallback(JSContex
                             while(ccxp)
                             {
                                 // Deal with the strictness of callcontext that
                                 // complains if you ask for a tearoff when
                                 // it is in a state where the tearoff could not
                                 // possibly be valid.
                                 if(ccxp->CanGetTearOff())
                                 {
-                                    XPCWrappedNativeTearOff* to = 
+                                    XPCWrappedNativeTearOff* to =
                                         ccxp->GetTearOff();
                                     if(to)
                                         to->Mark();
                                 }
                                 ccxp = ccxp->GetPrevCallContext();
                             }
                         }
                     }
@@ -1037,17 +1037,17 @@ WrappedJSShutdownMarker(JSDHashTable *ta
     wrapper->SystemIsBeingShutDown(rt);
     return JS_DHASH_NEXT;
 }
 
 static JSDHashOperator
 DetachedWrappedNativeProtoShutdownMarker(JSDHashTable *table, JSDHashEntryHdr *hdr,
                                          uint32 number, void *arg)
 {
-    XPCWrappedNativeProto* proto = 
+    XPCWrappedNativeProto* proto =
         (XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key;
 
     proto->SystemIsBeingShutDown((JSContext*)arg);
     return JS_DHASH_NEXT;
 }
 
 void XPCJSRuntime::SystemIsBeingShutDown(JSContext* cx)
 {
@@ -1245,53 +1245,16 @@ XPCJSRuntime::~XPCJSRuntime()
 #endif
     }
 
     XPCPerThreadData::ShutDown();
 }
 
 namespace {
 
-PRInt64
-GetCompartmentScriptsSize(JSCompartment *c)
-{
-    PRInt64 n = 0;
-    for(JSScript *script = (JSScript *)c->scripts.next;
-        &script->links != &c->scripts;
-        script = (JSScript *)script->links.next)
-    {
-        n += script->totalSize();
-    }
-    return n;
-}
-
-#ifdef JS_METHODJIT
-
-void
-GetCompartmentMjitCodeStats(JSCompartment *c, size_t& method, size_t& regexp,
-        size_t& unused)
-{
-    c->getMjitCodeStats(method, regexp, unused);
-}
-
-PRInt64
-GetCompartmentMjitDataSize(JSCompartment *c)
-{
-    PRInt64 n = 0;
-    for(JSScript *script = (JSScript *)c->scripts.next;
-        &script->links != &c->scripts;
-        script = (JSScript *)script->links.next)
-    {
-        n += script->jitDataSize();
-    }
-    return n;
-}
-
-#endif  // JS_METHODJIT
-
 #ifdef JS_TRACER
 
 PRInt64
 GetCompartmentTjitCodeSize(JSCompartment *c)
 {
     if(c->hasTraceMonitor())
     {
         size_t total, frag_size, free_size;
@@ -1333,84 +1296,95 @@ CompartmentCallback(JSContext *cx, void 
     // Append a new CompartmentStats to the vector.
     IterateData *data = static_cast<IterateData *>(vdata);
     CompartmentStats compartmentStats(cx, compartment);
     CompartmentStats *curr =
         data->compartmentStatsVector.AppendElement(compartmentStats);
     data->currCompartmentStats = curr;
 
     // Get the compartment-level numbers.
-    curr->scripts = GetCompartmentScriptsSize(compartment);
 #ifdef JS_METHODJIT
     size_t method, regexp, unused;
-    GetCompartmentMjitCodeStats(compartment, method, regexp, unused);
+    compartment->getMjitCodeStats(method, regexp, unused);
     curr->mjitCodeMethod = method;
     curr->mjitCodeRegexp = regexp;
     curr->mjitCodeUnused = unused;
-    curr->mjitData = GetCompartmentMjitDataSize(compartment);
 #endif
 #ifdef JS_TRACER
     curr->tjitCode = GetCompartmentTjitCodeSize(compartment);
     curr->tjitDataAllocatorsMain = GetCompartmentTjitDataAllocatorsMainSize(compartment);
     curr->tjitDataAllocatorsReserve = GetCompartmentTjitDataAllocatorsReserveSize(compartment);
     curr->tjitDataNonAllocators = GetCompartmentTjitDataTraceMonitorSize(compartment);
 #endif
     JS_GetTypeInferenceMemoryStats(cx, compartment, &curr->typeInferenceMemory);
 }
 
 void
 ArenaCallback(JSContext *cx, void *vdata, js::gc::Arena *arena,
-              size_t traceKind, size_t thingSize)
+              JSGCTraceKind traceKind, size_t thingSize)
 {
     IterateData *data = static_cast<IterateData *>(vdata);
     data->currCompartmentStats->gcHeapArenaHeaders +=
         sizeof(js::gc::ArenaHeader);
     data->currCompartmentStats->gcHeapArenaPadding +=
         arena->thingsStartOffset(thingSize) - sizeof(js::gc::ArenaHeader);
     // We don't call the callback on unused things.  So we compute the
     // unused space like this:  arenaUnused = maxArenaUnused - arenaUsed.
     // We do this by setting arenaUnused to maxArenaUnused here, and then
     // subtracting thingSize for every used cell, in CellCallback().
     data->currCompartmentStats->gcHeapArenaUnused += arena->thingsSpan(thingSize);
 }
 
 void
-CellCallback(JSContext *cx, void *vdata, void *thing, size_t traceKind,
+CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind,
              size_t thingSize)
 {
     IterateData *data = static_cast<IterateData *>(vdata);
     CompartmentStats *curr = data->currCompartmentStats;
-    if(traceKind == JSTRACE_OBJECT)
-    {
-        curr->gcHeapObjects += thingSize;
-        JSObject *obj = static_cast<JSObject *>(thing);
-        curr->objectSlots += JS_ObjectCountDynamicSlots(obj) * sizeof(js::Value);
-    }
-    else if(traceKind == JSTRACE_STRING)
-    {
-        curr->gcHeapStrings += thingSize;
-        JSString *str = static_cast<JSString *>(thing);
-        curr->stringChars += str->charsHeapSize();
-    }
-    else if(traceKind == JSTRACE_SHAPE)
+    curr->gcHeapKinds[traceKind] += thingSize;
+    switch (traceKind)
     {
-        curr->gcHeapShapes += thingSize;
-        js::Shape *shape = static_cast<js::Shape *>(thing);
-        if(shape->hasTable())
-            curr->propertyTables += shape->getTable()->sizeOf();
-    }
-    else if(traceKind == JSTRACE_TYPE_OBJECT)
-    {
-        js::types::TypeObject *obj = static_cast<js::types::TypeObject *>(thing);
-        JS_GetTypeInferenceObjectStats(obj, &curr->typeInferenceMemory);
-    }
-    else
-    {
-        JS_ASSERT(traceKind == JSTRACE_XML);
-        curr->gcHeapXml += thingSize;
+        case JSTRACE_OBJECT:
+        {
+            JSObject *obj = static_cast<JSObject *>(thing);
+            curr->objectSlots += JS_ObjectCountDynamicSlots(obj) * sizeof(js::Value);
+            break;
+        }
+        case JSTRACE_STRING:
+        {
+            JSString *str = static_cast<JSString *>(thing);
+            curr->stringChars += str->charsHeapSize();
+            break;
+        }
+        case JSTRACE_SHAPE:
+        {
+            js::Shape *shape = static_cast<js::Shape *>(thing);
+            if(shape->hasTable())
+                curr->propertyTables += shape->getTable()->sizeOf();
+            break;
+        }
+        case JSTRACE_SCRIPT:
+        {
+            JSScript *script = static_cast<JSScript *>(thing);
+            curr->scriptData += script->dataSize();
+#ifdef JS_METHODJIT
+            curr->mjitData += script->jitDataSize();
+#endif
+            break;
+        }
+        case JSTRACE_TYPE_OBJECT:
+        {
+            js::types::TypeObject *obj = static_cast<js::types::TypeObject *>(thing);
+            JS_GetTypeInferenceObjectStats(obj, &curr->typeInferenceMemory);
+            break;
+        }
+        case JSTRACE_XML:
+        {
+            break;
+        }
     }
     // Yes, this is a subtraction:  see ArenaCallback() for details.
     curr->gcHeapArenaUnused -= thingSize;
 }
 
 template <int N>
 inline void
 ReportMemory(const nsACString &path, PRInt32 kind, PRInt32 units,
@@ -1665,33 +1639,34 @@ CollectCompartmentStatsForRuntime(JSRunt
     data->gcHeapArenaUnused = 0;
 
     for(PRUint32 index = 0;
         index < data->compartmentStatsVector.Length();
         index++)
     {
         CompartmentStats &stats = data->compartmentStatsVector[index];
 
-        data->gcHeapChunkDirtyUnused -=
-            stats.gcHeapArenaHeaders + stats.gcHeapArenaPadding +
-            stats.gcHeapArenaUnused +
-            stats.gcHeapObjects + stats.gcHeapStrings +
-            stats.gcHeapShapes + stats.gcHeapXml;
-        
+        PRInt64 used = stats.gcHeapArenaHeaders +
+                       stats.gcHeapArenaPadding +
+                       stats.gcHeapArenaUnused;
+        for (size_t i = 0; i != JS_ARRAY_LENGTH(stats.gcHeapKinds); ++i)
+            used += stats.gcHeapKinds[i];
+
+        data->gcHeapChunkDirtyUnused -= used;
         data->gcHeapArenaUnused += stats.gcHeapArenaUnused;
     }
 
     size_t numDirtyChunks = (data->gcHeapChunkTotal -
                              data->gcHeapChunkCleanUnused) /
                             js::GC_CHUNK_SIZE;
     PRInt64 perChunkAdmin =
         sizeof(js::gc::Chunk) - (sizeof(js::gc::Arena) * js::gc::ArenasPerChunk);
     data->gcHeapChunkAdmin = numDirtyChunks * perChunkAdmin;
     data->gcHeapChunkDirtyUnused -= data->gcHeapChunkAdmin;
-    
+
     // Why 10000x?  100x because it's a percentage, and another 100x
     // because nsIMemoryReporter requires that for percentage amounts so
     // they can be fractional.
     data->gcHeapUnusedPercentage = (data->gcHeapChunkCleanUnused +
                                     data->gcHeapChunkDirtyUnused +
                                     data->gcHeapArenaUnused) * 10000 /
                                    data->gcHeapChunkTotal;
 
@@ -1723,42 +1698,57 @@ ReportCompartmentStats(const Compartment
                                               "gc-heap/arena-unused"),
                        JS_GC_HEAP_KIND, stats.gcHeapArenaUnused,
     "Memory on the compartment's garbage-collected JavaScript heap, within "
     "arenas, that could be holding useful data but currently isn't.",
                        callback, closure);
 
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
                                               "gc-heap/objects"),
-                       JS_GC_HEAP_KIND, stats.gcHeapObjects,
+                       JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_OBJECT],
     "Memory on the compartment's garbage-collected JavaScript heap that holds "
     "objects.",
                        callback, closure);
 
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
                                               "gc-heap/strings"),
-                       JS_GC_HEAP_KIND, stats.gcHeapStrings,
+                       JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_STRING],
     "Memory on the compartment's garbage-collected JavaScript heap that holds "
     "string headers.  String headers contain various pieces of information "
     "about a string, but do not contain (except in the case of very short "
     "strings) the string characters;  characters in longer strings are counted "
     "under 'gc-heap/string-chars' instead.",
                        callback, closure);
 
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
+                                              "gc-heap/scripts"),
+                       JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_SCRIPT],
+    "Memory on the compartment's garbage-collected JavaScript heap that holds "
+    "JSScript instances. A JSScript is created for each user-defined function "
+    "in a script. One is also created for the top-level code in a script.",
+                       callback, closure);
+
+    ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
                                               "gc-heap/shapes"),
-                       JS_GC_HEAP_KIND, stats.gcHeapShapes,
+                       JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_SHAPE],
     "Memory on the compartment's garbage-collected JavaScript heap that holds "
     "shapes. A shape is an internal data structure that makes JavaScript "
     "property accesses fast.",
                        callback, closure);
 
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
+                                              "gc-heap/shapes"),
+                       JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_TYPE_OBJECT],
+    "Memory on the compartment's garbage-collected JavaScript heap that holds "
+    "type inference information.",
+                       callback, closure);
+
+    ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
                                               "gc-heap/xml"),
-                       JS_GC_HEAP_KIND, stats.gcHeapXml,
+                       JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_XML],
     "Memory on the compartment's garbage-collected JavaScript heap that holds "
     "E4X XML objects.",
                        callback, closure);
 
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
                                               "object-slots"),
                        nsIMemoryReporter::KIND_HEAP, stats.objectSlots,
     "Memory allocated for the compartment's non-fixed object slot arrays, "
@@ -1790,21 +1780,28 @@ ReportCompartmentStats(const Compartment
                                               "object-empty-shapes"),
                        nsIMemoryReporter::KIND_HEAP,
                        stats.typeInferenceMemory.emptyShapes,
     "Arrays attached to prototype JS objects managing shape information.",
                        callback, closure);
 
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
                                               "scripts"),
-                       nsIMemoryReporter::KIND_HEAP, stats.scripts,
+                       nsIMemoryReporter::KIND_HEAP,
+                       stats.gcHeapKinds[JSTRACE_SCRIPT],
     "Memory allocated for the compartment's JSScripts.  A JSScript is created "
     "for each user-defined function in a script.  One is also created for "
-    "the top-level code in a script.  Each JSScript includes byte-code and "
-    "various other things.",
+    "the top-level code in a script.",
+                       callback, closure);
+
+    ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
+                                              "script-data"),
+                       nsIMemoryReporter::KIND_HEAP, stats.scriptData,
+    "Memory allocated for JSScript bytecode and various variable-length "
+    "tables.",
                        callback, closure);
 
 #ifdef JS_METHODJIT
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
                                               "mjit-code/method"),
                        nsIMemoryReporter::KIND_NONHEAP, stats.mjitCodeMethod,
     "Memory used by the method JIT to hold the compartment's generated code.",
                        callback, closure);
--- a/js/src/xpconnect/src/xpcpublic.h
+++ b/js/src/xpconnect/src/xpcpublic.h
@@ -197,26 +197,23 @@ struct CompartmentStats
 {
     CompartmentStats(JSContext *cx, JSCompartment *c);
 
     nsCString name;
     PRInt64 gcHeapArenaHeaders;
     PRInt64 gcHeapArenaPadding;
     PRInt64 gcHeapArenaUnused;
 
-    PRInt64 gcHeapObjects;
-    PRInt64 gcHeapStrings;
-    PRInt64 gcHeapShapes;
-    PRInt64 gcHeapXml;
+    PRInt64 gcHeapKinds[JSTRACE_LAST + 1];
 
     PRInt64 objectSlots;
     PRInt64 stringChars;
     PRInt64 propertyTables;
+    PRInt64 scriptData;
 
-    PRInt64 scripts;
 #ifdef JS_METHODJIT
     PRInt64 mjitCodeMethod;
     PRInt64 mjitCodeRegexp;
     PRInt64 mjitCodeUnused;
     PRInt64 mjitData;
 #endif
 #ifdef JS_TRACER
     PRInt64 tjitCode;