Backout changeset e6d269bc23b9 (bug 674251) to investigate Tdhtml regression on Mac.
authorMarco Bonardo <mbonardo@mozilla.com>
Thu, 18 Aug 2011 10:18:08 +0200
changeset 75455 21dd913a5c92471d01e9be79ebbb941fa685e824
parent 75454 3de869db521c06046f4bd4485ace8df1c387bb80
child 75456 f69a10f23bf334215464758d71ad900542856f5a
push id21023
push usermak77@bonardo.net
push dateThu, 18 Aug 2011 09:39:20 +0000
treeherdermozilla-central@f69a10f23bf3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs674251
milestone9.0a1
backs oute6d269bc23b91ac74bad6a774d367e1d9859ab32
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backout changeset e6d269bc23b9 (bug 674251) to investigate Tdhtml regression on Mac.
js/src/gnuplot/gcTimer.gnu
js/src/jsapi.cpp
js/src/jsapi.h
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/jsinterp.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsparse.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jstracer.cpp
js/src/jsxdrapi.cpp
js/src/methodjit/BaseCompiler.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/Retcon.cpp
js/src/vm/Debugger.cpp
js/src/vm/Debugger.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,10 +15,9 @@ 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 11 title columnheader(11) with points
+'' u 10 title columnheader(10) with points
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1204,17 +1204,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 *)
@@ -1254,17 +1254,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
@@ -3190,16 +3190,17 @@ 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;
@@ -4720,17 +4721,19 @@ CompileUCFunctionForPrincipalsCommon(JSC
         goto out2;
 
     {
         EmptyShape *emptyCallShape = EmptyShape::getEmptyCallShape(cx);
         if (!emptyCallShape) {
             fun = NULL;
             goto out2;
         }
-
+        AutoShapeRooter shapeRoot(cx, emptyCallShape);
+
+        AutoObjectRooter tvr(cx, FUN_OBJECT(fun));
         MUST_FLOW_THROUGH("out");
 
         Bindings bindings(cx, emptyCallShape);
         AutoBindingsRooter root(cx, bindings);
         for (i = 0; i < nargs; i++) {
             argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]));
             if (!argAtom) {
                 fun = NULL;
@@ -4829,18 +4832,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))
@@ -4919,18 +4922,18 @@ EvaluateUCScriptForPrincipalsCommon(JSCo
                                                chars, length, filename, lineno, compileVersion);
     if (!script) {
         LAST_FRAME_CHECKS(cx, script);
         return false;
     }
     JS_ASSERT(script->getVersion() == compileVersion);
 
     bool ok = ExternalExecute(cx, script, *obj, Valueify(rval));
-    js_CallDestroyScriptHook(cx, script);
     LAST_FRAME_CHECKS(cx, ok);
+    js_DestroyScript(cx, script, 5);
     return ok;
 
 }
 
 JS_PUBLIC_API(JSBool)
 JS_EvaluateUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
                                         JSPrincipals *principals,
                                         const jschar *chars, uintN length,
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1613,17 +1613,16 @@ JS_SetExtraGCRoots(JSRuntime *rt, JSTrac
  *
  * 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
-#define JSTRACE_SCRIPT  3
 
 /*
  * 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/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1447,30 +1447,32 @@ class AutoGCRooter {
      * memory corruption.
      */
     ptrdiff_t tag;
 
     JSContext * const context;
 
     enum {
         JSVAL =        -1, /* js::AutoValueRooter */
-        BINDINGS =     -2, /* js::Bindings */
+        SHAPE =        -2, /* js::AutoShapeRooter */
         PARSER =       -3, /* js::Parser */
-        SHAPEVECTOR =  -4, /* js::AutoShapeVector */
+        SCRIPT =       -4, /* js::AutoScriptRooter */
         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 */
+        IDVECTOR =    -15, /* js::AutoIdVector */
+        BINDINGS =    -16, /* js::Bindings */
+        SHAPEVECTOR = -17  /* js::AutoShapeVector */
     };
 
     private:
     /* No copy or assignment semantics. */
     AutoGCRooter(AutoGCRooter &ida);
     void operator=(AutoGCRooter &ida);
 };
 
@@ -1631,16 +1633,53 @@ 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;
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -186,17 +186,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
@@ -96,17 +96,17 @@ JSCompartment::JSCompartment(JSRuntime *
     initialStringShape(NULL),
     debugModeBits(rt->debugMode ? DebugFromC : 0),
     mathCache(NULL),
     breakpointSites(rt),
     watchpointMap(NULL)
 {
     JS_INIT_CLIST(&scripts);
 
-    PodArrayZero(evalCache);
+    PodArrayZero(scriptsToGC);
 }
 
 JSCompartment::~JSCompartment()
 {
 #if ENABLE_YARR_JIT
     Foreground::delete_(regExpAllocator);
 #endif
 
@@ -117,18 +117,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(evalCache); ++i)
-        JS_ASSERT(!evalCache[i]);
+    for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i)
+        JS_ASSERT(!scriptsToGC[i]);
 #endif
 }
 
 bool
 JSCompartment::init()
 {
     for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
         arenas[i].init();
@@ -544,30 +544,18 @@ JSCompartment::sweep(JSContext *cx, uint
 }
 
 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;
-        }
-    }
+    /* Destroy eval'ed scripts. */
+    js_DestroyScriptsToGC(cx, this);
 
     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.
@@ -641,17 +629,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)
 {
@@ -779,17 +767,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,21 +293,20 @@ 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. */
@@ -410,17 +409,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                     *evalCache[JS_EVAL_CACHE_SIZE];
+    JSScript                     *scriptsToGC[JS_EVAL_CACHE_SIZE];
 
     void                         *data;
     bool                         active;  // GC flag, whether there are active frames
     bool                         hasDebugModeCodeToDrop;
     js::WrapperMap               crossCompartmentWrappers;
 
 #ifdef JS_METHODJIT
   private:
@@ -523,17 +522,16 @@ struct JS_FRIEND_API(JSCompartment) {
     bool wrap(JSContext *cx, js::AutoIdVector &props);
 
     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:
@@ -617,16 +615,17 @@ 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
@@ -207,17 +207,17 @@ JS_SetSingleStepMode(JSContext *cx, JSSc
 
     return js_SetSingleStepMode(cx, script, 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))
@@ -237,48 +237,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);
 }
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -197,16 +197,17 @@ 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. */
@@ -1593,16 +1594,19 @@ 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_OBJECT(fun);
         fun->u.i.script->setOwnerObject(fun);
+#ifdef CHECK_SCRIPT_OWNER
+        fun->script()->owner = NULL;
+#endif
         JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
         js_CallNewScriptHook(cx, fun->script(), fun);
     }
 
     return true;
 }
 
 #else  /* !JS_HAS_XDR */
@@ -1661,39 +1665,52 @@ fun_trace(JSTracer *trc, JSObject *obj)
                            obj->getFlatClosureUpvars(), "upvars");
         }
         return;
     }
 
     if (fun->atom)
         MarkString(trc, fun->atom, "atom");
 
