bug 674251 - making JSScript a GC-thing. r=jorendorff

--HG--
extra : rebase_source : 8cec41c8afb99951e469eb3a97c0d48cb5da0b4f
This commit is contained in:
Igor Bukanov 2011-08-17 10:07:04 +02:00
parent c483d7d865
commit 3b1ac8c317
35 changed files with 532 additions and 568 deletions

View File

@ -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

View File

@ -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<JSCrossCompartmentCall*>(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;
}

View File

@ -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

View File

@ -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:

View File

@ -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);
}

View File

@ -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;

View File

@ -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)
/*

View File

@ -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)

View File

@ -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);
}

View File

@ -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<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);
@ -787,6 +793,9 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w)
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);
@ -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<JSObject_Slots16>(cx, &listHead);
break;
case FINALIZE_FUNCTION:
FinalizeArenas<JSFunction>(cx, &listHead);
break;
case FINALIZE_STRING:
FinalizeArenas<JSString>(cx, &listHead);
break;
@ -1459,6 +1478,8 @@ RefillFinalizableFreeList(JSContext *cx, unsigned thingKind)
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);
@ -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<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;
@ -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<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);
@ -1991,6 +1987,17 @@ JSCompartment::finalizeObjectArenaLists(JSContext *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
@ -2015,6 +2022,12 @@ 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 {
@ -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);

View File

@ -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 {

View File

@ -255,6 +255,12 @@ 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);

View File

@ -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<JSObject *>(thing));
break;
case JSTRACE_STRING:
MarkString(trc, reinterpret_cast<JSString *>(thing));
break;
case JSTRACE_SHAPE:
Mark(trc, reinterpret_cast<Shape *>(thing));
break;
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;
#if JS_HAS_XML_SUPPORT
case JSTRACE_XML:
Mark(trc, reinterpret_cast<JSXML *>(thing));
break;
case JSTRACE_XML:
Mark(trc, static_cast<JSXML *>(thing));
break;
#endif
default:
JS_ASSERT(false);
}
}
@ -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<JSObject *>(thing));
break;
case JSTRACE_STRING:
MarkChildren(trc, (JSString *)thing);
MarkChildren(trc, static_cast<JSString *>(thing));
break;
case JSTRACE_SHAPE:
MarkChildren(trc, (js::Shape *)thing);
MarkChildren(trc, static_cast<Shape *>(thing));
break;
case JSTRACE_SCRIPT:
MarkChildren(trc, static_cast<JSScript *>(thing));
break;
#if JS_HAS_XML_SUPPORT
case JSTRACE_XML:
MarkChildren(trc, (JSXML *)thing);
MarkChildren(trc, static_cast<JSXML *>(thing));
break;
#endif
}
}

View File

@ -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);

View File

@ -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]);
}

View File

@ -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;

View File

@ -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))

View File

@ -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) ||

View File

@ -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);

View File

@ -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
{

View File

@ -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.
*/

View File

