diff --git a/js/src/gnuplot/gcTimer.gnu b/js/src/gnuplot/gcTimer.gnu index d12e32ff9bdb..b8b3ac9d85ea 100644 --- a/js/src/gnuplot/gcTimer.gnu +++ b/js/src/gnuplot/gcTimer.gnu @@ -20,4 +20,5 @@ plot 'gcTimer.dat' using 2 title columnheader(2), \ '' u 7 title columnheader(7) with points, \ '' u 8 title columnheader(8) with points, \ '' u 9 title columnheader(9) with points, \ -'' u 10 title columnheader(10) with points +'' u 10 title columnheader(10) with points, \ +'' u 11 title columnheader(11) with points diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 59829e44c3d7..e144dbd6628a 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1238,7 +1238,7 @@ JS_EnterCrossCompartmentCallScript(JSContext *cx, JSScript *target) 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; @@ -1288,7 +1288,7 @@ bool AutoEnterScriptCompartment::enter(JSContext *cx, JSScript *target) { JS_ASSERT(!call); - if (cx->compartment == target->compartment) { + if (cx->compartment == target->compartment()) { call = reinterpret_cast(1); return true; } @@ -3226,7 +3226,6 @@ LookupResult(JSContext *cx, JSObject *obj, JSObject *obj2, jsid id, Shape *shape = (Shape *) prop; if (shape->isMethod()) { - AutoShapeRooter root(cx, shape); vp->setObject(shape->methodObject()); return !!obj2->methodReadBarrier(cx, *shape, vp); } @@ -4757,9 +4756,7 @@ CompileUCFunctionForPrincipalsCommon(JSContext *cx, JSObject *obj, fun = NULL; goto out2; } - AutoShapeRooter shapeRoot(cx, emptyCallShape); - AutoObjectRooter tvr(cx, FUN_OBJECT(fun)); MUST_FLOW_THROUGH("out"); Bindings bindings(cx, emptyCallShape); @@ -4868,8 +4865,8 @@ JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, uintN inde 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, @@ -4958,8 +4955,8 @@ EvaluateUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj, 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; } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index dc8ed256dfb3..5d0caaefd4c7 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1618,6 +1618,7 @@ JS_SetExtraGCRoots(JSRuntime *rt, JSTraceDataOp traceOp, void *data); #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 diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index e5227f1b3e94..26506a49ebf8 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1467,9 +1467,9 @@ class AutoGCRooter { enum { JSVAL = -1, /* js::AutoValueRooter */ - SHAPE = -2, /* js::AutoShapeRooter */ + BINDINGS = -2, /* js::Bindings */ PARSER = -3, /* js::Parser */ - SCRIPT = -4, /* js::AutoScriptRooter */ + SHAPEVECTOR = -4, /* js::AutoShapeVector */ ENUMERATOR = -5, /* js::AutoEnumStateRooter */ IDARRAY = -6, /* js::AutoIdArray */ DESCRIPTORS = -7, /* js::AutoPropDescArrayRooter */ @@ -1480,9 +1480,7 @@ class AutoGCRooter { VALVECTOR = -12, /* js::AutoValueVector */ DESCRIPTOR = -13, /* js::AutoPropertyDescriptorRooter */ STRING = -14, /* js::AutoStringRooter */ - IDVECTOR = -15, /* js::AutoIdVector */ - BINDINGS = -16, /* js::Bindings */ - SHAPEVECTOR = -17 /* js::AutoShapeVector */ + IDVECTOR = -15 /* js::AutoIdVector */ }; private: @@ -1653,43 +1651,6 @@ class AutoArrayRooter : private AutoGCRooter { 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: diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index 674b73f755b9..6968c91b4a4c 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -191,7 +191,7 @@ class CompartmentChecker void check(JSScript *script) { if (script) { - check(script->compartment); + check(script->compartment()); if (script->u.object) check(script->u.object); } diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 2d528964d33d..01b96eb492f0 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -101,7 +101,7 @@ JSCompartment::JSCompartment(JSRuntime *rt) { JS_INIT_CLIST(&scripts); - PodArrayZero(scriptsToGC); + PodArrayZero(evalCache); } JSCompartment::~JSCompartment() @@ -122,8 +122,8 @@ JSCompartment::~JSCompartment() Foreground::delete_(watchpointMap); #ifdef DEBUG - for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i) - JS_ASSERT(!scriptsToGC[i]); + for (size_t i = 0; i != JS_ARRAY_LENGTH(evalCache); ++i) + JS_ASSERT(!evalCache[i]); #endif } @@ -549,8 +549,20 @@ JSCompartment::purge(JSContext *cx) freeLists.purge(); dtoaCache.purge(); - /* Destroy eval'ed scripts. */ - js_DestroyScriptsToGC(cx, this); + /* + * Clear the hash and reset all evalHashLink to null before the GC. This + * way MarkChildren(trc, JSScript *) can assume that JSScript::u.object is + * not null when we have script owned by an object and not from the eval + * cache. + */ + for (size_t i = 0; i != JS_ARRAY_LENGTH(evalCache); ++i) { + for (JSScript **listHeadp = &evalCache[i]; *listHeadp; ) { + JSScript *script = *listHeadp; + JS_ASSERT(GetGCThingTraceKind(script) == JSTRACE_SCRIPT); + *listHeadp = NULL; + listHeadp = &script->u.evalHashLink; + } + } nativeIterCache.purge(); toSourceCache.destroyIfConstructed(); @@ -634,7 +646,7 @@ 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; @@ -772,7 +784,7 @@ 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; diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index e351cafe095b..d2018b85405b 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -298,10 +298,11 @@ 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 { @@ -414,7 +415,7 @@ struct JS_FRIEND_API(JSCompartment) { public: /* Hashed lists of scripts created by eval to garbage-collect. */ - JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE]; + JSScript *evalCache[JS_EVAL_CACHE_SIZE]; void *data; bool active; // GC flag, whether there are active frames @@ -527,6 +528,7 @@ struct JS_FRIEND_API(JSCompartment) { 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); @@ -620,7 +622,6 @@ struct JS_FRIEND_API(JSCompartment) { js::WatchpointMap *watchpointMap; }; -#define JS_SCRIPTS_TO_GC(cx) ((cx)->compartment->scriptsToGC) #define JS_PROPERTY_TREE(cx) ((cx)->compartment->propertyTree) /* diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index d59b21b0031e..95629cf3efea 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -212,7 +212,7 @@ 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) { @@ -242,7 +242,7 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler handle 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)); @@ -252,7 +252,7 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler handle 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); } @@ -260,7 +260,7 @@ 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) @@ -273,7 +273,7 @@ JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JS_PUBLIC_API(void) JS_ClearScriptTraps(JSContext *cx, JSScript *script) { - script->compartment->clearTraps(cx, script); + script->compartment()->clearTraps(cx, script); } JS_PUBLIC_API(void) diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 7c1476cca1a5..5082a8bf2fd2 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -202,7 +202,6 @@ ArgumentsObject::create(JSContext *cx, uint32 argc, JSObject &callee) EmptyShape *emptyArgumentsShape = EmptyShape::getEmptyArgumentsShape(cx); if (!emptyArgumentsShape) return NULL; - AutoShapeRooter shapeRoot(cx, emptyArgumentsShape); ArgumentsData *data = (ArgumentsData *) cx->malloc_(offsetof(ArgumentsData, slots) + argc * sizeof(Value)); @@ -1599,9 +1598,6 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp) 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); } @@ -1670,30 +1666,16 @@ fun_trace(JSTracer *trc, JSObject *obj) if (fun->atom) MarkString(trc, fun->atom, "atom"); - if (fun->isInterpreted() && fun->script()) - js_TraceScript(trc, fun->script(), obj); + if (fun->isInterpreted() && fun->script()) { + CheckScriptOwner(fun->script(), obj); + MarkScript(trc, fun->script(), "script"); + } } static void fun_finalize(JSContext *cx, JSObject *obj) { - /* Ignore newborn function objects. */ - JSFunction *fun = obj->getFunctionPrivate(); - if (!fun) - return; - - /* Cloned function objects may be flat closures with upvars to free. */ - if (fun != obj) { - if (fun->isFlatClosure() && fun->script()->bindings.hasUpvars()) - cx->free_((void *) obj->getFlatClosureUpvars()); - return; - } - - /* - * Null-check fun->script() because the parser sets interpreted very early. - */ - if (fun->isInterpreted() && fun->script()) - js_DestroyScriptFromGC(cx, fun->script(), obj); + obj->finalizeUpvarsIfFlatClosure(); } /* @@ -1705,7 +1687,8 @@ JS_PUBLIC_DATA(Class) js_FunctionClass = { js_Function_str, JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(JSFunction::CLASS_RESERVED_SLOTS) | - JSCLASS_HAS_CACHED_PROTO(JSProto_Function), + JSCLASS_HAS_CACHED_PROTO(JSProto_Function) | + JSCLASS_CONCURRENT_FINALIZER, PropertyStub, /* addProperty */ PropertyStub, /* delProperty */ PropertyStub, /* getProperty */ @@ -2155,7 +2138,6 @@ Function(JSContext *cx, uintN argc, Value *vp) EmptyShape *emptyCallShape = EmptyShape::getEmptyCallShape(cx); if (!emptyCallShape) return false; - AutoShapeRooter shapeRoot(cx, emptyCallShape); Bindings bindings(cx, emptyCallShape); AutoBindingsRooter root(cx, bindings); @@ -2372,9 +2354,6 @@ js_InitFunctionClass(JSContext *cx, JSObject *obj) 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); @@ -2477,8 +2456,8 @@ js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, 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); @@ -2486,9 +2465,6 @@ js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, 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); } diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index a40f90c9e671..49fa88f92dd0 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -112,13 +112,14 @@ using namespace js::gc; JS_STATIC_ASSERT(JSTRACE_OBJECT == 0); JS_STATIC_ASSERT(JSTRACE_STRING == 1); JS_STATIC_ASSERT(JSTRACE_SHAPE == 2); -JS_STATIC_ASSERT(JSTRACE_XML == 3); +JS_STATIC_ASSERT(JSTRACE_SCRIPT == 3); +JS_STATIC_ASSERT(JSTRACE_XML == 4); /* * 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_SHAPE + 1 == JSTRACE_XML); +JS_STATIC_ASSERT(JSTRACE_SCRIPT + 1 == JSTRACE_XML); namespace js { namespace gc { @@ -149,6 +150,7 @@ const uint8 GCThingSizeMap[] = { 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 @@ -196,6 +198,10 @@ template 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); @@ -787,6 +793,9 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w) case FINALIZE_SHAPE: test = MarkArenaPtrConservatively(trc, aheader, addr); break; + case FINALIZE_SCRIPT: + test = MarkArenaPtrConservatively(trc, aheader, addr); + break; #if JS_HAS_XML_SUPPORT case FINALIZE_XML: test = MarkArenaPtrConservatively(trc, aheader, addr); @@ -1247,6 +1256,7 @@ ArenaList::finalizeLater(JSContext *cx) 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); @@ -1258,7 +1268,13 @@ ArenaList::finalizeLater(JSContext *cx) JS_ASSERT(backgroundFinalizeState == BFS_DONE || backgroundFinalizeState == BFS_JUST_FINISHED); - if (head && cx->gcBackgroundFree && cx->gcBackgroundFree->finalizeVector.append(head)) { + if (head && cx->gcBackgroundFree) { + /* + * To ensure the finalization order even during the background GC we + * must use infallibleAppend so arenas scheduled for background + * finalization would not be finalized now if the append fails. + */ + cx->gcBackgroundFree->finalizeVector.infallibleAppend(head); head = NULL; cursor = &head; backgroundFinalizeState = BFS_RUN; @@ -1299,6 +1315,9 @@ ArenaList::backgroundFinalize(JSContext *cx, ArenaHeader *listHead) case FINALIZE_OBJECT16_BACKGROUND: FinalizeArenas(cx, &listHead); break; + case FINALIZE_FUNCTION: + FinalizeArenas(cx, &listHead); + break; case FINALIZE_STRING: FinalizeArenas(cx, &listHead); break; @@ -1459,6 +1478,8 @@ RefillFinalizableFreeList(JSContext *cx, unsigned thingKind) return RefillTypedFreeList(cx, thingKind); case FINALIZE_SHAPE: return RefillTypedFreeList(cx, thingKind); + case FINALIZE_SCRIPT: + return RefillTypedFreeList(cx, thingKind); #if JS_HAS_XML_SUPPORT case FINALIZE_XML: return RefillTypedFreeList(cx, thingKind); @@ -1662,8 +1683,8 @@ js_TraceStackFrame(JSTracer *trc, StackFrame *fp) return; if (fp->hasArgsObj()) MarkObject(trc, fp->argsObj(), "arguments"); - js_TraceScript(trc, fp->script(), NULL); - fp->script()->compartment->active = true; + MarkScript(trc, fp->script(), "script"); + fp->script()->compartment()->active = true; MarkValue(trc, fp->returnValue(), "rval"); } @@ -1688,19 +1709,10 @@ AutoGCRooter::trace(JSTracer *trc) MarkValue(trc, static_cast(this)->val, "js::AutoValueRooter.val"); return; - case SHAPE: - MarkShape(trc, static_cast(this)->shape, "js::AutoShapeRooter.val"); - return; - case PARSER: static_cast(this)->trace(trc); return; - case SCRIPT: - if (JSScript *script = static_cast(this)->script) - js_TraceScript(trc, script, NULL); - return; - case ENUMERATOR: static_cast(this)->trace(trc); return; @@ -1956,21 +1968,6 @@ MaybeGC(JSContext *cx) } /* 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) { @@ -1980,7 +1977,6 @@ JSCompartment::finalizeObjectArenaLists(JSContext *cx) arenas[FINALIZE_OBJECT8]. finalizeNow(cx); arenas[FINALIZE_OBJECT12].finalizeNow(cx); arenas[FINALIZE_OBJECT16].finalizeNow(cx); - arenas[FINALIZE_FUNCTION].finalizeNow(cx); #ifdef JS_THREADSAFE arenas[FINALIZE_OBJECT0_BACKGROUND]. finalizeLater(cx); @@ -1991,6 +1987,17 @@ JSCompartment::finalizeObjectArenaLists(JSContext *cx) arenas[FINALIZE_OBJECT16_BACKGROUND].finalizeLater(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(cx); +#else + arenas[FINALIZE_FUNCTION].finalizeNow(cx); +#endif + #if JS_HAS_XML_SUPPORT arenas[FINALIZE_XML].finalizeNow(cx); #endif @@ -2015,6 +2022,12 @@ JSCompartment::finalizeShapeArenaLists(JSContext *cx) arenas[FINALIZE_SHAPE].finalizeNow(cx); } +void +JSCompartment::finalizeScriptArenaLists(JSContext *cx) +{ + arenas[FINALIZE_SCRIPT].finalizeNow(cx); +} + #ifdef JS_THREADSAFE namespace js { @@ -2084,6 +2097,15 @@ GCHelperThread::threadLoop(JSRuntime *rt) } } +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) { @@ -2127,6 +2149,11 @@ 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); @@ -2343,6 +2370,8 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM GCTIMESTAMP(sweepObjectEnd); comp->finalizeStringArenaLists(cx); GCTIMESTAMP(sweepStringEnd); + comp->finalizeScriptArenaLists(cx); + GCTIMESTAMP(sweepScriptEnd); comp->finalizeShapeArenaLists(cx); GCTIMESTAMP(sweepShapeEnd); Probes::GCEndSweepPhase(comp); @@ -2368,6 +2397,12 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM 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); @@ -2646,8 +2681,8 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIMER_P JS_ASSERT(!cx->gcBackgroundFree); rt->gcHelperThread.waitBackgroundSweepEnd(rt); if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) { - cx->gcBackgroundFree = &rt->gcHelperThread; - cx->gcBackgroundFree->setContext(cx); + if (rt->gcHelperThread.prepareForBackgroundSweep(cx)) + cx->gcBackgroundFree = &rt->gcHelperThread; } #endif MarkAndSweep(cx, comp, gckind GCTIMER_ARG); diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 74957448e965..d1f019fa0bbe 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -98,6 +98,7 @@ enum FinalizeKind { FINALIZE_FUNCTION, FINALIZE_FUNCTION_AND_OBJECT_LAST = FINALIZE_FUNCTION, FINALIZE_SHAPE, + FINALIZE_SCRIPT, #if JS_HAS_XML_SUPPORT FINALIZE_XML, #endif @@ -107,6 +108,12 @@ enum FinalizeKind { 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; @@ -755,12 +762,12 @@ Cell::compartment() const return arenaHeader()->compartment; } -#define JSTRACE_XML 3 +#define JSTRACE_XML 4 /* * One past the maximum trace kind. */ -#define JSTRACE_LIMIT 4 +#define JSTRACE_LIMIT 5 /* * Lower limit after which we limit the heap growth @@ -797,6 +804,7 @@ GetFinalizableTraceKind(size_t thingKind) 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 @@ -1191,10 +1199,6 @@ js_WaitForGC(JSRuntime *rt); #endif -extern void -js_DestroyScriptsToGC(JSContext *cx, JSCompartment *comp); - - namespace js { #ifdef JS_THREADSAFE @@ -1270,7 +1274,7 @@ class GCHelperThread { replenishAndFreeLater(ptr); } - void setContext(JSContext *context) { cx = context; } + bool prepareForBackgroundSweep(JSContext *context); }; #endif /* JS_THREADSAFE */ @@ -1492,7 +1496,7 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str); #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_SHAPE) +# define JS_IS_VALID_TRACE_KIND(kind) ((uint32)(kind) <= JSTRACE_SCRIPT) #endif namespace js { diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index d79cf37059fd..6567245baa74 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -255,6 +255,12 @@ js_NewGCShape(JSContext *cx) return NewGCThing(cx, js::gc::FINALIZE_SHAPE, sizeof(js::Shape)); } +inline JSScript * +js_NewGCScript(JSContext *cx) +{ + return NewGCThing(cx, js::gc::FINALIZE_SCRIPT, sizeof(JSScript)); +} + #if JS_HAS_XML_SUPPORT extern JSXML * js_NewGCXML(JSContext *cx); diff --git a/js/src/jsgcmark.cpp b/js/src/jsgcmark.cpp index 93766e5cf26f..83751097d384 100644 --- a/js/src/jsgcmark.cpp +++ b/js/src/jsgcmark.cpp @@ -46,6 +46,7 @@ #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 @@ -94,6 +95,9 @@ 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); @@ -191,6 +195,15 @@ MarkShape(JSTracer *trc, const Shape *shape, const char *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) @@ -256,6 +269,21 @@ PushMarkStack(GCMarker *gcmarker, const Shape *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++) { @@ -329,22 +357,24 @@ MarkKind(JSTracer *trc, void *thing, uint32 kind) JS_ASSERT(thing); JS_ASSERT(kind == GetGCThingTraceKind(thing)); switch (kind) { - case JSTRACE_OBJECT: - Mark(trc, reinterpret_cast(thing)); - break; - case JSTRACE_STRING: - MarkString(trc, reinterpret_cast(thing)); - break; - case JSTRACE_SHAPE: - Mark(trc, reinterpret_cast(thing)); - break; + default: + JS_ASSERT(kind == JSTRACE_OBJECT); + Mark(trc, static_cast(thing)); + break; + case JSTRACE_STRING: + MarkString(trc, static_cast(thing)); + break; + case JSTRACE_SHAPE: + Mark(trc, static_cast(thing)); + break; + case JSTRACE_SCRIPT: + Mark(trc, static_cast(thing)); + break; #if JS_HAS_XML_SUPPORT - case JSTRACE_XML: - Mark(trc, reinterpret_cast(thing)); - break; + case JSTRACE_XML: + Mark(trc, static_cast(thing)); + break; #endif - default: - JS_ASSERT(false); } } @@ -469,6 +499,12 @@ 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) { @@ -765,6 +801,54 @@ restart: 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) @@ -815,22 +899,28 @@ JS_PUBLIC_API(void) JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind) { switch (kind) { - case JSTRACE_OBJECT: - MarkChildren(trc, (JSObject *)thing); + default: + JS_ASSERT(kind == JSTRACE_OBJECT); + MarkChildren(trc, static_cast(thing)); break; case JSTRACE_STRING: - MarkChildren(trc, (JSString *)thing); + MarkChildren(trc, static_cast(thing)); break; case JSTRACE_SHAPE: - MarkChildren(trc, (js::Shape *)thing); + MarkChildren(trc, static_cast(thing)); + break; + + case JSTRACE_SCRIPT: + MarkChildren(trc, static_cast(thing)); break; #if JS_HAS_XML_SUPPORT case JSTRACE_XML: - MarkChildren(trc, (JSXML *)thing); + MarkChildren(trc, static_cast(thing)); break; #endif } } + diff --git a/js/src/jsgcmark.h b/js/src/jsgcmark.h index 03fb915215e8..eedacc066b7d 100644 --- a/js/src/jsgcmark.h +++ b/js/src/jsgcmark.h @@ -77,10 +77,10 @@ void MarkShape(JSTracer *trc, const Shape *shape, const char *name); void -MarkXML(JSTracer *trc, JSXML *xml, const char *name); +MarkScript(JSTracer *trc, JSScript *script, const char *name); void -MarkAtomRange(JSTracer *trc, size_t len, JSAtom **vec, const char *name); +MarkXML(JSTracer *trc, JSXML *xml, const char *name); void MarkObjectRange(JSTracer *trc, size_t len, JSObject **vec, const char *name); @@ -153,6 +153,9 @@ 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); @@ -165,6 +168,9 @@ MarkChildren(JSTracer *trc, JSString *str); void MarkChildren(JSTracer *trc, const Shape *shape); +void +MarkChildren(JSTracer *trc, JSScript *script); + void MarkChildren(JSTracer *trc, JSXML *xml); diff --git a/js/src/jsgcstats.cpp b/js/src/jsgcstats.cpp index 5ae2f89c1974..2c21d59c4c25 100644 --- a/js/src/jsgcstats.cpp +++ b/js/src/jsgcstats.cpp @@ -258,7 +258,8 @@ GCTimer::finish(bool lastGC) double sweepTime = TIMEDIFF(startSweep, sweepDestroyEnd); double sweepObjTime = TIMEDIFF(startSweep, sweepObjectEnd); double sweepStringTime = TIMEDIFF(sweepObjectEnd, sweepStringEnd); - double sweepShapeTime = TIMEDIFF(sweepStringEnd, sweepShapeEnd); + double sweepScriptTime = TIMEDIFF(sweepStringEnd, sweepScriptEnd); + double sweepShapeTime = TIMEDIFF(sweepScriptEnd, sweepShapeEnd); double destroyTime = TIMEDIFF(sweepShapeEnd, sweepDestroyEnd); double endTime = TIMEDIFF(sweepDestroyEnd, end); @@ -275,6 +276,7 @@ GCTimer::finish(bool lastGC) info.sweepTime = sweepTime; info.sweepObjTime = sweepObjTime; info.sweepStringTime = sweepStringTime; + info.sweepScriptTime = sweepScriptTime; info.sweepShapeTime = sweepShapeTime; info.destroyTime = destroyTime; info.endTime = endTime; @@ -297,7 +299,7 @@ GCTimer::finish(bool lastGC) JS_ASSERT(gcFile); fullFormat = true; fprintf(gcFile, " AppTime, Total, Wait, Mark, Sweep, FinObj," - " FinStr, SwShapes, Destroy, End, +Chu, -Chu, T, Reason\n"); + " FinStr, SwScripts, SwShapes, Destroy, End, +Chu, -Chu, T, Reason\n"); } } @@ -307,10 +309,10 @@ GCTimer::finish(bool lastGC) TIMEDIFF(startMark, startSweep), TIMEDIFF(startSweep, sweepDestroyEnd)); } else { - /* App , Tot , Wai , Mar , Swe , FiO , FiS , SwS , Des , End */ - fprintf(gcFile, "%12.0f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %8.1f, %6.1f, %6.1f, ", + /* App , Tot , Wai , Mar , Swe , FiO , FiS , SwScr , SwS , Des , End */ + fprintf(gcFile, "%12.0f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %8.1f, %6.1f, %6.1f, ", appTime, gcTime, waitTime, markTime, sweepTime, sweepObjTime, sweepStringTime, - sweepShapeTime, destroyTime, endTime); + sweepScriptTime, sweepShapeTime, destroyTime, endTime); fprintf(gcFile, "%4d, %4d,", newChunkCount, destroyChunkCount); fprintf(gcFile, " %s, %s\n", isCompartmental ? "C" : "G", gcReasons[gcReason]); } diff --git a/js/src/jsgcstats.h b/js/src/jsgcstats.h index f6eb486d7706..4925006c9a70 100644 --- a/js/src/jsgcstats.h +++ b/js/src/jsgcstats.h @@ -49,7 +49,8 @@ JS_BEGIN_EXTERN_C struct JSGCInfo { double appTime, gcTime, waitTime, markTime, sweepTime; - double sweepObjTime, sweepStringTime, sweepShapeTime, destroyTime, endTime; + double sweepObjTime, sweepStringTime, sweepScriptTime, sweepShapeTime; + double destroyTime, endTime; bool isCompartmental; }; @@ -134,6 +135,7 @@ struct GCTimer uint64 startSweep; uint64 sweepObjectEnd; uint64 sweepStringEnd; + uint64 sweepScriptEnd; uint64 sweepShapeEnd; uint64 sweepDestroyEnd; uint64 end; diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 5f138007fc5f..84be23a49c43 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -890,7 +890,6 @@ Execute(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value &this } LeaveTrace(cx); - AutoScriptRooter root(cx, script); ExecuteFrameGuard efg; if (!cx->stack.pushExecuteFrame(cx, script, thisv, scopeChain, type, evalInFrame, &efg)) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 50926dc83ae3..d6fd20200650 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -982,7 +982,7 @@ EvalCacheHash(JSContext *cx, JSLinearString *str) h *= JS_GOLDEN_RATIO; h >>= 32 - JS_EVAL_CACHE_SHIFT; - return &JS_SCRIPTS_TO_GC(cx)[h]; + return &cx->compartment->evalCache[h]; } static JS_ALWAYS_INLINE JSScript * @@ -1054,8 +1054,8 @@ EvalCacheLookup(JSContext *cx, JSLinearString *str, StackFrame *caller, uintN st if (i < 0 || objarray->vector[i]->getParent() == &scopeobj) { JS_ASSERT(staticLevel == script->staticLevel); - *scriptp = script->u.nextToGC; - script->u.nextToGC = NULL; + *scriptp = script->u.evalHashLink; + script->u.evalHashLink = NULL; return script; } } @@ -1064,7 +1064,7 @@ EvalCacheLookup(JSContext *cx, JSLinearString *str, StackFrame *caller, uintN st if (++count == EVAL_CACHE_CHAIN_LIMIT) return NULL; - scriptp = &script->u.nextToGC; + scriptp = &script->u.evalHashLink; } return NULL; } @@ -1078,7 +1078,7 @@ EvalCacheLookup(JSContext *cx, JSLinearString *str, StackFrame *caller, uintN st * a jsdbgapi user's perspective, we want each eval() to create and destroy a * script. This hides implementation details and means we don't have to deal * with calls to JS_GetScriptObject for scripts in the eval cache (currently, - * script->u.object aliases script->u.nextToGC). + * script->u.object aliases script->u.evalHashLink). */ class EvalScriptGuard { @@ -1098,11 +1098,8 @@ class EvalScriptGuard ~EvalScriptGuard() { if (script_) { js_CallDestroyScriptHook(cx_, script_); - script_->u.nextToGC = *bucket_; + script_->u.evalHashLink = *bucket_; *bucket_ = script_; -#ifdef CHECK_SCRIPT_OWNER - script_->owner = NULL; -#endif } } @@ -5164,12 +5161,8 @@ js_NativeGetInline(JSContext *cx, JSObject *receiver, JSObject *obj, JSObject *p } sample = cx->runtime->propertyRemovals; - { - AutoShapeRooter tvr(cx, shape); - AutoObjectRooter tvr2(cx, pobj); - if (!shape->get(cx, receiver, obj, pobj, vp)) - return false; - } + if (!shape->get(cx, receiver, obj, pobj, vp)) + return false; if (pobj->containsSlot(slot) && (JS_LIKELY(cx->runtime->propertyRemovals == sample) || @@ -5227,14 +5220,11 @@ js_NativeSet(JSContext *cx, JSObject *obj, const Shape *shape, bool added, bool } sample = cx->runtime->propertyRemovals; - { - AutoShapeRooter tvr(cx, shape); - if (!shape->set(cx, obj, strict, vp)) - return false; - - JS_ASSERT_IF(!obj->inDictionaryMode(), shape->slot == slot); - slot = shape->slot; - } + if (!shape->set(cx, obj, strict, vp)) + return false; + + JS_ASSERT_IF(!obj->inDictionaryMode(), shape->slot == slot); + slot = shape->slot; if (obj->containsSlot(slot) && (JS_LIKELY(cx->runtime->propertyRemovals == sample) || diff --git a/js/src/jsobj.h b/js/src/jsobj.h index f982a6010a1d..6cbfa3012293 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -990,6 +990,9 @@ struct JSObject : js::gc::Cell { 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); diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 7c477501aeb7..0861dda65047 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -525,6 +525,37 @@ JSObject::getFlatClosureUpvars() const 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 { diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index dee6585a9b9e..7179a9977250 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -372,7 +372,7 @@ 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 @@ -585,7 +585,7 @@ PushNodeChildren(JSParseNode *pn, NodeStack *stack) stack->pushUnlessNull(pn->pn_kid); break; case PN_NULLARY: - /* + /* * E4X function namespace nodes are PN_NULLARY, but can appear on use * lists. */ @@ -1123,11 +1123,8 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerF JS_ASSERT(script->savedCallerFun == savedCallerFun); - { - AutoScriptRooter root(cx, script); - if (!defineGlobals(cx, globalScope, script)) - goto late_error; - } + if (!defineGlobals(cx, globalScope, script)) + script = NULL; out: JS_FinishArenaPool(&codePool); @@ -1137,11 +1134,6 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerF 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; } @@ -2617,13 +2609,13 @@ Parser::setFunctionKinds(JSFunctionBox *funbox, uint32 *tcflags) * 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()); @@ -3229,11 +3221,11 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, FunctionSyntaxKind kind) * 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. */ @@ -3732,7 +3724,7 @@ DefineGlobal(JSParseNode *pn, JSCodeGenerator *cg, JSAtom *atom) !shape->hasDefaultSetter()) { return true; } - + def = GlobalScope::GlobalDef(shape->slot); } else { def = GlobalScope::GlobalDef(atom, funbox); @@ -6771,7 +6763,7 @@ class CompExprTransplanter { * 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: @@ -7013,7 +7005,7 @@ CompExprTransplanter::transplant(JSParseNode *pn) 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. */ diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 89c1b35bdd5c..294b5fb0a8d7 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -287,30 +287,30 @@ Bindings::trace(JSTracer *trc) MarkShape(trc, lastBinding, "shape"); } -} /* namespace js */ +#ifdef JS_CRASH_DIAGNOSTICS -static void +void CheckScript(JSScript *script, JSScript *prev) { -#ifdef JS_CRASH_DIAGNOSTICS if (script->cookie1 != JS_SCRIPT_COOKIE || script->cookie2 != JS_SCRIPT_COOKIE) { crash::StackBuffer buf1(script); crash::StackBuffer buf2(prev); JS_OPT_ASSERT(false); } -#endif } -static void +void CheckScriptOwner(JSScript *script, JSObject *owner) { -#ifdef JS_CRASH_DIAGNOSTICS JS_OPT_ASSERT(script->ownerObject == owner); if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT) - JS_OPT_ASSERT(script->compartment == owner->compartment()); -#endif + JS_OPT_ASSERT(script->compartment() == owner->compartment()); } +#endif /* JS_CRASH_DIAGNOSTICS */ + +} /* namespace js */ + #if JS_HAS_XDR enum ScriptBits { @@ -379,7 +379,6 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) EmptyShape *emptyCallShape = EmptyShape::getEmptyCallShape(cx); if (!emptyCallShape) return false; - AutoShapeRooter shapeRoot(cx, emptyCallShape); Bindings bindings(cx, emptyCallShape); AutoBindingsRooter rooter(cx, bindings); @@ -537,8 +536,6 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) if (!JS_XDRUint32(xdr, &scriptBits)) return JS_FALSE; - AutoScriptRooter tvr(cx, NULL); - if (xdr->mode == JSXDR_DECODE) { nClosedArgs = encodedClosedCount >> 16; nClosedVars = encodedClosedCount & 0xFFFF; @@ -560,7 +557,6 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) /* 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; @@ -737,10 +733,8 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp) return JS_TRUE; error: - if (xdr->mode == JSXDR_DECODE) { - js_DestroyScript(cx, script, 1); + if (xdr->mode == JSXDR_DECODE) *scriptp = NULL; - } xdr->script = oldscript; return JS_FALSE; } @@ -768,20 +762,14 @@ JSPCCounters::destroy(JSContext *cx) } } -static void -script_finalize(JSContext *cx, JSObject *obj) -{ - JSScript *script = (JSScript *) obj->getPrivate(); - if (script) - js_DestroyScriptFromGC(cx, script, obj); -} - static void script_trace(JSTracer *trc, JSObject *obj) { JSScript *script = (JSScript *) obj->getPrivate(); - if (script) - js_TraceScript(trc, script, obj); + if (script) { + CheckScriptOwner(script, obj); + MarkScript(trc, script, "script"); + } } Class js_ScriptClass = { @@ -795,7 +783,7 @@ Class js_ScriptClass = { EnumerateStub, ResolveStub, ConvertStub, - script_finalize, + NULL, /* finalize */ NULL, /* reserved0 */ NULL, /* checkAccess */ NULL, /* call */ @@ -922,17 +910,13 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom 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(JSScript) + - sizeof(JSAtom *) * natoms; + size = sizeof(JSAtom *) * natoms; if (nobjects != 0) size += sizeof(JSObjectArray) + nobjects * sizeof(JSObject *); @@ -960,15 +944,21 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom size += length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote); - script = (JSScript *) cx->malloc_(size); - if (!script) + uint8 *data = static_cast(cx->malloc_(size)); + if (!data) 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); @@ -976,42 +966,40 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom if (cx->hasRunOption(JSOPTION_PCCOUNT)) (void) script->pcCounters.init(cx, length); - uint8 *scriptEnd = reinterpret_cast(script + 1); - - cursor = scriptEnd; + uint8 *cursor = data; if (nobjects != 0) { - script->objectsOffset = (uint8)(cursor - scriptEnd); + script->objectsOffset = (uint8)(cursor - data); cursor += sizeof(JSObjectArray); } else { script->objectsOffset = JSScript::INVALID_OFFSET; } if (nupvars != 0) { - script->upvarsOffset = (uint8)(cursor - scriptEnd); + script->upvarsOffset = (uint8)(cursor - data); cursor += sizeof(JSUpvarArray); } else { script->upvarsOffset = JSScript::INVALID_OFFSET; } if (nregexps != 0) { - script->regexpsOffset = (uint8)(cursor - scriptEnd); + script->regexpsOffset = (uint8)(cursor - data); cursor += sizeof(JSObjectArray); } else { script->regexpsOffset = JSScript::INVALID_OFFSET; } if (ntrynotes != 0) { - script->trynotesOffset = (uint8)(cursor - scriptEnd); + script->trynotesOffset = (uint8)(cursor - data); cursor += sizeof(JSTryNoteArray); } else { script->trynotesOffset = JSScript::INVALID_OFFSET; } if (nglobals != 0) { - script->globalsOffset = (uint8)(cursor - scriptEnd); + script->globalsOffset = (uint8)(cursor - data); cursor += sizeof(GlobalSlotArray); } else { script->globalsOffset = JSScript::INVALID_OFFSET; } - JS_ASSERT(cursor - scriptEnd < 0xFF); + JS_ASSERT(cursor - data < 0xFF); if (nconsts != 0) { - script->constOffset = (uint8)(cursor - scriptEnd); + script->constOffset = (uint8)(cursor - data); cursor += sizeof(JSConstArray); } else { script->constOffset = JSScript::INVALID_OFFSET; @@ -1103,12 +1091,7 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom JS_ASSERT(cursor + length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote) == - (uint8 *)script + size); - - script->compartment = cx->compartment; -#ifdef CHECK_SCRIPT_OWNER - script->owner = cx->thread(); -#endif + data + size); JS_APPEND_LINK(&script->links, &cx->compartment->scripts); @@ -1147,9 +1130,7 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg) 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)); @@ -1164,12 +1145,12 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg) if (filename) { script->filename = SaveScriptFilename(cx, filename); if (!script->filename) - goto bad; + return NULL; } script->lineno = cg->firstLine; if (script->nfixed + cg->maxStackDepth >= JS_BIT(16)) { ReportCompileErrorNumber(cx, CG_TS(cg), NULL, JSREPORT_ERROR, JSMSG_NEED_DIET, "script"); - goto bad; + return NULL; } script->nslots = script->nfixed + cg->maxStackDepth; script->staticLevel = uint16(cg->staticLevel); @@ -1180,7 +1161,7 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg) script->sourceMap = (jschar *) cg->parser->tokenStream.releaseSourceMap(); if (!js_FinishTakingSrcNotes(cx, cg, script->notes())) - goto bad; + return NULL; if (cg->ntrynotes != 0) js_FinishTakingTryNotes(cg, script->trynotes()); if (cg->objectList.length != 0) @@ -1247,9 +1228,6 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg) #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 { @@ -1258,7 +1236,7 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg) * valid holder object. */ if ((cg->flags & TCF_NEED_SCRIPT_OBJECT) && !js_NewScriptObject(cx, script)) - goto bad; + return NULL; } /* Tell the debugger about this compiled script. */ @@ -1272,20 +1250,14 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg) } return script; - -bad: - if (!script->u.object) - js_DestroyScript(cx, script, 2); - return NULL; } size_t -JSScript::totalSize() +JSScript::dataSize() { - return code + - length * sizeof(jsbytecode) + - numNotes() * sizeof(jssrcnote) - - (uint8 *) this; + uint8 *dataEnd = code + length * sizeof(jsbytecode) + numNotes() * sizeof(jssrcnote); + JS_ASSERT(dataEnd > data); + return dataEnd - data; } void @@ -1314,168 +1286,64 @@ JSScript::numNotes() JS_FRIEND_API(void) js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun) { - JSNewScriptHook hook; - - hook = cx->debugHooks->newScriptHook; - if (hook) { + JS_ASSERT(!script->callDestroyHook); + if (JSNewScriptHook hook = cx->debugHooks->newScriptHook) { AutoKeepAtoms keep(cx->runtime); hook(cx, script->filename, script->lineno, script, fun, cx->debugHooks->newScriptHookData); } + script->callDestroyHook = true; } void js_CallDestroyScriptHook(JSContext *cx, JSScript *script) { - JSDestroyScriptHook hook; + if (!script->callDestroyHook) + return; - hook = cx->debugHooks->destroyScriptHook; - if (hook) + if (JSDestroyScriptHook hook = cx->debugHooks->destroyScriptHook) hook(cx, script, cx->debugHooks->destroyScriptHookData); + script->callDestroyHook = false; Debugger::onDestroyScript(script); JS_ClearScriptTraps(cx, script); } -static void -DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller) +void +JSScript::finalize(JSContext *cx) { - CheckScript(script, NULL); - CheckScriptOwner(script, owner); + CheckScript(this, NULL); - if (script->principals) - JSPRINCIPALS_DROP(cx, script->principals); + js_CallDestroyScriptHook(cx, this); - GSNCache *gsnCache = GetGSNCache(cx); - if (gsnCache->code == script->code) - gsnCache->purge(); - - /* - * 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 - - /* FIXME: bug 506341; would like to do this only if regenerating shapes. */ - if (!cx->runtime->gcRunning) { - JS_PROPERTY_CACHE(cx).purgeForScript(cx, script); - -#ifdef CHECK_SCRIPT_OWNER - JS_ASSERT(script->owner == cx->thread()); -#endif - } + if (principals) + JSPRINCIPALS_DROP(cx, principals); #ifdef JS_TRACER - if (script->compartment->hasTraceMonitor()) - PurgeScriptFragments(script->compartment->traceMonitor(), script); + if (compartment()->hasTraceMonitor()) + PurgeScriptFragments(compartment()->traceMonitor(), this); #endif #ifdef JS_METHODJIT - mjit::ReleaseScriptCode(cx, script); + mjit::ReleaseScriptCode(cx, this); #endif - JS_REMOVE_LINK(&script->links); + JS_REMOVE_LINK(&links); - script->pcCounters.destroy(cx); + pcCounters.destroy(cx); - if (script->sourceMap) - cx->free_(script->sourceMap); - - JS_POISON(script, 0xdb, sizeof(JSScript)); - *(uint32 *)script = caller; - cx->free_(script); -} - -void -js_DestroyScript(JSContext *cx, JSScript *script, uint32 caller) -{ - JS_ASSERT(!cx->runtime->gcRunning); - js_CallDestroyScriptHook(cx, script); - DestroyScript(cx, script, JS_NEW_SCRIPT, caller); -} - -void -js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner) -{ - JS_ASSERT(cx->runtime->gcRunning); - js_CallDestroyScriptHook(cx, script); - DestroyScript(cx, script, owner, 100); -} - -void -js_DestroyCachedScript(JSContext *cx, JSScript *script) -{ - JS_ASSERT(cx->runtime->gcRunning); - DestroyScript(cx, script, JS_CACHED_SCRIPT, 101); -} - -void -js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner) -{ - CheckScript(script, NULL); - if (owner) - CheckScriptOwner(script, owner); + if (sourceMap) + cx->free_(sourceMap); #ifdef JS_CRASH_DIAGNOSTICS - JSRuntime *rt = trc->context->runtime; - JS_OPT_ASSERT_IF(rt->gcCheckCompartment, script->compartment == rt->gcCheckCompartment); + if (data) + JS_POISON(data, 0xdb, dataSize()); #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"); - } - - 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 + cx->free_(data); } JSObject * js_NewScriptObject(JSContext *cx, JSScript *script) { - AutoScriptRooter root(cx, script); - JS_ASSERT(!script->u.object); JSObject *obj = NewNonFunction(cx, &js_ScriptClass, NULL, NULL); @@ -1491,10 +1359,6 @@ js_NewScriptObject(JSContext *cx, JSScript *script) */ obj->clearProto(); -#ifdef CHECK_SCRIPT_OWNER - script->owner = NULL; -#endif - return obj; } @@ -1776,8 +1640,7 @@ private: JSScript * js_CloneScript(JSContext *cx, JSScript *script) { - JS_ASSERT(cx->compartment != script->compartment); - JS_ASSERT(script->compartment); + JS_ASSERT(cx->compartment != script->compartment()); // serialize script AutoJSXDRState w(JS_XDRNewMem(cx, JSXDR_ENCODE)); @@ -1817,7 +1680,7 @@ js_CloneScript(JSContext *cx, JSScript *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); diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 5029769ef9e3..cb72bb1a4551 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -1,4 +1,4 @@ -/* -*- 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 ***** @@ -358,10 +358,6 @@ class Bindings { #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; @@ -425,7 +421,7 @@ static const uint32 JS_SCRIPT_COOKIE = 0xc00cee; static JSObject * const JS_NEW_SCRIPT = (JSObject *)0x12345678; static JSObject * const JS_CACHED_SCRIPT = (JSObject *)0x12341234; -struct JSScript { +struct JSScript : public js::gc::Cell { /* * Two successively less primitive ways to make a new JSScript. The first * does *not* call a non-null cx->runtime->newScriptHook -- only the second, @@ -447,12 +443,12 @@ struct JSScript { /* 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 */ @@ -494,11 +490,11 @@ struct JSScript { 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 */ @@ -527,12 +523,10 @@ struct JSScript { * explicitly destroyed by the code that created them. */ JSObject *object; - JSScript *nextToGC; /* next to GC in rt->scriptsToGC list */ - } u; -#ifdef CHECK_SCRIPT_OWNER - JSThread *owner; /* for thread-safe life-cycle assertions */ -#endif + /* Hash table chaining for JSCompartment::evalCache. */ + JSScript *evalHashLink; + } u; uint32 *closedSlots; /* vector of closed slots; args first, then vars. */ @@ -554,7 +548,14 @@ struct JSScript { 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; } @@ -584,7 +585,7 @@ struct JSScript { JS_FRIEND_API(size_t) jitDataSize();/* Size of the JITScript and all sections */ #endif - JS_FRIEND_API(size_t) totalSize(); /* Size of the JSScript and all sections */ + JS_FRIEND_API(size_t) dataSize(); /* Size of all data sections */ uint32 numNotes(); /* Number of srcnote slots in the srcnotes section */ /* Script notes are allocated right after the code. */ @@ -595,32 +596,32 @@ struct JSScript { JSObjectArray *objects() { JS_ASSERT(isValidOffset(objectsOffset)); - return reinterpret_cast(uintptr_t(this + 1) + objectsOffset); + return reinterpret_cast(data + objectsOffset); } JSUpvarArray *upvars() { JS_ASSERT(isValidOffset(upvarsOffset)); - return reinterpret_cast(uintptr_t(this + 1) + upvarsOffset); + return reinterpret_cast(data + upvarsOffset); } JSObjectArray *regexps() { JS_ASSERT(isValidOffset(regexpsOffset)); - return reinterpret_cast(uintptr_t(this + 1) + regexpsOffset); + return reinterpret_cast(data + regexpsOffset); } JSTryNoteArray *trynotes() { JS_ASSERT(isValidOffset(trynotesOffset)); - return reinterpret_cast(uintptr_t(this + 1) + trynotesOffset); + return reinterpret_cast(data + trynotesOffset); } js::GlobalSlotArray *globals() { JS_ASSERT(isValidOffset(globalsOffset)); - return reinterpret_cast(uintptr_t(this + 1) + globalsOffset); + return reinterpret_cast(data + globalsOffset); } JSConstArray *consts() { JS_ASSERT(isValidOffset(constOffset)); - return reinterpret_cast(uintptr_t(this + 1) + constOffset); + return reinterpret_cast(data + constOffset); } JSAtom *getAtom(size_t index) { @@ -679,8 +680,12 @@ struct JSScript { } 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 */ @@ -729,24 +734,31 @@ js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun); extern void js_CallDestroyScriptHook(JSContext *cx, JSScript *script); -/* - * The function must be used only outside the GC for a script that was run - * only on the current thread. - */ -extern void -js_DestroyScript(JSContext *cx, JSScript *script, uint32 caller); +namespace js { -extern void -js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner); +#ifdef JS_CRASH_DIAGNOSTICS -/* - * 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); +void +CheckScriptOwner(JSScript *script, JSObject *owner); + +void +CheckScript(JSScript *script, JSScript *prev); + +#else + +inline void +CheckScriptOwner(JSScript *script, JSObject *owner) +{ +} + +inline void +CheckScript(JSScript *script, JSScript *prev) +{ +} + +#endif /* !JS_CRASH_DIAGNOSTICS */ + +} /* namespace js */ extern void js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 0cfe1142965a..d7f473531f54 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -16975,7 +16975,7 @@ 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); diff --git a/js/src/jsxdrapi.cpp b/js/src/jsxdrapi.cpp index 1ee98e35bb46..5c8cbb3e8223 100644 --- a/js/src/jsxdrapi.cpp +++ b/js/src/jsxdrapi.cpp @@ -719,10 +719,8 @@ JS_XDRScriptObject(JSXDRState *xdr, JSObject **scriptObjp) if (xdr->mode == JSXDR_DECODE) { *scriptObjp = js_NewScriptObject(xdr->cx, script); - if (!*scriptObjp) { - js_DestroyScript(xdr->cx, script, 8); + if (!*scriptObjp) return false; - } js_CallNewScriptHook(xdr->cx, script, NULL); Debugger::onNewScript(xdr->cx, script, *scriptObjp, Debugger::NewHeldScript); } diff --git a/js/src/methodjit/BaseCompiler.h b/js/src/methodjit/BaseCompiler.h index 0864357ba411..9b57981aaa21 100644 --- a/js/src/methodjit/BaseCompiler.h +++ b/js/src/methodjit/BaseCompiler.h @@ -147,7 +147,7 @@ class LinkerHelper : public JSC::LinkBuffer // 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) { diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 7f57a072e44e..a12d10815ee2 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -424,7 +424,7 @@ mjit::Compiler::finishThisUp(JITScript **jitp) 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; @@ -4054,7 +4054,7 @@ mjit::Compiler::iter(uintN flags) 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); diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index 7df63ba4b86f..7e16fccf0d93 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -119,14 +119,14 @@ 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 diff --git a/js/src/methodjit/Retcon.cpp b/js/src/methodjit/Retcon.cpp index c865e3fce14b..64e94a63b571 100644 --- a/js/src/methodjit/Retcon.cpp +++ b/js/src/methodjit/Retcon.cpp @@ -128,7 +128,7 @@ Recompiler::recompile() // 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) { diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 2af8e06ef8da..a82c4987c902 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -236,7 +236,7 @@ BreakpointSite::destroyIfEmpty(JSRuntime *rt, BreakpointSiteMap::Enum *e) if (e) e->removeFront(); else - script->compartment->breakpointSites.remove(pc); + script->compartment()->breakpointSites.remove(pc); rt->delete_(this); } } @@ -446,7 +446,7 @@ Debugger::slowPathOnLeaveFrame(JSContext *cx) */ if (fp->isEvalFrame()) { JSScript *script = fp->script(); - script->compartment->clearBreakpointsIn(cx, NULL, script, NULL); + script->compartment()->clearBreakpointsIn(cx, NULL, script, NULL); } } @@ -804,7 +804,7 @@ Debugger::slowPathOnNewScript(JSContext *cx, JSScript *script, JSObject *obj, Ne } } 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; @@ -1742,8 +1742,8 @@ 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) { @@ -1775,7 +1775,7 @@ 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) { @@ -1794,7 +1794,7 @@ 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++) @@ -2239,7 +2239,7 @@ DebuggerScript_setBreakpoint(JSContext *cx, uintN argc, Value *vp) 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) @@ -2274,7 +2274,7 @@ DebuggerScript_getBreakpoints(JSContext *cx, uintN argc, Value *vp) 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)) { @@ -2302,7 +2302,7 @@ DebuggerScript_clearBreakpoint(JSContext *cx, uintN argc, Value *vp) if (!handler) return false; - script->compartment->clearBreakpointsIn(cx, dbg, script, handler); + script->compartment()->clearBreakpointsIn(cx, dbg, script, handler); args.rval().setUndefined(); return true; } @@ -2312,7 +2312,7 @@ 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; } @@ -2672,8 +2672,8 @@ EvaluateInScope(JSContext *cx, JSObject *scobj, StackFrame *fp, const jschar *ch if (!script) return false; - bool ok = Execute(cx, script, *scobj, fp->thisValue(), EXECUTE_DEBUG, fp, rval); - js_DestroyScript(cx, script, 6); + JSBool ok = Execute(cx, script, *scobj, fp->thisValue(), EXECUTE_DEBUG, fp, rval); + js_CallDestroyScriptHook(cx, script); return ok; } diff --git a/js/src/vm/Debugger.h b/js/src/vm/Debugger.h index f54bc9ff1e67..1f86034a9b3b 100644 --- a/js/src/vm/Debugger.h +++ b/js/src/vm/Debugger.h @@ -548,14 +548,14 @@ 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); } diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index e761cd3f5f9e..8003089d5bef 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -831,7 +831,7 @@ class StackFrame */ JSCompartment *compartment() const { - JS_ASSERT_IF(isScriptFrame(), scopeChain().compartment() == script()->compartment); + JS_ASSERT_IF(isScriptFrame(), scopeChain().compartment() == script()->compartment()); return scopeChain().compartment(); } diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index 22af7df498d2..078e19747e71 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -873,11 +873,14 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb) } else { - static const char trace_types[JSTRACE_LIMIT][7] = { + static const char trace_types[][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]); } diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index cbcdb1933ae2..98eb85236ba0 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -226,7 +226,7 @@ static JSDHashOperator DetachedWrappedNativeProtoMarker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg) { - XPCWrappedNativeProto* proto = + XPCWrappedNativeProto* proto = (XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key; proto->Mark(); @@ -510,7 +510,7 @@ XPCJSRuntime::SuspectWrappedNative(JSContext *cx, XPCWrappedNative *wrapper, 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 @@ -850,7 +850,7 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) if(threadLock) { // Do the marking... - + { // scoped lock MutexAutoLock lock(*threadLock); @@ -869,7 +869,7 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) // possibly be valid. if(ccxp->CanGetTearOff()) { - XPCWrappedNativeTearOff* to = + XPCWrappedNativeTearOff* to = ccxp->GetTearOff(); if(to) to->Mark(); @@ -1041,7 +1041,7 @@ static JSDHashOperator DetachedWrappedNativeProtoShutdownMarker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg) { - XPCWrappedNativeProto* proto = + XPCWrappedNativeProto* proto = (XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key; proto->SystemIsBeingShutDown((JSContext*)arg); @@ -1249,42 +1249,6 @@ XPCJSRuntime::~XPCJSRuntime() 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 @@ -1336,10 +1300,8 @@ CompartmentCallback(JSContext *cx, void *vdata, JSCompartment *compartment) data->currCompartmentStats = curr; // Get the compartment-level numbers. - curr->scripts = GetCompartmentScriptsSize(compartment); #ifdef JS_METHODJIT - curr->mjitCode = GetCompartmentMjitCodeSize(compartment); - curr->mjitData = GetCompartmentMjitDataSize(compartment); + curr->mjitCode = compartment->getMjitCodeSize(); #endif #ifdef JS_TRACER curr->tjitCode = GetCompartmentTjitCodeSize(compartment); @@ -1371,30 +1333,43 @@ CellCallback(JSContext *cx, void *vdata, void *thing, size_t traceKind, { IterateData *data = static_cast(vdata); CompartmentStats *curr = data->currCompartmentStats; - if(traceKind == JSTRACE_OBJECT) + curr->gcHeapKinds[traceKind] += thingSize; + switch (traceKind) { - curr->gcHeapObjects += thingSize; - JSObject *obj = static_cast(thing); - if(obj->hasSlotsArray()) - curr->objectSlots += obj->numSlots() * sizeof(js::Value); - } - else if(traceKind == JSTRACE_STRING) - { - curr->gcHeapStrings += thingSize; - JSString *str = static_cast(thing); - curr->stringChars += str->charsHeapSize(); - } - else if(traceKind == JSTRACE_SHAPE) - { - curr->gcHeapShapes += thingSize; - js::Shape *shape = static_cast(thing); - if(shape->hasTable()) - curr->propertyTables += shape->getTable()->sizeOf(); - } - else - { - JS_ASSERT(traceKind == JSTRACE_XML); - curr->gcHeapXml += thingSize; + case JSTRACE_OBJECT: + { + JSObject *obj = static_cast(thing); + if(obj->hasSlotsArray()) + curr->objectSlots += obj->numSlots() * sizeof(js::Value); + break; + } + case JSTRACE_STRING: + { + JSString *str = static_cast(thing); + curr->stringChars += str->charsHeapSize(); + break; + } + case JSTRACE_SHAPE: + { + js::Shape *shape = static_cast(thing); + if(shape->hasTable()) + curr->propertyTables += shape->getTable()->sizeOf(); + break; + } + case JSTRACE_SCRIPT: + { + JSScript *script = static_cast(thing); + curr->scriptData += script->dataSize(); +#ifdef JS_METHODJIT + curr->mjitData += script->jitDataSize(); +#endif + break; + } + default: + { + JS_ASSERT(traceKind == JSTRACE_XML); + break; + } } // Yes, this is a subtraction: see ArenaCallback() for details. curr->gcHeapArenaUnused -= thingSize; @@ -1651,12 +1626,13 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data) { CompartmentStats &stats = data->compartmentStatsVector[index]; - data->gcHeapChunkDirtyUnused -= - stats.gcHeapArenaHeaders + stats.gcHeapArenaPadding + - stats.gcHeapArenaUnused + - stats.gcHeapObjects + stats.gcHeapStrings + - stats.gcHeapShapes + stats.gcHeapXml; - + PRInt64 used = stats.gcHeapArenaHeaders + + stats.gcHeapArenaPadding + + stats.gcHeapArenaUnused; + for (size_t i = 0; i != JS_ARRAY_LENGTH(stats.gcHeapKinds); ++i) + used += stats.gcHeapKinds[i]; + + data->gcHeapChunkDirtyUnused -= used; data->gcHeapArenaUnused += stats.gcHeapArenaUnused; } @@ -1667,7 +1643,7 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data) 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. @@ -1709,14 +1685,14 @@ ReportCompartmentStats(const CompartmentStats &stats, ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "gc-heap/objects"), - JS_GC_HEAP_KIND, stats.gcHeapObjects, + JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_OBJECT], "Memory on the compartment's garbage-collected JavaScript heap that holds " "objects.", callback, closure); ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "gc-heap/strings"), - JS_GC_HEAP_KIND, stats.gcHeapStrings, + JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_STRING], "Memory on the compartment's garbage-collected JavaScript heap that holds " "string headers. String headers contain various pieces of information " "about a string, but do not contain (except in the case of very short " @@ -1726,15 +1702,23 @@ ReportCompartmentStats(const CompartmentStats &stats, ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, "gc-heap/shapes"), - JS_GC_HEAP_KIND, stats.gcHeapShapes, + JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_SHAPE], "Memory on the compartment's garbage-collected JavaScript heap that holds " "shapes. A shape is an internal data structure that makes JavaScript " "property accesses fast.", callback, closure); + ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, + "gc-heap/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.gcHeapXml, + JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_XML], "Memory on the compartment's garbage-collected JavaScript heap that holds " "E4X XML objects.", callback, closure); @@ -1768,12 +1752,10 @@ ReportCompartmentStats(const CompartmentStats &stats, callback, closure); ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name, - "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.", + "script-data"), + nsIMemoryReporter::KIND_HEAP, stats.scriptData, + "Memory allocated for JSScript bytecode and various variable-length " + "tables.", callback, closure); #ifdef JS_METHODJIT diff --git a/js/src/xpconnect/src/xpcpublic.h b/js/src/xpconnect/src/xpcpublic.h index bfd6fc295376..022df9fdc25a 100644 --- a/js/src/xpconnect/src/xpcpublic.h +++ b/js/src/xpconnect/src/xpcpublic.h @@ -201,16 +201,13 @@ struct CompartmentStats PRInt64 gcHeapArenaPadding; PRInt64 gcHeapArenaUnused; - PRInt64 gcHeapObjects; - PRInt64 gcHeapStrings; - PRInt64 gcHeapShapes; - PRInt64 gcHeapXml; + PRInt64 gcHeapKinds[JSTRACE_LIMIT]; PRInt64 objectSlots; PRInt64 stringChars; PRInt64 propertyTables; + PRInt64 scriptData; - PRInt64 scripts; #ifdef JS_METHODJIT PRInt64 mjitCode; PRInt64 mjitData;