-    if (fun->isInterpreted() && fun->script()) {
-        CheckScriptOwner(fun->script(), obj);
-        MarkScript(trc, fun->script(), "script");
-    }
+    if (fun->isInterpreted() && fun->script())
+        js_TraceScript(trc, fun->script(), obj);
 }
 
 static void
 fun_finalize(JSContext *cx, JSObject *obj)
 {
-    obj->finalizeUpvarsIfFlatClosure();
+    /* 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);
 }
 
 /*
  * 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_CONCURRENT_FINALIZER,
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
     PropertyStub,         /* addProperty */
     PropertyStub,         /* delProperty */
     PropertyStub,         /* getProperty */
     StrictPropertyStub,   /* setProperty */
     fun_enumerate,
     (JSResolveOp)fun_resolve,
     ConvertStub,
     fun_finalize,
@@ -2133,16 +2150,17 @@ Function(JSContext *cx, uintN argc, Valu
     JSFunction *fun = js_NewFunction(cx, obj.get(), NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED,
                                      global, cx->runtime->atomState.anonymousAtom);
     if (!fun)
         return false;
 
     EmptyShape *emptyCallShape = EmptyShape::getEmptyCallShape(cx);
     if (!emptyCallShape)
         return false;
+    AutoShapeRooter shapeRoot(cx, emptyCallShape);
 
     Bindings bindings(cx, emptyCallShape);
     AutoBindingsRooter root(cx, bindings);
 
     uintN lineno;
     const char *filename = CurrentScriptFileAndLine(cx, &lineno);
 
     Value *argv = call.argv();
@@ -2349,16 +2367,19 @@ 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, 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;
     script->setOwnerObject(fun);
     js_CallNewScriptHook(cx, script, fun);
 
     if (obj->isGlobal()) {
         /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */
         JSFunction *throwTypeError =
             js_NewFunction(cx, NULL, reinterpret_cast<Native>(ThrowTypeError), 0,
@@ -2451,25 +2472,28 @@ 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);
 
             JSScript *cscript = js_CloneScript(cx, script);
             if (!cscript)
                 return NULL;
             cfun->u.i.script = cscript;
             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
@@ -107,24 +107,23 @@ 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_SCRIPT == 3);
-JS_STATIC_ASSERT(JSTRACE_XML    == 4);
+JS_STATIC_ASSERT(JSTRACE_XML    == 3);
 
 /*
  * JS_IS_VALID_TRACE_KIND assumes that JSTRACE_SHAPE is the last non-xml
  * trace kind when JS_HAS_XML_SUPPORT is false.
  */
-JS_STATIC_ASSERT(JSTRACE_SCRIPT + 1 == JSTRACE_XML);
+JS_STATIC_ASSERT(JSTRACE_SHAPE + 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,
@@ -145,17 +144,16 @@ const uint8 GCThingSizeMap[] = {
     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(Shape),              /* FINALIZE_SHAPE               */
-    sizeof(JSScript),           /* FINALIZE_SCRIPT              */
 #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,20 +191,16 @@ 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();
@@ -788,19 +782,16 @@ MarkIfGCThingWord(JSTracer *trc, jsuword
         test = MarkArenaPtrConservatively<JSShortString>(trc, aheader, addr);
         break;
       case FINALIZE_FUNCTION:
         test = MarkArenaPtrConservatively<JSFunction>(trc, aheader, addr);
         break;
       case FINALIZE_SHAPE:
         test = MarkArenaPtrConservatively<Shape>(trc, aheader, addr);
         break;
-      case FINALIZE_SCRIPT:
-        test = MarkArenaPtrConservatively<JSScript>(trc, aheader, addr);
-        break;
 #if JS_HAS_XML_SUPPORT
       case FINALIZE_XML:
         test = MarkArenaPtrConservatively<JSXML>(trc, aheader, addr);
         break;
 #endif
       default:
         test = CGCT_WRONGTAG;
         JS_NOT_REACHED("wrong tag");
@@ -1251,35 +1242,28 @@ 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) {
-        /*
-         * 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);
+    if (head && cx->gcBackgroundFree && cx->gcBackgroundFree->finalizeVector.append(head)) {
         head = NULL;
         cursor = &head;
         backgroundFinalizeState = BFS_RUN;
     } else {
         JS_ASSERT_IF(!head, cursor == &head);
         backgroundFinalizeState = BFS_DONE;
         finalizeNow<T>(cx);
     }
@@ -1310,19 +1294,16 @@ 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;
     }
 
@@ -1473,18 +1454,16 @@ RefillFinalizableFreeList(JSContext *cx,
       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_SHAPE:
         return RefillTypedFreeList<Shape>(cx, thingKind);
-      case FINALIZE_SCRIPT:
-        return RefillTypedFreeList<JSScript>(cx, thingKind);
 #if JS_HAS_XML_SUPPORT
       case FINALIZE_XML:
         return RefillTypedFreeList<JSXML>(cx, thingKind);
 #endif
       default:
         JS_NOT_REACHED("bad finalize kind");
         return 0;
     }
@@ -1678,18 +1657,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");
-    MarkScript(trc, fp->script(), "script");
-    fp->script()->compartment()->active = true;
+    js_TraceScript(trc, fp->script(), NULL);
+    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");
@@ -1704,20 +1683,29 @@ 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;
@@ -1963,45 +1951,50 @@ 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)
 {
@@ -2016,22 +2009,16 @@ JSCompartment::finalizeStringArenaLists(
 }
 
 void
 JSCompartment::finalizeShapeArenaLists(JSContext *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)))
@@ -2091,25 +2078,16 @@ 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);
@@ -2143,21 +2121,16 @@ 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;
@@ -2364,18 +2337,16 @@ 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.)
@@ -2392,22 +2363,16 @@ 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
@@ -2675,18 +2640,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) {
-            if (rt->gcHelperThread.prepareForBackgroundSweep(cx))
-                cx->gcBackgroundFree = &rt->gcHelperThread;
+            cx->gcBackgroundFree = &rt->gcHelperThread;
+            cx->gcBackgroundFree->setContext(cx);
         }
 #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);
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -93,32 +93,25 @@ enum FinalizeKind {
     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_SHAPE,
-    FINALIZE_SCRIPT,
 #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
@@ -757,22 +750,22 @@ Cell::unmark(uint32 color) const
 }
 
 JSCompartment *
 Cell::compartment() const
 {
     return arenaHeader()->compartment;
 }
 
-#define JSTRACE_XML         4
+#define JSTRACE_XML         3
 
 /*
  * One past the maximum trace kind.
  */
-#define JSTRACE_LIMIT       5
+#define JSTRACE_LIMIT       4
 
 /*
  * 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
@@ -799,17 +792,16 @@ GetFinalizableTraceKind(size_t thingKind
         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_SHAPE,      /* FINALIZE_SHAPE */
-        JSTRACE_SCRIPT,     /* FINALIZE_SCRIPT */
 #if JS_HAS_XML_SUPPORT      /* FINALIZE_XML */
         JSTRACE_XML,
 #endif
         JSTRACE_STRING,     /* FINALIZE_SHORT_STRING */
         JSTRACE_STRING,     /* FINALIZE_STRING */
         JSTRACE_STRING,     /* FINALIZE_EXTERNAL_STRING */
     };
 
@@ -1194,16 +1186,20 @@ 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.
@@ -1269,17 +1265,17 @@ class GCHelperThread {
     void freeLater(void *ptr) {
         JS_ASSERT(!sweeping);
         if (freeCursor != freeCursorEnd)
             *freeCursor++ = ptr;
         else
             replenishAndFreeLater(ptr);
     }
 
-    bool prepareForBackgroundSweep(JSContext *context);
+    void setContext(JSContext *context) { cx = context; }
 };
 
 #endif /* JS_THREADSAFE */
 
 struct GCChunkHasher {
     typedef gc::Chunk *Lookup;
 
     /*
@@ -1491,17 +1487,17 @@ js_FinalizeStringRT(JSRuntime *rt, JSStr
 /*
  * 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_SCRIPT)
+# define JS_IS_VALID_TRACE_KIND(kind) ((uint32)(kind) <= JSTRACE_SHAPE)
 #endif
 
 namespace js {
 namespace gc {
 
 JSCompartment *
 NewCompartment(JSContext *cx, JSPrincipals *principals);
 
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -250,20 +250,14 @@ js_NewGCFunction(JSContext *cx)
 }
 
 inline js::Shape *
 js_NewGCShape(JSContext *cx)
 {
     return NewGCThing<js::Shape>(cx, js::gc::FINALIZE_SHAPE, sizeof(js::Shape));
 }
 
-inline JSScript *
-js_NewGCScript(JSContext *cx)
-{
-    return NewGCThing<JSScript>(cx, js::gc::FINALIZE_SCRIPT, sizeof(JSScript));
-}
-
 #if JS_HAS_XML_SUPPORT
 extern JSXML *
 js_NewGCXML(JSContext *cx);
 #endif
 
 #endif /* jsgcinlines_h___ */
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -41,17 +41,16 @@
 #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
@@ -91,19 +90,16 @@ PushMarkStack(GCMarker *gcmarker, JSObje
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSFunction *thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, const Shape *thing);
 
 static inline void
-PushMarkStack(GCMarker *gcmarker, JSScript *thing);
-
-static inline void
 PushMarkStack(GCMarker *gcmarker, JSShortString *thing);
 
 static inline void
 PushMarkStack(GCMarker *gcmarker, JSString *thing);
 
 template<typename T>
 void
 Mark(JSTracer *trc, T *thing)
@@ -190,25 +186,16 @@ 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);
 }
 
-void
-MarkScript(JSTracer *trc, JSScript *script, const char *name)
-{
-    JS_ASSERT(trc);
-    JS_ASSERT(script);
-    JS_SET_TRACING_NAME(trc, name);
-    Mark(trc, script);
-}
-
 #if JS_HAS_XML_SUPPORT
 void
 MarkXML(JSTracer *trc, JSXML *xml, const char *name)
 {
     JS_ASSERT(trc);
     JS_ASSERT(xml);
     JS_SET_TRACING_NAME(trc, name);
     Mark(trc, xml);
@@ -264,31 +251,16 @@ PushMarkStack(GCMarker *gcmarker, const 
                  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
-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
 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);
         }
@@ -352,34 +324,32 @@ MarkIdRange(JSTracer *trc, size_t len, j
 }
 
 void
 MarkKind(JSTracer *trc, void *thing, uint32 kind)
 {
     JS_ASSERT(thing);
     JS_ASSERT(kind == GetGCThingTraceKind(thing));
     switch (kind) {
-      default:
-        JS_ASSERT(kind == JSTRACE_OBJECT);
-        Mark(trc, static_cast<JSObject *>(thing));
-        break;
-      case JSTRACE_STRING:
-        MarkString(trc, static_cast<JSString *>(thing));
-        break;
-      case JSTRACE_SHAPE:
-        Mark(trc, static_cast<Shape *>(thing));
-        break;
-      case JSTRACE_SCRIPT:
-        Mark(trc, static_cast<JSScript *>(thing));
-        break;
+        case JSTRACE_OBJECT:
+            Mark(trc, reinterpret_cast<JSObject *>(thing));
+            break;
+        case JSTRACE_STRING:
+            MarkString(trc, reinterpret_cast<JSString *>(thing));
+            break;
+        case JSTRACE_SHAPE:
+            Mark(trc, reinterpret_cast<Shape *>(thing));
+            break;
 #if JS_HAS_XML_SUPPORT
-      case JSTRACE_XML:
-        Mark(trc, static_cast<JSXML *>(thing));
-        break;
+        case JSTRACE_XML:
+            Mark(trc, reinterpret_cast<JSXML *>(thing));
+            break;
 #endif
+        default:
+            JS_ASSERT(false);
     }
 }
 
 /* N.B. Assumes JS_SET_TRACING_NAME/INDEX has already been called. */
 void
 MarkValueRaw(JSTracer *trc, const js::Value &v)
 {
     if (v.isMarkable()) {
@@ -495,22 +465,16 @@ MarkRoot(JSTracer *trc, JSString *thing,
 
 void
 MarkRoot(JSTracer *trc, const Shape *thing, const char *name)
 {
     MarkShape(trc, thing, name);
 }
 
 void
-MarkRoot(JSTracer *trc, JSScript *thing, const char *name)
-{
-    MarkScript(trc, thing, name);
-}
-
-void
 MarkRoot(JSTracer *trc, JSXML *thing, const char *name)
 {
     MarkXML(trc, thing, name);
 }
 
 static void
 PrintPropertyGetterOrSetter(JSTracer *trc, char *buf, size_t bufsize)
 {
@@ -796,64 +760,16 @@ restart:
     if (shape->isMethod())
         MarkObjectWithPrinter(trc, shape->methodObject(), PrintPropertyMethod, shape, 0);
 
     shape = shape->previous();
     if (shape)
         goto restart;
 }
 
-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
-    
-    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");
-    }
-
-    /*
-     * For cached eval scripts JSScript::u.evalHashLink is cleared during the
-     * purge phase. So we can assume that if JSScript::u is not null, it
-     * points to JSObject, not another script.
-     */
-    if (script->u.object) {
-        JS_ASSERT(GetGCThingTraceKind(script->u.object) == JSTRACE_OBJECT);
-        MarkObject(trc, *script->u.object, "object");
-    }
-
-    if (IS_GC_MARKING_TRACER(trc) && script->filename)
-        js_MarkScriptFilename(script->filename);
-
-    script->bindings.trace(trc);
-
-#ifdef JS_METHODJIT
-    mjit::TraceScript(trc, script);
-#endif
-}
-
 #ifdef JS_HAS_XML_SUPPORT
 void
 MarkChildren(JSTracer *trc, JSXML *xml)
 {
     js_TraceXML(trc, xml);
 }
 #endif
 
@@ -894,33 +810,27 @@ GCMarker::drainMarkStack()
 }
 
 } /* namespace js */
 
 JS_PUBLIC_API(void)
 JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind)
 {
     switch (kind) {
-      default:
-        JS_ASSERT(kind == JSTRACE_OBJECT);
-	MarkChildren(trc, static_cast<JSObject *>(thing));
+      case JSTRACE_OBJECT:
+	MarkChildren(trc, (JSObject *)thing);
         break;
 
       case JSTRACE_STRING:
-	MarkChildren(trc, static_cast<JSString *>(thing));
+	MarkChildren(trc, (JSString *)thing);
         break;
 
       case JSTRACE_SHAPE:
-	MarkChildren(trc, static_cast<Shape *>(thing));
-        break;
-
-      case JSTRACE_SCRIPT:
-	MarkChildren(trc, static_cast<JSScript *>(thing));
+	MarkChildren(trc, (js::Shape *)thing);
         break;
 
 #if JS_HAS_XML_SUPPORT
       case JSTRACE_XML:
-        MarkChildren(trc, static_cast<JSXML *>(thing));
+        MarkChildren(trc, (JSXML *)thing);
         break;
 #endif
     }
 }
-
--- a/js/src/jsgcmark.h
+++ b/js/src/jsgcmark.h
@@ -72,20 +72,20 @@ MarkCrossCompartmentObject(JSTracer *trc
 void
 MarkObjectWithPrinter(JSTracer *trc, JSObject &obj, JSTraceNamePrinter printer,
 		      const void *arg, size_t index);
 
 void
 MarkShape(JSTracer *trc, const Shape *shape, const char *name);
 
 void
-MarkScript(JSTracer *trc, JSScript *script, const char *name);
+MarkXML(JSTracer *trc, JSXML *xml, const char *name);
 
 void
-MarkXML(JSTracer *trc, JSXML *xml, const char *name);
+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
@@ -149,32 +149,26 @@ MarkRoot(JSTracer *trc, JSObject *thing,
 
 void
 MarkRoot(JSTracer *trc, JSString *thing, const char *name);
 
 void
 MarkRoot(JSTracer *trc, const Shape *thing, const char *name);
 
 void
-MarkRoot(JSTracer *trc, JSScript *thing, const char *name);
-
-void
 MarkRoot(JSTracer *trc, JSXML *thing, const char *name);
 
 void
 MarkChildren(JSTracer *trc, JSObject *obj);
 
 void
 MarkChildren(JSTracer *trc, JSString *str);
 
 void
 MarkChildren(JSTracer *trc, const Shape *shape);
 
 void
-MarkChildren(JSTracer *trc, JSScript *script);
-
-void
 MarkChildren(JSTracer *trc, JSXML *xml);
 
 }
 }
 
 #endif
--- a/js/src/jsgcstats.cpp
+++ b/js/src/jsgcstats.cpp
@@ -253,35 +253,33 @@ 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 sweepScriptTime = TIMEDIFF(sweepStringEnd, sweepScriptEnd);
-        double sweepShapeTime = TIMEDIFF(sweepScriptEnd, sweepShapeEnd);
+        double sweepShapeTime = TIMEDIFF(sweepStringEnd, 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;
@@ -294,30 +292,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, SwScripts, SwShapes, Destroy,    End, +Chu, -Chu, T, Reason\n");
+                        " FinStr, 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  , 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, ",
+            /*               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, ",
                     appTime, gcTime, waitTime, markTime, sweepTime, sweepObjTime, sweepStringTime,
-                    sweepScriptTime, sweepShapeTime, destroyTime, endTime);
+                    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,18 +44,17 @@
 #endif
 
 #ifdef JSGC_TESTPILOT
 JS_BEGIN_EXTERN_C
 
 struct JSGCInfo
 {
     double appTime, gcTime, waitTime, markTime, sweepTime;
-    double sweepObjTime, sweepStringTime, sweepScriptTime, sweepShapeTime;
-    double destroyTime, endTime;
+    double sweepObjTime, sweepStringTime, sweepShapeTime, destroyTime, endTime;
     bool isCompartmental;
 };
 
 extern JS_PUBLIC_API(void)
 JS_SetGCInfoEnabled(JSRuntime *rt, bool enabled);
 
 extern JS_PUBLIC_API(bool)
 JS_GetGCInfoEnabled(JSRuntime *rt);
@@ -130,17 +129,16 @@ 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/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -885,16 +885,17 @@ Execute(JSContext *cx, JSScript *script,
 
     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))
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -977,17 +977,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 &cx->compartment->evalCache[h];
+    return &JS_SCRIPTS_TO_GC(cx)[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.
@@ -1049,41 +1049,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.evalHashLink;
-                        script->u.evalHashLink = NULL;
+                        *scriptp = script->u.nextToGC;
+                        script->u.nextToGC = NULL;
                         return script;
                     }
                 }
             }
         }
 
         if (++count == EVAL_CACHE_CHAIN_LIMIT)
             return NULL;
-        scriptp = &script->u.evalHashLink;
+        scriptp = &script->u.nextToGC;
     }
     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.evalHashLink).
+ * script->u.object aliases script->u.nextToGC).
  */
 class EvalScriptGuard
 {
     JSContext *cx_;
     JSLinearString *str_;
     JSScript **bucket_;
     JSScript *script_;
 
@@ -1093,18 +1093,21 @@ class EvalScriptGuard
         str_(str),
         script_(NULL) {
         bucket_ = EvalCacheHash(cx, str);
     }
 
     ~EvalScriptGuard() {
         if (script_) {
             js_CallDestroyScriptHook(cx_, script_);
-            script_->u.evalHashLink = *bucket_;
+            script_->u.nextToGC = *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);
@@ -5156,18 +5159,22 @@ 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;
-    if (!shape->get(cx, receiver, obj, pobj, vp))
-        return false;
+    {
+        AutoShapeRooter tvr(cx, shape);
+        AutoObjectRooter tvr2(cx, pobj);
+        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);
     }
@@ -5215,21 +5222,24 @@ 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;
-    if (!shape->set(cx, obj, strict, vp))
-        return false;
-    
-    JS_ASSERT_IF(!obj->inDictionaryMode(), shape->slot == slot);
-    slot = shape->slot;
+    {
+        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 (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
@@ -985,19 +985,16 @@ 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
@@ -520,47 +520,16 @@ 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 *) getSlot(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/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -367,17 +367,17 @@ Parser::trace(JSTracer *trc)
 
 /* Add |node| to |parser|'s free node list. */
 static inline void
 AddNodeToFreeList(JSParseNode *pn, js::Parser *parser)
 {
     /* Catch back-to-back dup recycles. */
     JS_ASSERT(pn != parser->nodeList);
 
-    /*
+    /* 
      * It's too hard to clear these nodes from the AtomDefnMaps, etc. that
      * hold references to them, so we never free them. It's our caller's job to
      * recognize and process these, since their children do need to be dealt
      * with.
      */
     JS_ASSERT(!pn->pn_used);
     JS_ASSERT(!pn->pn_defn);
 
@@ -580,17 +580,17 @@ PushNodeChildren(JSParseNode *pn, NodeSt
         if (pn->pn_left != pn->pn_right)
             stack->pushUnlessNull(pn->pn_left);
         stack->pushUnlessNull(pn->pn_right);
         break;
       case PN_UNARY:
         stack->pushUnlessNull(pn->pn_kid);
         break;
       case PN_NULLARY:
-        /*
+        /* 
          * E4X function namespace nodes are PN_NULLARY, but can appear on use
          * lists.
          */
         return !pn->pn_used && !pn->pn_defn;
     }
 
     return true;
 }
@@ -1118,27 +1118,35 @@ Compiler::compileScript(JSContext *cx, J
     JS_ASSERT(cg.version() == version);
 
     script = JSScript::NewScriptFromCG(cx, &cg);
     if (!script)
         goto out;
 
     JS_ASSERT(script->savedCallerFun == savedCallerFun);
 
-    if (!defineGlobals(cx, globalScope, script))
-        script = NULL;
+    {
+        AutoScriptRooter root(cx, script);
+        if (!defineGlobals(cx, globalScope, script))
+            goto late_error;
+    }
 
   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())
@@ -2604,23 +2612,23 @@ Parser::setFunctionKinds(JSFunctionBox *
  * within the top statement list, which don't take effect unless they are
  * evaluated). Such call objects may acquire bindings that shadow variables
  * defined in enclosing scopes, so any enclosed functions must have their
  * bindings' extensibleParents flags set, and enclosed compiler-created blocks
  * must have their OWN_SHAPE flags set; the comments for
  * js::Bindings::extensibleParents explain why.
  */
 void
-Parser::markExtensibleScopeDescendants(JSFunctionBox *funbox, bool hasExtensibleParent)
+Parser::markExtensibleScopeDescendants(JSFunctionBox *funbox, bool hasExtensibleParent) 
 {
     for (; funbox; funbox = funbox->siblings) {
         /*
          * It would be nice to use FUN_KIND(fun) here to recognize functions
          * that will never consult their parent chains, and thus don't need
-         * their 'extensible parents' flag set. Filed as bug 619750.
+         * their 'extensible parents' flag set. Filed as bug 619750. 
          */
 
         JS_ASSERT(!funbox->bindings.extensibleParents());
         if (hasExtensibleParent)
             funbox->bindings.setExtensibleParents();
 
         if (funbox->kids) {
             markExtensibleScopeDescendants(funbox->kids,
@@ -3216,21 +3224,21 @@ Parser::functionDef(JSAtom *funAtom, Fun
 #endif
     pn->pn_pos.end = tokenStream.currentToken().pos.end;
 
     /*
      * Fruit of the poisonous tree: if a closure calls eval, we consider the
      * parent to call eval. We need this for two reasons: (1) the Jaegermonkey
      * optimizations really need to know if eval is called transitively, and
      * (2) in strict mode, eval called transitively requires eager argument
-     * creation in strict mode parent functions.
+     * creation in strict mode parent functions. 
      *
-     * For the latter, we really only need to propagate callsEval if both
-     * functions are strict mode, but we don't lose much by always propagating.
-     * The only optimization we lose this way is in the case where a function
+     * For the latter, we really only need to propagate callsEval if both 
+     * functions are strict mode, but we don't lose much by always propagating. 
+     * The only optimization we lose this way is in the case where a function 
      * is strict, does not mutate arguments, does not call eval directly, but
      * calls eval transitively.
      */
     if (funtc.callsEval())
         outertc->noteCallsEval();
 
 #if JS_HAS_DESTRUCTURING
     /*
@@ -3719,17 +3727,17 @@ DefineGlobal(JSParseNode *pn, JSCodeGene
             if (funbox ||
                 globalObj != holder ||
                 shape->configurable() ||
                 !shape->hasSlot() ||
                 !shape->hasDefaultGetterOrIsMethod() ||
                 !shape->hasDefaultSetter()) {
                 return true;
             }
-
+            
             def = GlobalScope::GlobalDef(shape->slot);
         } else {
             def = GlobalScope::GlobalDef(atom, funbox);
         }
 
         if (!globalScope->defs.append(def))
             return false;
 
@@ -6758,17 +6766,17 @@ class CompExprTransplanter {
  * A helper for lazily checking for the presence of illegal |yield| or |arguments|
  * tokens inside of generator expressions. This must be done lazily since we don't
  * know whether we're in a generator expression until we see the "for" token after
  * we've already parsed the body expression.
  *
  * Use in any context which may turn out to be inside a generator expression. This
  * includes parenthesized expressions and argument lists, and it includes the tail
  * of generator expressions.
- *
+ * 
  * The guard will keep track of any |yield| or |arguments| tokens that occur while
  * parsing the body. As soon as the parser reaches the end of the body expression,
  * call endBody() to reset the context's state, and then immediately call:
  *
  * - checkValidBody() if this *did* turn out to be a generator expression
  * - maybeNoteGenerator() if this *did not* turn out to be a generator expression
  */
 class GenexpGuard {
@@ -7000,17 +7008,17 @@ CompExprTransplanter::transplant(JSParse
                      * generator) a use of a new placeholder in the generator's
                      * lexdeps.
                      */
                     JSDefinition *dn2 = MakePlaceholder(pn, tc);
                     if (!dn2)
                         return false;
                     dn2->pn_pos = root->pn_pos;
 
-                    /*
+                    /* 
                      * Change all uses of |dn| that lie within the generator's
                      * |yield| expression into uses of dn2.
                      */
                     JSParseNode **pnup = &dn->dn_uses;
                     JSParseNode *pnu;
                     while ((pnu = *pnup) != NULL && pnu->pn_pos >= root->pn_pos) {
                         pnu->pn_lexdef = dn2;
                         dn2->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -282,40 +282,40 @@ Bindings::makeImmutable()
 
 void
 Bindings::trace(JSTracer *trc)
 {
     if (lastBinding)
         MarkShape(trc, lastBinding, "shape");
 }
 
-#ifdef JS_CRASH_DIAGNOSTICS
+} /* namespace js */
 
-void
+static void
 CheckScript(JSScript *script, JSScript *prev)
 {
+#ifdef JS_CRASH_DIAGNOSTICS
     if (script->cookie1 != JS_SCRIPT_COOKIE || script->cookie2 != JS_SCRIPT_COOKIE) {
         crash::StackBuffer<sizeof(JSScript), 0x87> buf1(script);
         crash::StackBuffer<sizeof(JSScript), 0x88> buf2(prev);
         JS_OPT_ASSERT(false);
     }
+#endif
 }
 
-void
+static 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());
+        JS_OPT_ASSERT(script->compartment == owner->compartment());
+#endif
 }
 
-#endif /* JS_CRASH_DIAGNOSTICS */
-
-} /* namespace js */
-
 #if JS_HAS_XDR
 
 enum ScriptBits {
     NoScriptRval,
     SavedCallerFun,
     HasSharps,
     StrictModeCode,
     UsesEval,
@@ -374,16 +374,17 @@ js_XDRScript(JSXDRState *xdr, JSScript *
     }
     JS_ASSERT(nargs != Bindings::BINDING_COUNT_LIMIT);
     JS_ASSERT(nvars != Bindings::BINDING_COUNT_LIMIT);
     JS_ASSERT(nupvars != Bindings::BINDING_COUNT_LIMIT);
 
     EmptyShape *emptyCallShape = EmptyShape::getEmptyCallShape(cx);
     if (!emptyCallShape)
         return false;
+    AutoShapeRooter shapeRoot(cx, emptyCallShape);
 
     Bindings bindings(cx, emptyCallShape);
     AutoBindingsRooter rooter(cx, bindings);
     uint32 nameCount = nargs + nvars + nupvars;
     if (nameCount > 0) {
         struct AutoMark {
           JSArenaPool * const pool;
           void * const mark;
@@ -531,16 +532,18 @@ js_XDRScript(JSXDRState *xdr, JSScript *
         return JS_FALSE;
     if (!JS_XDRUint32(xdr, &nconsts))
         return JS_FALSE;
     if (!JS_XDRUint32(xdr, &encodedClosedCount))
         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,
@@ -552,16 +555,17 @@ js_XDRScript(JSXDRState *xdr, JSScript *
         script->bindings.transfer(cx, &bindings);
 
         script->main += 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))
@@ -728,18 +732,20 @@ 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)
+    if (xdr->mode == JSXDR_DECODE) {
+        js_DestroyScript(cx, script, 1);
         *scriptp = NULL;
+    }
     xdr->script = oldscript;
     return JS_FALSE;
 }
 
 #endif /* JS_HAS_XDR */
 
 bool
 JSPCCounters::init(JSContext *cx, size_t numBytecodes)
@@ -758,37 +764,43 @@ 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) {
-        CheckScriptOwner(script, obj);
-        MarkScript(trc, script, "script");
-    }
+    if (script)
+        js_TraceScript(trc, script, obj);
 }
 
 Class js_ScriptClass = {
     "Script",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
     PropertyStub,         /* addProperty */
     PropertyStub,         /* delProperty */
     PropertyStub,         /* getProperty */
     StrictPropertyStub,   /* setProperty */
     EnumerateStub,
     ResolveStub,
     ConvertStub,
-    NULL,                 /* finalize */
+    script_finalize,
     NULL,                 /* reserved0   */
     NULL,                 /* checkAccess */
     NULL,                 /* call        */
     NULL,                 /* construct   */
     NULL,                 /* xdrObject   */
     NULL,                 /* hasInstance */
     script_trace
 };
@@ -905,23 +917,27 @@ 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, JSVersion version)
 {
     EmptyShape *emptyCallShape = EmptyShape::getEmptyCallShape(cx);
     if (!emptyCallShape)
         return NULL;
+    AutoShapeRooter shapeRoot(cx, emptyCallShape);
 
     size_t size, vectorSize;
+    JSScript *script;
+    uint8 *cursor;
     unsigned constPadding = 0;
 
     uint32 totalClosed = nClosedArgs + nClosedVars;
 
-    size = sizeof(JSAtom *) * natoms;
+    size = sizeof(JSScript) +
+           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)
@@ -939,72 +955,68 @@ JSScript::NewScript(JSContext *cx, uint3
          */
         constPadding = (8 - (size % 8)) % 8;
         size += constPadding + nconsts * sizeof(Value);
     }
 
     size += length * sizeof(jsbytecode) +
             nsrcnotes * sizeof(jssrcnote);
 
-    uint8 *data = static_cast<uint8 *>(cx->malloc_(size));
-    if (!data)
+    script = (JSScript *) cx->malloc_(size);
+    if (!script)
         return NULL;
-    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->ownerObject = JS_NEW_SCRIPT;
 #endif
-    script->data = data;
     script->length = length;
     script->version = version;
     new (&script->bindings) Bindings(cx, emptyCallShape);
 
     if (cx->hasRunOption(JSOPTION_PCCOUNT))
         (void) script->pcCounters.init(cx, length);
 
-    uint8 *cursor = data;
+    uint8 *scriptEnd = reinterpret_cast<uint8 *>(script + 1);
+
+    cursor = scriptEnd;
     if (nobjects != 0) {
-        script->objectsOffset = (uint8)(cursor - data);
+        script->objectsOffset = (uint8)(cursor - scriptEnd);
         cursor += sizeof(JSObjectArray);
     } else {
         script->objectsOffset = JSScript::INVALID_OFFSET;
     }
     if (nupvars != 0) {
-        script->upvarsOffset = (uint8)(cursor - data);
+        script->upvarsOffset = (uint8)(cursor - scriptEnd);
         cursor += sizeof(JSUpvarArray);
     } else {
         script->upvarsOffset = JSScript::INVALID_OFFSET;
     }
     if (nregexps != 0) {
-        script->regexpsOffset = (uint8)(cursor - data);
+        script->regexpsOffset = (uint8)(cursor - scriptEnd);
         cursor += sizeof(JSObjectArray);
     } else {
         script->regexpsOffset = JSScript::INVALID_OFFSET;
     }
     if (ntrynotes != 0) {
-        script->trynotesOffset = (uint8)(cursor - data);
+        script->trynotesOffset = (uint8)(cursor - scriptEnd);
         cursor += sizeof(JSTryNoteArray);
     } else {
         script->trynotesOffset = JSScript::INVALID_OFFSET;
     }
     if (nglobals != 0) {
-        script->globalsOffset = (uint8)(cursor - data);
+        script->globalsOffset = (uint8)(cursor - scriptEnd);
         cursor += sizeof(GlobalSlotArray);
     } else {
         script->globalsOffset = JSScript::INVALID_OFFSET;
     }
-    JS_ASSERT(cursor - data < 0xFF);
+    JS_ASSERT(cursor - scriptEnd < 0xFF);
     if (nconsts != 0) {
-        script->constOffset = (uint8)(cursor - data);
+        script->constOffset = (uint8)(cursor - scriptEnd);
         cursor += sizeof(JSConstArray);
     } else {
         script->constOffset = JSScript::INVALID_OFFSET;
     }
 
     JS_STATIC_ASSERT(sizeof(JSObjectArray) +
                      sizeof(JSUpvarArray) +
                      sizeof(JSObjectArray) +
@@ -1086,17 +1098,22 @@ JSScript::NewScript(JSContext *cx, uint3
         memset(cursor, 0, vectorSize);
         cursor += vectorSize;
     }
 
     script->code = script->main = (jsbytecode *)cursor;
     JS_ASSERT(cursor +
               length * sizeof(jsbytecode) +
               nsrcnotes * sizeof(jssrcnote) ==
-              data + size);
+              (uint8 *)script + size);
+
+    script->compartment = cx->compartment;
+#ifdef CHECK_SCRIPT_OWNER
+    script->owner = cx->thread();
+#endif
 
     JS_APPEND_LINK(&script->links, &cx->compartment->scripts);
 
     JS_ASSERT(script->getVersion() == version);
     return script;
 }
 
 JSScript *
@@ -1125,48 +1142,50 @@ JSScript::NewScriptFromCG(JSContext *cx,
                        cg->atomIndices->count(), cg->objectList.length,
                        upvarIndexCount, cg->regexpList.length,
                        cg->ntrynotes, cg->constList.length(),
                        cg->globalUses.length(), nClosedArgs, nClosedVars, 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;
     memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * 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());
 
     filename = cg->parser->tokenStream.getFilename();
     if (filename) {
         script->filename = SaveScriptFilename(cx, filename);
         if (!script->filename)
-            return NULL;
+            goto bad;
     }
     script->lineno = cg->firstLine;
     if (script->nfixed + cg->maxStackDepth >= JS_BIT(16)) {
         ReportCompileErrorNumber(cx, CG_TS(cg), NULL, JSREPORT_ERROR, JSMSG_NEED_DIET, "script");
-        return NULL;
+        goto bad;
     }
     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()))
-        return NULL;
+        goto bad;
     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());
@@ -1223,46 +1242,55 @@ JSScript::NewScriptFromCG(JSContext *cx,
 #ifdef DEBUG
         if (JSScript::isValidOffset(script->upvarsOffset))
             JS_ASSERT(script->upvars()->length == script->bindings.countUpvars());
         else
             JS_ASSERT(script->bindings.countUpvars() == 0);
 #endif
         fun->u.i.script = script;
         script->setOwnerObject(fun);
+#ifdef CHECK_SCRIPT_OWNER
+        script->owner = NULL;
+#endif
         if (cg->flags & TCF_FUN_HEAVYWEIGHT)
             fun->flags |= JSFUN_HEAVYWEIGHT;
     } 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))