@ -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<sizeof(JSScript), 0x87> buf1(script);
crash::StackBuffer<sizeof(JSScript), 0x88> buf2(prev);
JS_OPT_ASSERT(false);
}
#endif
}
static void
void
CheckScriptOwner(JSScript *script, JSObject *owner)
{
#ifdef JS_CRASH_DIAGNOSTICS
JS_OPT_ASSERT(script->ownerObject == owner);
if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT)
JS_OPT_ASSERT(script->compartment == owner->compartment());
#endif
JS_OPT_ASSERT(script->compartment() == owner->compartment());
}
#endif /* JS_CRASH_DIAGNOSTICS */
} /* namespace js */
#if JS_HAS_XDR
enum ScriptBits {
@ -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<uint8 *>(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<uint8 *>(script + 1);
cursor = scriptEnd;
uint8 *cursor = data;
if (nobjects != 0) {
script->objectsOffset = (uint8)(cursor - scriptEnd);
script->objectsOffset = (uint8)(cursor - data);
cursor += sizeof(JSObjectArray);
} else {
script->objectsOffset = JSScript::INVALID_OFFSET;
}
if (nupvars != 0) {
script->upvarsOffset = (uint8)(cursor - scriptEnd);
script->upvarsOffset = (uint8)(cursor - data);
cursor += sizeof(JSUpvarArray);
} else {
script->upvarsOffset = JSScript::INVALID_OFFSET;
}
if (nregexps != 0) {
script->regexpsOffset = (uint8)(cursor - scriptEnd);
script->regexpsOffset = (uint8)(cursor - data);
cursor += sizeof(JSObjectArray);
} else {
script->regexpsOffset = JSScript::INVALID_OFFSET;
}
if (ntrynotes != 0) {
script->trynotesOffset = (uint8)(cursor - scriptEnd);
script->trynotesOffset = (uint8)(cursor - data);
cursor += sizeof(JSTryNoteArray);
} else {
script->trynotesOffset = JSScript::INVALID_OFFSET;
}
if (nglobals != 0) {
script->globalsOffset = (uint8)(cursor - scriptEnd);
script->globalsOffset = (uint8)(cursor - data);
cursor += sizeof(GlobalSlotArray);
} else {
script->globalsOffset = JSScript::INVALID_OFFSET;
}
JS_ASSERT(cursor - scriptEnd < 0xFF);
JS_ASSERT(cursor - data < 0xFF);
if (nconsts != 0) {
script->constOffset = (uint8)(cursor - scriptEnd);
script->constOffset = (uint8)(cursor - data);
cursor += sizeof(JSConstArray);
} else {
script->constOffset = JSScript::INVALID_OFFSET;
@ -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<WithProto::Class>(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);

View File

@ -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<JSObjectArray *>(uintptr_t(this + 1) + objectsOffset);
return reinterpret_cast<JSObjectArray *>(data + objectsOffset);
}
JSUpvarArray *upvars() {
JS_ASSERT(isValidOffset(upvarsOffset));
return reinterpret_cast<JSUpvarArray *>(uintptr_t(this + 1) + upvarsOffset);
return reinterpret_cast<JSUpvarArray *>(data + upvarsOffset);
}
JSObjectArray *regexps() {
JS_ASSERT(isValidOffset(regexpsOffset));
return reinterpret_cast<JSObjectArray *>(uintptr_t(this + 1) + regexpsOffset);
return reinterpret_cast<JSObjectArray *>(data + regexpsOffset);
}
JSTryNoteArray *trynotes() {
JS_ASSERT(isValidOffset(trynotesOffset));
return reinterpret_cast<JSTryNoteArray *>(uintptr_t(this + 1) + trynotesOffset);
return reinterpret_cast<JSTryNoteArray *>(data + trynotesOffset);
}
js::GlobalSlotArray *globals() {
JS_ASSERT(isValidOffset(globalsOffset));
return reinterpret_cast<js::GlobalSlotArray *>(uintptr_t(this + 1) + globalsOffset);
return reinterpret_cast<js::GlobalSlotArray *>(data + globalsOffset);
}
JSConstArray *consts() {
JS_ASSERT(isValidOffset(constOffset));
return reinterpret_cast<JSConstArray *>(uintptr_t(this + 1) + constOffset);
return reinterpret_cast<JSConstArray *>(data + constOffset);
}
JSAtom *getAtom(size_t index) {
@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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) {

View File

@ -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);

View File

@ -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

View File

@ -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) {

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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]);
}

View File

@ -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<IterateData *>(vdata);
CompartmentStats *curr = data->currCompartmentStats;
if(traceKind == JSTRACE_OBJECT)
curr->gcHeapKinds[traceKind] += thingSize;
switch (traceKind)
{
curr->gcHeapObjects += thingSize;
JSObject *obj = static_cast<JSObject *>(thing);
if(obj->hasSlotsArray())
curr->objectSlots += obj->numSlots() * sizeof(js::Value);
}
else if(traceKind == JSTRACE_STRING)
{
curr->gcHeapStrings += thingSize;
JSString *str = static_cast<JSString *>(thing);
curr->stringChars += str->charsHeapSize();
}
else if(traceKind == JSTRACE_SHAPE)
{
curr->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;
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;
}
}
// 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

View File

@ -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;