-            return NULL;
+            goto bad;
     }
 
     /* 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::dataSize()
+JSScript::totalSize()
 {
-    uint8 *dataEnd = code + length * sizeof(jsbytecode) + numNotes() * sizeof(jssrcnote);
-    JS_ASSERT(dataEnd > data);
-    return dataEnd - data;
+    return code +
+           length * sizeof(jsbytecode) +
+           numNotes() * sizeof(jssrcnote) -
+           (uint8 *) this;
 }
 
 void
 JSScript::setOwnerObject(JSObject *owner)
 {
 #ifdef JS_CRASH_DIAGNOSTICS
     CheckScriptOwner(this, JS_NEW_SCRIPT);
     ownerObject = owner;
@@ -1281,89 +1309,197 @@ 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)
 {
-    JS_ASSERT(!script->callDestroyHook);
-    if (JSNewScriptHook hook = cx->debugHooks->newScriptHook) {
+    JSNewScriptHook hook;
+
+    hook = cx->debugHooks->newScriptHook;
+    if (hook) {
         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)
 {
-    if (!script->callDestroyHook)
-        return;
+    JSDestroyScriptHook hook;
 
-    if (JSDestroyScriptHook hook = cx->debugHooks->destroyScriptHook)
+    hook = cx->debugHooks->destroyScriptHook;
+    if (hook)
         hook(cx, script, cx->debugHooks->destroyScriptHookData);
-    script->callDestroyHook = false;
     Debugger::onDestroyScript(script);
     JS_ClearScriptTraps(cx, script);
 }
 
-void
-JSScript::finalize(JSContext *cx)
+static void
+DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller)
 {
-    CheckScript(this, NULL);
+    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();
 
-    js_CallDestroyScriptHook(cx, this);
+    /*
+     * 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
 
-    if (principals)
-        JSPRINCIPALS_DROP(cx, principals);
+    /* 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
+    }
 
 #ifdef JS_TRACER
-    if (compartment()->hasTraceMonitor())
-        PurgeScriptFragments(compartment()->traceMonitor(), this);
+    if (script->compartment->hasTraceMonitor())
+        PurgeScriptFragments(script->compartment->traceMonitor(), script);
 #endif
 
 #ifdef JS_METHODJIT
-    mjit::ReleaseScriptCode(cx, this);
+    mjit::ReleaseScriptCode(cx, script);
 #endif
-    JS_REMOVE_LINK(&links);
+    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);
+}
 
-    pcCounters.destroy(cx);
+void
+js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner)
+{
+    JS_ASSERT(cx->runtime->gcRunning);
+    js_CallDestroyScriptHook(cx, script);
+    DestroyScript(cx, script, owner, 100);
+}
 
-    if (sourceMap)
-        cx->free_(sourceMap);
+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)
+{
+    CheckScript(script, NULL);
+    if (owner)
+        CheckScriptOwner(script, owner);
 
 #ifdef JS_CRASH_DIAGNOSTICS
-    if (data)
-        JS_POISON(data, 0xdb, dataSize());
+    JSRuntime *rt = trc->context->runtime;
+    JS_OPT_ASSERT_IF(rt->gcCheckCompartment, script->compartment == rt->gcCheckCompartment);
 #endif
 
-    cx->free_(data);
+    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 (script->u.object)
+        MarkObject(trc, *script->u.object, "object");
+
+    if (IS_GC_MARKING_TRACER(trc) && script->filename)
+        js_MarkScriptFilename(script->filename);
+
+    script->bindings.trace(trc);
+
+#ifdef JS_METHODJIT
+    mjit::TraceScript(trc, script);
+#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 proto, to avoid entraining stuff. Once we no longer use the parent
      * for security checks, then we can clear the parent, too.
      */
     obj->clearProto();
 
+#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;
 
@@ -1635,17 +1771,18 @@ 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(cx->compartment != script->compartment);
+    JS_ASSERT(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);
@@ -1675,17 +1812,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)
--- 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
@@ -353,16 +353,20 @@ 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 {
@@ -416,17 +420,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 : public js::gc::Cell {
+struct JSScript {
     /*
      * 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
@@ -438,22 +442,22 @@ struct JSScript : public js::gc::Cell {
                                uint32 ntrynotes, uint32 nconsts, uint32 nglobals,
                                uint16 nClosedArgs, uint16 nClosedVars, JSVersion version);
 
     static JSScript *NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg);
 
     /* FIXME: bug 586181 */
     JSCList         links;      /* Links for compartment script list */
     jsbytecode      *code;      /* bytecodes and their immediate operands */
+    uint32          length;     /* length of code vector */
+
 #ifdef JS_CRASH_DIAGNOSTICS
     uint32          cookie1;
 #endif
 
-    uint8           *data;      /* pointer to variable-length data array */  
-    uint32          length;     /* length of code vector */
   private:
     uint16          version;    /* JS version under which script was compiled */
 
   public:
     uint16          nfixed;     /* number of slots besides stack operands in
                                    slot array */
   private:
     size_t          callCount_; /* Number of times the script has been called. */
@@ -485,21 +489,21 @@ struct JSScript : public js::gc::Cell {
     bool            warnedAboutTwoArgumentEval:1; /* have warned about use of
                                                      obsolete eval(s, o) in
                                                      this script */
     bool            hasSingletons:1;  /* script has singleton objects */
 #ifdef JS_METHODJIT
     bool            debugMode:1;      /* script was compiled in debug mode */
     bool            singleStepMode:1; /* compile script in single-step mode */
 #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 */
-    JSAtomMap       atomMap;    /* maps immediate index to literal struct */
     uint32          lineno;     /* base line number of script */
     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. */
     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 */
@@ -518,20 +522,22 @@ struct JSScript : public js::gc::Cell {
          *   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 */
+    } u;
 
-        /* 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;
@@ -543,24 +549,17 @@ struct JSScript : public js::gc::Cell {
     // 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
 
-#if JS_BYTES_PER_WORD == 4 && !defined JS_CRASH_DIAGNOSTICS
-    uint32          gcpad;            /* sizeof(JSScript) must be multiple of
-                                         js::gc::Cell::CellSize. */
-#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);
@@ -580,53 +579,53 @@ struct JSScript : public js::gc::Cell {
             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) dataSize();   /* Size of all data sections */
+    JS_FRIEND_API(size_t) totalSize();  /* Size of the JSScript and all 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 *>(data + objectsOffset);
+        return reinterpret_cast<JSObjectArray *>(uintptr_t(this + 1) + objectsOffset);
     }
 
     JSUpvarArray *upvars() {
         JS_ASSERT(isValidOffset(upvarsOffset));
-        return reinterpret_cast<JSUpvarArray *>(data + upvarsOffset);
+        return reinterpret_cast<JSUpvarArray *>(uintptr_t(this + 1) + upvarsOffset);
     }
 
     JSObjectArray *regexps() {
         JS_ASSERT(isValidOffset(regexpsOffset));
-        return reinterpret_cast<JSObjectArray *>(data + regexpsOffset);
+        return reinterpret_cast<JSObjectArray *>(uintptr_t(this + 1) + regexpsOffset);
     }
 
     JSTryNoteArray *trynotes() {
         JS_ASSERT(isValidOffset(trynotesOffset));
-        return reinterpret_cast<JSTryNoteArray *>(data + trynotesOffset);
+        return reinterpret_cast<JSTryNoteArray *>(uintptr_t(this + 1) + trynotesOffset);
     }
 
     js::GlobalSlotArray *globals() {
         JS_ASSERT(isValidOffset(globalsOffset));
-        return reinterpret_cast<js::GlobalSlotArray *>(data + globalsOffset);
+        return reinterpret_cast<js::GlobalSlotArray *>(uintptr_t(this + 1) + globalsOffset);
     }
 
     JSConstArray *consts() {
         JS_ASSERT(isValidOffset(constOffset));
-        return reinterpret_cast<JSConstArray *>(data + constOffset);
+        return reinterpret_cast<JSConstArray *>(uintptr_t(this + 1) + constOffset);
     }
 
     JSAtom *getAtom(size_t index) {
         JS_ASSERT(index < atomMap.length);
         return atomMap.vector[index];
     }
 
     JSObject *getObject(size_t index) {
@@ -675,22 +674,18 @@ struct JSScript : public js::gc::Cell {
     }
 
     uint32 getClosedVar(uint32 index) {
         JS_ASSERT(index < nClosedVars);
         return closedSlots[nClosedArgs + index];
     }
 
     void copyClosedSlotsTo(JSScript *other);
-
-    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;
 }
@@ -729,41 +724,34 @@ 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);
 
-namespace js {
-
-#ifdef JS_CRASH_DIAGNOSTICS
-
-void
-CheckScriptOwner(JSScript *script, JSObject *owner);
-
-void
-CheckScript(JSScript *script, JSScript *prev);
-
-#else
+/*
+ * 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);
 
-inline void
-CheckScriptOwner(JSScript *script, JSObject *owner)
-{
-}
+extern void
+js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner);
 
-inline void
-CheckScript(JSScript *script, JSScript *prev)
-{
-}
-
-#endif /* !JS_CRASH_DIAGNOSTICS */
-
-} /* namespace js */
+/*
+ * 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);
 
 extern void
 js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner);
 
 extern JSObject *
 js_NewScriptObject(JSContext *cx, JSScript *script);
 
 /*
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -16970,17 +16970,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/jsxdrapi.cpp
+++ b/js/src/jsxdrapi.cpp
@@ -714,18 +714,20 @@ 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)
+        if (!*scriptObjp) {
+            js_DestroyScript(xdr->cx, script, 8);
             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
@@ -142,17 +142,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
@@ -419,17 +419,17 @@ mjit::Compiler::finishThisUp(JITScript *
 
     size_t codeSize = masm.size() +
                       stubcc.size() +
                       doubleList.length() * sizeof(double) +
                       jumpTableOffsets.length() * sizeof(void *);
 
     JSC::ExecutablePool *execPool;
     uint8 *result =
-        (uint8 *)script->compartment()->jaegerCompartment()->execAlloc()->alloc(codeSize, &execPool);
+        (uint8 *)script->compartment->jaegerCompartment()->execAlloc()->alloc(codeSize, &execPool);
     if (!result) {
         js_ReportOutOfMemory(cx);
         return Compile_Error;
     }
     JS_ASSERT(execPool);
     JSC::ExecutableAllocator::makeWritable(result, codeSize);
     masm.executableCopy(result);
     stubcc.masm.executableCopy(result + masm.size());
@@ -4049,17 +4049,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/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -114,24 +114,24 @@ static const size_t STUB_CALLS_FOR_OP_CO
 static uint32 StubCallsForOp[STUB_CALLS_FOR_OP_COUNT];
 #endif
 
 extern "C" void JaegerTrampolineReturn();
 
 extern "C" void JS_FASTCALL
 PushActiveVMFrame(VMFrame &f)
 {
-    f.entryfp->script()->compartment()->jaegerCompartment()->pushActiveFrame(&f);
+    f.entryfp->script()->compartment->jaegerCompartment()->pushActiveFrame(&f);
     f.regs.fp()->setNativeReturnAddress(JS_FUNC_TO_DATA_PTR(void*, JaegerTrampolineReturn));
 }
 
 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)
 {
     /* Restored on exit from EnterMethodJIT. */
     f.cx->stack.repointRegs(&f.regs);
 }
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -123,17 +123,17 @@ Recompiler::recompile()
     Vector<PatchableAddress> normalPatches(cx);
     Vector<PatchableAddress> ctorPatches(cx);
 
     StackFrame *firstCtorFrame = NULL;
     StackFrame *firstNormalFrame = NULL;
 
     // 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();
         for (StackFrame *fp = f->fp(); fp != end; fp = fp->prev()) {
             // Remember the latest frame for each type of JIT'd code, so the
             // compiler will have a frame to re-JIT from.
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -231,17 +231,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))
@@ -441,17 +441,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);
 
@@ -799,17 +799,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
@@ -1737,18 +1737,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;
@@ -1770,17 +1770,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;
@@ -1789,17 +1789,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
@@ -2234,17 +2234,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;
@@ -2269,17 +2269,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;
@@ -2297,27 +2297,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");
@@ -2667,18 +2667,18 @@ EvaluateInScope(JSContext *cx, JSObject 
     JSScript *script = Compiler::compileScript(cx, scobj, fp, fp->scopeChain().principals(cx),
                                                TCF_COMPILE_N_GO, chars, length,
                                                filename, lineno, cx->findVersion(),
                                                NULL, UpvarCookie::UPVAR_LEVEL_LIMIT);
 
     if (!script)
         return false;
 
-    JSBool ok = Execute(cx, script, *scobj, fp->thisValue(), EXECUTE_DEBUG, fp, rval);
-    js_CallDestroyScriptHook(cx, script);
+    bool ok = Execute(cx, script, *scobj, fp->thisValue(), EXECUTE_DEBUG, fp, rval);
+    js_DestroyScript(cx, script, 6);
     return ok;
 }
 
 }
 
 enum EvalBindingsMode { WithoutBindings, WithBindings };
 
 static JSBool
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -543,24 +543,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.h
+++ b/js/src/vm/Stack.h
@@ -826,17 +826,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,24 +840,21 @@ nsXPConnect::Traverse(void *p, nsCycleCo
             else
             {
                 JS_snprintf(name, sizeof(name), "JS Object (%s)",
                             clazz->name);
             }
         }
         else
         {
-            static const char trace_types[][7] = {
+            static const char trace_types[JSTRACE_LIMIT][7] = {
                 "Object",
                 "String",
-                "Shape",
-                "Script",
                 "Xml"
             };
-            JS_STATIC_ASSERT(JS_ARRAY_LENGTH(trace_types) == JSTRACE_LIMIT);
             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;
 
@@ -845,17 +845,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)))
@@ -864,17 +864,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();
                             }
                         }
                     }
@@ -1036,17 +1036,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)
 {
@@ -1244,16 +1244,52 @@ 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
+
+PRInt64
+GetCompartmentMjitCodeSize(JSCompartment *c)
+{
+    return c->getMjitCodeSize();
+}
+
+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;
@@ -1295,18 +1331,20 @@ 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
-    curr->mjitCode = compartment->getMjitCodeSize();
+    curr->mjitCode = GetCompartmentMjitCodeSize(compartment);
+    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
 }
@@ -1328,53 +1366,40 @@ ArenaCallback(JSContext *cx, void *vdata
 }
 
 void
 CellCallback(JSContext *cx, void *vdata, void *thing, size_t traceKind,
              size_t thingSize)
 {
     IterateData *data = static_cast<IterateData *>(vdata);
     CompartmentStats *curr = data->currCompartmentStats;
-    curr->gcHeapKinds[traceKind] += thingSize;
-    switch (traceKind)
+    if(traceKind == JSTRACE_OBJECT)
+    {
+        curr->gcHeapObjects += thingSize;
+        JSObject *obj = static_cast<JSObject *>(thing);
+        if(obj->hasSlotsArray())
+            curr->objectSlots += obj->numSlots() * sizeof(js::Value);
+    }
+    else if(traceKind == JSTRACE_STRING)
     {
-        case JSTRACE_OBJECT:
-        {
-            JSObject *obj = static_cast<JSObject *>(thing);
-            if(obj->hasSlotsArray())
-                curr->objectSlots += obj->numSlots() * 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;
-        }
-        default:
-        {
-            JS_ASSERT(traceKind == JSTRACE_XML);
-            break;
-        }
+        curr->gcHeapStrings += thingSize;
+        JSString *str = static_cast<JSString *>(thing);
+        curr->stringChars += str->charsHeapSize();
+    }
+    else if(traceKind == JSTRACE_SHAPE)
+    {
+        curr->gcHeapShapes += thingSize;
+        js::Shape *shape = static_cast<js::Shape *>(thing);
+        if(shape->hasTable())
+            curr->propertyTables += shape->getTable()->sizeOf();
+    }
+    else
+    {
+        JS_ASSERT(traceKind == JSTRACE_XML);
+        curr->gcHeapXml += thingSize;
     }
     // 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,
@@ -1621,34 +1646,33 @@ CollectCompartmentStatsForRuntime(JSRunt
     data->gcHeapArenaUnused = 0;
 
     for(PRUint32 index = 0;
         index < data->compartmentStatsVector.Length();
         index++)
     {
         CompartmentStats &stats = data->compartmentStatsVector[index];
 
-        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->gcHeapChunkDirtyUnused -=
+            stats.gcHeapArenaHeaders + stats.gcHeapArenaPadding +
+            stats.gcHeapArenaUnused +
+            stats.gcHeapObjects + stats.gcHeapStrings +
+            stats.gcHeapShapes + stats.gcHeapXml;
+        
         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;
 
@@ -1680,50 +1704,42 @@ 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.gcHeapKinds[JSTRACE_OBJECT],
+                       JS_GC_HEAP_KIND, stats.gcHeapObjects,
     "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.gcHeapKinds[JSTRACE_STRING],
+                       JS_GC_HEAP_KIND, stats.gcHeapStrings,
     "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/shapes"),
-                       JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_SHAPE],
+                       JS_GC_HEAP_KIND, stats.gcHeapShapes,
     "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/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/xml"),
-                       JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_XML],
+                       JS_GC_HEAP_KIND, stats.gcHeapXml,
     "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, "
@@ -1747,20 +1763,22 @@ ReportCompartmentStats(const Compartment
                                               "property-tables"),
                        nsIMemoryReporter::KIND_HEAP, stats.propertyTables,
     "Memory allocated for the compartment's property tables.  A property "
     "table is an internal data structure that makes JavaScript property "
     "accesses fast.",
                        callback, closure);
 
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
-                                              "script-data"),
-                       nsIMemoryReporter::KIND_HEAP, stats.scriptData,
-    "Memory allocated for JSScript bytecode and various variable-length "
-    "tables.",
+                                              "scripts"),
+                       nsIMemoryReporter::KIND_HEAP, stats.scripts,
+    "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.",
                        callback, closure);
 
 #ifdef JS_METHODJIT
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
                                               "mjit-code"),
                        nsIMemoryReporter::KIND_NONHEAP, stats.mjitCode,
     "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
@@ -196,23 +196,26 @@ struct CompartmentStats
 {
     CompartmentStats(JSContext *cx, JSCompartment *c);
 
     nsCString name;
     PRInt64 gcHeapArenaHeaders;
     PRInt64 gcHeapArenaPadding;
     PRInt64 gcHeapArenaUnused;
 
-    PRInt64 gcHeapKinds[JSTRACE_LIMIT];
+    PRInt64 gcHeapObjects;
+    PRInt64 gcHeapStrings;
+    PRInt64 gcHeapShapes;
+    PRInt64 gcHeapXml;
 
     PRInt64 objectSlots;
     PRInt64 stringChars;
     PRInt64 propertyTables;
-    PRInt64 scriptData;
 
+    PRInt64 scripts;
 #ifdef JS_METHODJIT
     PRInt64 mjitCode;
     PRInt64 mjitData;
 #endif
 #ifdef JS_TRACER
     PRInt64 tjitCode;
     PRInt64 tjitDataAllocatorsMain;
     PRInt64 tjitDataAllocatorsReserve;