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

This commit is contained in:
Igor Bukanov 2011-08-09 10:51:59 +02:00
parent 66072d5ad9
commit 486ba2f469
48 changed files with 1105 additions and 1090 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

@ -308,7 +308,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
PodZero(escapedSlots, numSlots);
if (script->usesEval || script->usesArguments || script->compartment->debugMode()) {
if (script->usesEval || script->usesArguments || script->compartment()->debugMode()) {
for (unsigned i = 0; i < nargs; i++)
escapedSlots[ArgSlot(i)] = true;
} else {
@ -319,7 +319,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
}
}
if (script->usesEval || script->compartment->debugMode()) {
if (script->usesEval || script->compartment()->debugMode()) {
for (unsigned i = 0; i < script->nfixed; i++) {
escapedSlots[LocalSlot(script, i)] = true;
setLocal(i, LOCAL_USE_BEFORE_DEF);
@ -612,7 +612,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx)
JSTryNote *tn = script->trynotes()->vector;
JSTryNote *tnlimit = tn + script->trynotes()->length;
for (; tn < tnlimit; tn++) {
unsigned startOffset = script->main - script->code + tn->start;
unsigned startOffset = script->mainOffset + tn->start;
if (startOffset == offset + 1) {
unsigned catchOffset = startOffset + tn->length;
@ -894,7 +894,7 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx)
JSTryNote *tn = script->trynotes()->vector;
JSTryNote *tnlimit = tn + script->trynotes()->length;
for (; tn < tnlimit; tn++) {
unsigned startOffset = script->main - script->code + tn->start;
unsigned startOffset = script->mainOffset + tn->start;
if (startOffset + tn->length == offset) {
/*
* Extend all live variables at exception entry to the start of
@ -1674,7 +1674,7 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
JSTryNote *tn = script->trynotes()->vector;
JSTryNote *tnlimit = tn + script->trynotes()->length;
for (; tn < tnlimit; tn++) {
unsigned startOffset = script->main - script->code + tn->start;
unsigned startOffset = script->mainOffset + tn->start;
if (startOffset == offset + 1) {
unsigned catchOffset = startOffset + tn->length;

View File

@ -952,7 +952,7 @@ class ScriptAnalysis
/* Accessors for bytecode information. */
Bytecode& getCode(uint32 offset) {
JS_ASSERT(script->compartment->activeAnalysis);
JS_ASSERT(script->compartment()->activeAnalysis);
JS_ASSERT(offset < script->length);
JS_ASSERT(codeArray[offset]);
return *codeArray[offset];
@ -960,7 +960,7 @@ class ScriptAnalysis
Bytecode& getCode(const jsbytecode *pc) { return getCode(pc - script->code); }
Bytecode* maybeCode(uint32 offset) {
JS_ASSERT(script->compartment->activeAnalysis);
JS_ASSERT(script->compartment()->activeAnalysis);
JS_ASSERT(offset < script->length);
return codeArray[offset];
}
@ -1148,7 +1148,7 @@ class ScriptAnalysis
* containing script (which does not imply the variable is closed).
*/
bool slotEscapes(uint32 slot) {
JS_ASSERT(script->compartment->activeAnalysis);
JS_ASSERT(script->compartment()->activeAnalysis);
if (slot >= numSlots)
return true;
return escapedSlots[slot];
@ -1163,7 +1163,7 @@ class ScriptAnalysis
bool trackSlot(uint32 slot) { return !slotEscapes(slot) && canTrackVars; }
const LifetimeVariable & liveness(uint32 slot) {
JS_ASSERT(script->compartment->activeAnalysis);
JS_ASSERT(script->compartment()->activeAnalysis);
JS_ASSERT(!slotEscapes(slot));
return lifetimes[slot];
}

View File

@ -1211,7 +1211,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;
@ -1261,7 +1261,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;
}
@ -2195,7 +2195,8 @@ JS_PUBLIC_API(void)
JS_CallTracer(JSTracer *trc, void *thing, uint32 kind)
{
JS_ASSERT(thing);
MarkKind(trc, thing, kind);
JS_ASSERT(kind <= JSTRACE_LAST);
MarkKind(trc, thing, JSGCTraceKind(kind));
}
#ifdef DEBUG
@ -2205,9 +2206,11 @@ JS_CallTracer(JSTracer *trc, void *thing, uint32 kind)
#endif
JS_PUBLIC_API(void)
JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing, uint32 kind,
JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing, uint32 kindIndex,
JSBool details)
{
JS_ASSERT(kindIndex <= JSTRACE_LAST);
JSGCTraceKind kind = JSGCTraceKind(kindIndex);
const char *name;
size_t n;
@ -2240,19 +2243,23 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing, ui
: "string";
break;
case JSTRACE_SCRIPT:
name = "script";
break;
case JSTRACE_SHAPE:
name = "shape";
break;
case JSTRACE_TYPE_OBJECT:
name = "type_object";
break;
#if JS_HAS_XML_SUPPORT
case JSTRACE_XML:
name = "xml";
break;
#endif
default:
JS_ASSERT(0);
return;
break;
}
n = strlen(name);
@ -2299,12 +2306,17 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing, ui
break;
}
case JSTRACE_SHAPE:
case JSTRACE_SCRIPT:
{
JS_snprintf(buf, bufsize, "<shape>");
JSScript *script = static_cast<JSScript *>(thing);
JS_snprintf(buf, bufsize, "%s:%u", script->filename, unsigned(script->lineno));
break;
}
case JSTRACE_SHAPE:
case JSTRACE_TYPE_OBJECT:
break;
#if JS_HAS_XML_SUPPORT
case JSTRACE_XML:
{
@ -2315,9 +2327,6 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing, ui
break;
}
#endif
default:
JS_ASSERT(0);
break;
}
}
buf[bufsize - 1] = '\0';
@ -3206,7 +3215,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);
}
@ -4743,7 +4751,6 @@ CompileUCFunctionForPrincipalsCommon(JSContext *cx, JSObject *obj,
}
Bindings bindings(cx);
AutoBindingsRooter root(cx, bindings);
for (uintN i = 0; i < nargs; i++) {
uint16 dummy;
JSAtom *argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]));
@ -4842,8 +4849,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,

View File

@ -1601,16 +1601,25 @@ JS_SetExtraGCRoots(JSRuntime *rt, JSTraceDataOp traceOp, void *data);
* other strong ref identified for debugging purposes by name or index or
* a naming callback.
*
* By definition references to traceable things include non-null pointers
* to JSObject, JSString and jsdouble and corresponding jsvals.
*
* See the JSTraceOp typedef in jspubtd.h.
*/
/* Trace kinds to pass to JS_Tracing. */
#define JSTRACE_OBJECT 0
#define JSTRACE_STRING 1
#define JSTRACE_SHAPE 2
typedef enum {
JSTRACE_OBJECT,
JSTRACE_STRING,
JSTRACE_SCRIPT,
/*
* Trace kinds internal to the engine. JSTraceCallback implementation can
* only call JS_TraceChildren on them.
*/
#if JS_HAS_XML_SUPPORT
JSTRACE_XML,
#endif
JSTRACE_SHAPE,
JSTRACE_TYPE_OBJECT,
JSTRACE_LAST = JSTRACE_TYPE_OBJECT
} JSGCTraceKind;
/*
* Use the following macros to check if a particular jsval is a traceable

View File

@ -645,19 +645,16 @@ js_DumpAtoms(JSContext *cx, FILE *fp)
JS_STATIC_ASSERT(TEMP_SIZE_START >= sizeof(JSHashTable));
void
js_InitAtomMap(JSContext *cx, JSAtomMap *map, AtomIndexMap *indices)
js_InitAtomMap(JSContext *cx, AtomIndexMap *indices, JSAtom **atoms)
{
/* Map length must already be initialized. */
JS_ASSERT(indices->count() == map->length);
if (indices->isMap()) {
typedef AtomIndexMap::WordMap WordMap;
const WordMap &wm = indices->asMap();
for (WordMap::Range r = wm.all(); !r.empty(); r.popFront()) {
JSAtom *atom = r.front().key;
jsatomid index = r.front().value;
JS_ASSERT(index < map->length);
map->vector[index] = atom;
JS_ASSERT(index < indices->count());
atoms[index] = atom;
}
} else {
for (const AtomIndexMap::InlineElem *it = indices->asInline(), *end = indices->inlineEnd();
@ -665,8 +662,8 @@ js_InitAtomMap(JSContext *cx, JSAtomMap *map, AtomIndexMap *indices)
JSAtom *atom = it->key;
if (!atom)
continue;
JS_ASSERT(it->value < map->length);
map->vector[it->value] = atom;
JS_ASSERT(it->value < indices->count());
atoms[it->value] = atom;
}
}
}

View File

@ -162,11 +162,6 @@ struct DefaultHasher<jsid>
extern const char *
js_AtomToPrintableString(JSContext *cx, JSAtom *atom, JSAutoByteString *bytes);
struct JSAtomMap {
JSAtom **vector; /* array of ptrs to indexed atoms */
uint32 length; /* count of (to-be-)indexed atoms */
};
namespace js {
typedef TaggedPointerEntry<JSAtom> AtomStateEntry;
@ -542,6 +537,6 @@ js_InternNonIntElementId(JSContext *cx, JSObject *obj, const js::Value &idval,
* the list and map->vector must point to pre-allocated memory.
*/
extern void
js_InitAtomMap(JSContext *cx, JSAtomMap *map, js::AtomIndexMap *indices);
js_InitAtomMap(JSContext *cx, js::AtomIndexMap *indices, JSAtom **atoms);
#endif /* jsatom_h___ */

View File

@ -114,6 +114,9 @@ ThreadData::ThreadData()
pendingProxyOperation(NULL),
interpreterFrames(NULL)
{
#ifdef DEBUG
noGCOrAllocationCheck = 0;
#endif
}
ThreadData::~ThreadData()

View File

@ -209,6 +209,10 @@ struct ThreadData {
ConservativeGCThreadData conservativeGC;
#ifdef DEBUG
size_t noGCOrAllocationCheck;
#endif
ThreadData();
~ThreadData();
@ -1395,7 +1399,7 @@ FrameAtomBase(JSContext *cx, js::StackFrame *fp)
{
return fp->hasImacropc()
? cx->runtime->atomState.commonAtomsStart()
: fp->script()->atomMap.vector;
: fp->script()->atoms;
}
struct AutoResolving {
@ -1478,9 +1482,9 @@ class AutoGCRooter {
enum {
JSVAL = -1, /* js::AutoValueRooter */
SHAPE = -2, /* js::AutoShapeRooter */
VALARRAY = -2, /* js::AutoValueArrayRooter */
PARSER = -3, /* js::Parser */
SCRIPT = -4, /* js::AutoScriptRooter */
SHAPEVECTOR = -4, /* js::AutoShapeVector */
ENUMERATOR = -5, /* js::AutoEnumStateRooter */
IDARRAY = -6, /* js::AutoIdArray */
DESCRIPTORS = -7, /* js::AutoPropDescArrayRooter */
@ -1492,11 +1496,8 @@ class AutoGCRooter {
DESCRIPTOR = -13, /* js::AutoPropertyDescriptorRooter */
STRING = -14, /* js::AutoStringRooter */
IDVECTOR = -15, /* js::AutoIdVector */
BINDINGS = -16, /* js::Bindings */
SHAPEVECTOR = -17, /* js::AutoShapeVector */
OBJVECTOR = -18, /* js::AutoObjectVector */
TYPE = -19, /* js::types::AutoTypeRooter */
VALARRAY = -20 /* js::AutoValueArrayRooter */
OBJVECTOR = -16, /* js::AutoObjectVector */
TYPE = -17 /* js::types::AutoTypeRooter */
};
private:
@ -1667,43 +1668,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:
@ -1828,22 +1792,6 @@ class AutoXMLRooter : private AutoGCRooter {
};
#endif /* JS_HAS_XML_SUPPORT */
class AutoBindingsRooter : private AutoGCRooter {
public:
AutoBindingsRooter(JSContext *cx, Bindings &bindings
JS_GUARD_OBJECT_NOTIFIER_PARAM)
: AutoGCRooter(cx, BINDINGS), bindings(bindings)
{
JS_GUARD_OBJECT_NOTIFIER_INIT;
}
friend void AutoGCRooter::trace(JSTracer *trc);
private:
Bindings &bindings;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
};
class AutoLockGC {
public:
explicit AutoLockGC(JSRuntime *rt

View File

@ -210,7 +210,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

@ -100,9 +100,7 @@ JSCompartment::JSCompartment(JSRuntime *rt)
breakpointSites(rt),
watchpointMap(NULL)
{
JS_INIT_CLIST(&scripts);
PodArrayZero(scriptsToGC);
PodArrayZero(evalCache);
}
JSCompartment::~JSCompartment()
@ -123,8 +121,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
}
@ -492,27 +490,6 @@ JSCompartment::markCrossCompartmentWrappers(JSTracer *trc)
MarkValue(trc, e.front().key, "cross-compartment wrapper");
}
struct MarkSingletonObjectOp
{
JSTracer *trc;
MarkSingletonObjectOp(JSTracer *trc) : trc(trc) {}
void operator()(Cell *cell) {
JSObject *object = static_cast<JSObject *>(cell);
if (!object->isNewborn() && object->hasSingletonType())
MarkObject(trc, *object, "mark_types_singleton");
}
};
struct MarkTypeObjectOp
{
JSTracer *trc;
MarkTypeObjectOp(JSTracer *trc) : trc(trc) {}
void operator()(Cell *cell) {
types::TypeObject *object = static_cast<types::TypeObject *>(cell);
MarkTypeObject(trc, object, "mark_types_scan");
}
};
void
JSCompartment::markTypes(JSTracer *trc)
{
@ -523,20 +500,23 @@ JSCompartment::markTypes(JSTracer *trc)
*/
JS_ASSERT(activeAnalysis);
for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
JSScript *script = reinterpret_cast<JSScript *>(cursor);
js_TraceScript(trc, script, NULL);
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
MarkScript(trc, script, "mark_types_script");
}
MarkSingletonObjectOp objectCellOp(trc);
for (unsigned thingKind = FINALIZE_OBJECT0;
thingKind <= FINALIZE_FUNCTION_AND_OBJECT_LAST;
thingKind++) {
gc::ForEachArenaAndCell(this, (FinalizeKind) thingKind, EmptyArenaOp, objectCellOp);
for (CellIterUnderGC i(this, FinalizeKind(thingKind)); !i.done(); i.next()) {
JSObject *object = i.get<JSObject>();
if (!object->isNewborn() && object->hasSingletonType())
MarkObject(trc, *object, "mark_types_singleton");
}
}
MarkTypeObjectOp typeCellOp(trc);
gc::ForEachArenaAndCell(this, FINALIZE_TYPE_OBJECT, EmptyArenaOp, typeCellOp);
for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next())
MarkTypeObject(trc, i.get<types::TypeObject>(), "mark_types_scan");
}
void
@ -584,8 +564,8 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
* Purge all PICs in the compartment. These can reference type data and
* need to know which types are pending collection.
*/
for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
JSScript *script = reinterpret_cast<JSScript *>(cursor);
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->hasJITCode())
mjit::ic::PurgePICs(cx, script);
}
@ -606,8 +586,8 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
if (discardScripts)
hasDebugModeCodeToDrop = false;
for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
JSScript *script = reinterpret_cast<JSScript *>(cursor);
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->hasJITCode()) {
mjit::ic::SweepCallICs(cx, script, discardScripts);
if (discardScripts) {
@ -636,9 +616,8 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
#ifdef JS_METHODJIT
mjit::ClearAllFrames(this);
#endif
for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
JSScript *script = reinterpret_cast<JSScript *>(cursor);
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->types) {
types::TypeScript::Sweep(cx, script);
@ -657,20 +636,14 @@ JSCompartment::sweep(JSContext *cx, uint32 releaseInterval)
types.sweep(cx);
for (JSCList *cursor = scripts.next; cursor != &scripts; cursor = cursor->next) {
JSScript *script = reinterpret_cast<JSScript *>(cursor);
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->types)
script->types->analysis = NULL;
}
/* Reset the analysis pool, releasing all analysis and intermediate type data. */
JS_FinishArenaPool(&oldPool);
/*
* Destroy eval'ed scripts, now that any type inference information referring
* to eval scripts has been removed.
*/
js_DestroyScriptsToGC(cx, this);
}
active = false;
@ -682,6 +655,21 @@ JSCompartment::purge(JSContext *cx)
freeLists.purge();
dtoaCache.purge();
/*
* Clear the hash and reset all evalHashLink to null before the GC. This
* way MarkChildren(trc, JSScript *) can assume that JSScript::u.object is
* not null when we have script owned by an object and not from the eval
* cache.
*/
for (size_t i = 0; i != JS_ARRAY_LENGTH(evalCache); ++i) {
for (JSScript **listHeadp = &evalCache[i]; *listHeadp; ) {
JSScript *script = *listHeadp;
JS_ASSERT(GetGCThingTraceKind(script) == JSTRACE_SCRIPT);
*listHeadp = NULL;
listHeadp = &script->u.evalHashLink;
}
}
nativeIterCache.purge();
toSourceCache.destroyIfConstructed();
@ -695,19 +683,16 @@ JSCompartment::purge(JSContext *cx)
traceMonitor()->needFlush = JS_TRUE;
#endif
#ifdef JS_METHODJIT
for (JSScript *script = (JSScript *)scripts.next;
&script->links != &scripts;
script = (JSScript *)script->links.next) {
if (script->hasJITCode()) {
# if defined JS_MONOIC
/*
* MICs do not refer to data which can be GC'ed and do not generate stubs
* which might need to be discarded, but are sensitive to shape regeneration.
*/
if (cx->runtime->gcRegenShapes)
#if defined JS_METHODJIT && defined JS_MONOIC
/*
* MICs do not refer to data which can be GC'ed and do not generate stubs
* which might need to be discarded, but are sensitive to shape regeneration.
*/
if (cx->runtime->gcRegenShapes) {
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->hasJITCode())
mjit::ic::PurgeMICs(cx, script);
# endif
}
}
#endif
@ -761,7 +746,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;
@ -818,12 +803,12 @@ JSCompartment::updateForDebugMode(JSContext *cx)
return;
}
// Discard JIT code for any scripts that change debugMode. This assumes
// that 'comp' is in the same thread as 'cx'.
for (JSScript *script = (JSScript *) scripts.next;
&script->links != &scripts;
script = (JSScript *) script->links.next)
{
/*
* Discard JIT code for any scripts that change debugMode. This assumes
* that 'comp' is in the same thread as 'cx'.
*/
for (gc::CellIter i(cx, this, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->debugMode != enabled) {
mjit::ReleaseScriptCode(cx, script);
script->debugMode = enabled;
@ -905,7 +890,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 {
@ -426,7 +427,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
@ -507,8 +508,6 @@ struct JS_FRIEND_API(JSCompartment) {
uintN debugModeBits; // see debugMode() below
public:
JSCList scripts; // scripts in this compartment
js::NativeIterCache nativeIterCache;
typedef js::Maybe<js::ToSourceCache> LazyToSourceCache;
@ -540,6 +539,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);
@ -633,7 +633,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

@ -182,7 +182,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) {
@ -212,7 +212,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));
@ -222,7 +222,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);
}
@ -230,7 +230,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)
@ -243,7 +243,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)
@ -1089,9 +1089,9 @@ JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
nbytes += JS_GetObjectTotalSize(cx, script->u.object);
nbytes += script->length * sizeof script->code[0];
nbytes += script->atomMap.length * sizeof script->atomMap.vector[0];
for (size_t i = 0; i < script->atomMap.length; i++)
nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]);
nbytes += script->natoms * sizeof script->atoms[0];
for (size_t i = 0; i < script->natoms; i++)
nbytes += GetAtomTotalSize(cx, script->atoms[i]);
if (script->filename)
nbytes += strlen(script->filename) + 1;
@ -2178,13 +2178,18 @@ JS_DumpBytecode(JSContext *cx, JSScript *script)
#endif
}
static void
DumpBytecodeScriptCallback(JSContext *cx, void *data, void *thing,
JSGCTraceKind traceKind, size_t thingSize)
{
JS_ASSERT(traceKind == JSTRACE_SCRIPT);
JS_ASSERT(!data);
JSScript *script = static_cast<JSScript *>(thing);
JS_DumpBytecode(cx, script);
}
JS_PUBLIC_API(void)
JS_DumpCompartmentBytecode(JSContext *cx)
{
for (JSScript *script = (JSScript *) JS_LIST_HEAD(&cx->compartment->scripts);
script != (JSScript *) &cx->compartment->scripts;
script = (JSScript *) JS_NEXT_LINK((JSCList *)script))
{
JS_DumpBytecode(cx, script);
}
IterateCells(cx, cx->compartment, gc::FINALIZE_SCRIPT, NULL, DumpBytecodeScriptCallback);
}

View File

@ -207,7 +207,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));
@ -1632,9 +1631,6 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
if (xdr->mode == JSXDR_DECODE) {
*objp = fun;
fun->u.i.script->setOwnerObject(fun);
#ifdef CHECK_SCRIPT_OWNER
fun->script()->owner = NULL;
#endif
if (!fun->u.i.script->typeSetFunction(cx, fun))
return false;
JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
@ -1705,30 +1701,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();
}
/*
@ -1740,7 +1722,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 */
@ -2180,8 +2163,6 @@ Function(JSContext *cx, uintN argc, Value *vp)
}
Bindings bindings(cx);
AutoBindingsRooter root(cx, bindings);
uintN lineno;
const char *filename = CurrentScriptFileAndLine(cx, &lineno);
@ -2411,9 +2392,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;
fun->getType(cx)->functionScript = script;
script->hasFunction = true;
@ -2532,8 +2510,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);
cfun->u.i.script = NULL;
@ -2546,9 +2524,6 @@ js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
return NULL;
cfun->script()->setOwnerObject(cfun);
#ifdef CHECK_SCRIPT_OWNER
cfun->script()->owner = NULL;
#endif
js_CallNewScriptHook(cx, cfun->script(), cfun);
Debugger::onNewScript(cx, cfun->script(), cfun, Debugger::NewHeldScript);
}

View File

@ -106,21 +106,6 @@
using namespace js;
using namespace js::gc;
/*
* Check that JSTRACE_XML follows JSTRACE_OBJECT and JSTRACE_STRING.
*/
JS_STATIC_ASSERT(JSTRACE_OBJECT == 0);
JS_STATIC_ASSERT(JSTRACE_STRING == 1);
JS_STATIC_ASSERT(JSTRACE_SHAPE == 2);
JS_STATIC_ASSERT(JSTRACE_TYPE_OBJECT == 3);
JS_STATIC_ASSERT(JSTRACE_XML == 4);
/*
* JS_IS_VALID_TRACE_KIND assumes that JSTRACE_TYPE_OBJECT is the last non-xml
* trace kind when JS_HAS_XML_SUPPORT is false.
*/
JS_STATIC_ASSERT(JSTRACE_TYPE_OBJECT + 1 == JSTRACE_XML);
namespace js {
namespace gc {
@ -149,6 +134,7 @@ const uint8 GCThingSizeMap[] = {
sizeof(JSObject_Slots16), /* FINALIZE_OBJECT16 */
sizeof(JSObject_Slots16), /* FINALIZE_OBJECT16_BACKGROUND */
sizeof(JSFunction), /* FINALIZE_FUNCTION */
sizeof(JSScript), /* FINALIZE_SCRIPT */
sizeof(Shape), /* FINALIZE_SHAPE */
sizeof(types::TypeObject), /* FINALIZE_TYPE_OBJECT */
#if JS_HAS_XML_SUPPORT
@ -198,6 +184,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 +777,9 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w)
case FINALIZE_FUNCTION:
test = MarkArenaPtrConservatively<JSFunction>(trc, aheader, addr);
break;
case FINALIZE_SCRIPT:
test = MarkArenaPtrConservatively<JSScript>(trc, aheader, addr);
break;
case FINALIZE_SHAPE:
test = MarkArenaPtrConservatively<Shape>(trc, aheader, addr);
break;
@ -1253,6 +1246,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);
@ -1264,7 +1258,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;
@ -1305,6 +1305,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;
@ -1463,6 +1466,8 @@ RefillFinalizableFreeList(JSContext *cx, unsigned thingKind)
return RefillTypedFreeList<JSShortString>(cx, thingKind);
case FINALIZE_FUNCTION:
return RefillTypedFreeList<JSFunction>(cx, thingKind);
case FINALIZE_SCRIPT:
return RefillTypedFreeList<JSScript>(cx, thingKind);
case FINALIZE_SHAPE:
return RefillTypedFreeList<Shape>(cx, thingKind);
case FINALIZE_TYPE_OBJECT:
@ -1480,7 +1485,7 @@ RefillFinalizableFreeList(JSContext *cx, unsigned thingKind)
} /* namespace gc */
} /* namespace js */
uint32
JSGCTraceKind
js_GetGCThingTraceKind(void *thing)
{
return GetGCThingTraceKind(thing);
@ -1578,7 +1583,7 @@ GCMarker::delayMarkingChildren(const void *thing)
static void
MarkDelayedChildren(JSTracer *trc, ArenaHeader *aheader)
{
unsigned traceKind = GetFinalizableTraceKind(aheader->getThingKind());
JSGCTraceKind traceKind = GetFinalizableTraceKind(aheader->getThingKind());
size_t thingSize = aheader->getThingSize();
Arena *a = aheader->getArena();
uintptr_t end = a->thingsEnd();
@ -1675,8 +1680,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");
}
@ -1701,19 +1706,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;
@ -1793,23 +1789,12 @@ AutoGCRooter::trace(JSTracer *trc)
return;
}
case BINDINGS: {
static_cast<js::AutoBindingsRooter *>(this)->bindings.trace(trc);
return;
}
case OBJVECTOR: {
AutoObjectVector::VectorImpl &vector = static_cast<AutoObjectVector *>(this)->vector;
MarkObjectRange(trc, vector.length(), vector.begin(), "js::AutoObjectVector.vector");
return;
}
case TYPE: {
types::TypeObject *type = static_cast<types::AutoTypeRooter *>(this)->type;
MarkTypeObject(trc, type, "js::AutoTypeRooter");
return;
}
case VALARRAY: {
AutoValueArray *array = static_cast<AutoValueArray *>(this);
MarkValueRange(trc, array->length(), array->start(), "js::AutoValueArray");
@ -1988,21 +1973,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)
{
@ -2012,7 +1982,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);
@ -2023,6 +1992,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
@ -2048,6 +2028,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 {
@ -2117,6 +2103,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)
{
@ -2160,6 +2155,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);
@ -2376,6 +2376,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);
@ -2401,6 +2403,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);
@ -2553,6 +2561,7 @@ class AutoGCSession {
AutoGCSession::AutoGCSession(JSContext *cx)
: context(cx)
{
JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck);
JSRuntime *rt = cx->runtime;
#ifdef JS_THREADSAFE
@ -2679,8 +2688,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);
@ -2848,10 +2857,10 @@ struct IterateArenaCallbackOp
JSContext *cx;
void *data;
IterateArenaCallback callback;
size_t traceKind;
JSGCTraceKind traceKind;
size_t thingSize;
IterateArenaCallbackOp(JSContext *cx, void *data, IterateArenaCallback callback,
size_t traceKind, size_t thingSize)
JSGCTraceKind traceKind, size_t thingSize)
: cx(cx), data(data), callback(callback), traceKind(traceKind), thingSize(thingSize)
{}
void operator()(Arena *arena) { (*callback)(cx, data, arena, traceKind, thingSize); }
@ -2862,10 +2871,10 @@ struct IterateCellCallbackOp
JSContext *cx;
void *data;
IterateCellCallback callback;
size_t traceKind;
JSGCTraceKind traceKind;
size_t thingSize;
IterateCellCallbackOp(JSContext *cx, void *data, IterateCellCallback callback,
size_t traceKind, size_t thingSize)
JSGCTraceKind traceKind, size_t thingSize)
: cx(cx), data(data), callback(callback), traceKind(traceKind), thingSize(thingSize)
{}
void operator()(Cell *cell) { (*callback)(cx, data, cell, traceKind, thingSize); }
@ -2896,7 +2905,7 @@ IterateCompartmentsArenasCells(JSContext *cx, void *data,
(*compartmentCallback)(cx, data, compartment);
for (unsigned thingKind = 0; thingKind < FINALIZE_LIMIT; thingKind++) {
size_t traceKind = GetFinalizableTraceKind(thingKind);
JSGCTraceKind traceKind = GetFinalizableTraceKind(thingKind);
size_t thingSize = GCThingSizeMap[thingKind];
IterateArenaCallbackOp arenaOp(cx, data, arenaCallback, traceKind, thingSize);
IterateCellCallbackOp cellOp(cx, data, cellCallback, traceKind, thingSize);
@ -2927,11 +2936,18 @@ IterateCells(JSContext *cx, JSCompartment *compartment, FinalizeKind thingKind,
AutoCopyFreeListToArenas copy(rt);
size_t traceKind = GetFinalizableTraceKind(thingKind);
JSGCTraceKind traceKind = GetFinalizableTraceKind(thingKind);
size_t thingSize = GCThingSizeMap[thingKind];
IterateCellCallbackOp cellOp(cx, data, cellCallback, traceKind, thingSize);
ForEachArenaAndCell(compartment, thingKind, EmptyArenaOp, cellOp);
if (compartment) {
for (CellIterUnderGC i(compartment, thingKind); !i.done(); i.next())
cellCallback(cx, data, i.getCell(), traceKind, thingSize);
} else {
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
for (CellIterUnderGC i(*c, thingKind); !i.done(); i.next())
cellCallback(cx, data, i.getCell(), traceKind, thingSize);
}
}
}
namespace gc {

View File

@ -97,6 +97,7 @@ enum FinalizeKind {
FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND,
FINALIZE_FUNCTION,
FINALIZE_FUNCTION_AND_OBJECT_LAST = FINALIZE_FUNCTION,
FINALIZE_SCRIPT,
FINALIZE_SHAPE,
FINALIZE_TYPE_OBJECT,
#if JS_HAS_XML_SUPPORT
@ -108,6 +109,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;
@ -756,14 +763,6 @@ Cell::compartment() const
return arenaHeader()->compartment;
}
#define JSTRACE_TYPE_OBJECT 3
#define JSTRACE_XML 4
/*
* One past the maximum trace kind.
*/
#define JSTRACE_LIMIT 5
/*
* Lower limit after which we limit the heap growth
*/
@ -779,12 +778,10 @@ const float GC_HEAP_GROWTH_FACTOR = 3.0f;
/* Perform a Full GC every 20 seconds if MaybeGC is called */
static const int64 GC_IDLE_FULL_SPAN = 20 * 1000 * 1000;
static inline size_t
static inline JSGCTraceKind
GetFinalizableTraceKind(size_t thingKind)
{
JS_STATIC_ASSERT(JSExternalString::TYPE_LIMIT == 8);
static const uint8 map[FINALIZE_LIMIT] = {
static const JSGCTraceKind map[FINALIZE_LIMIT] = {
JSTRACE_OBJECT, /* FINALIZE_OBJECT0 */
JSTRACE_OBJECT, /* FINALIZE_OBJECT0_BACKGROUND */
JSTRACE_OBJECT, /* FINALIZE_OBJECT2 */
@ -798,6 +795,7 @@ GetFinalizableTraceKind(size_t thingKind)
JSTRACE_OBJECT, /* FINALIZE_OBJECT16 */
JSTRACE_OBJECT, /* FINALIZE_OBJECT16_BACKGROUND */
JSTRACE_OBJECT, /* FINALIZE_FUNCTION */
JSTRACE_SCRIPT, /* FINALIZE_SCRIPT */
JSTRACE_SHAPE, /* FINALIZE_SHAPE */
JSTRACE_TYPE_OBJECT,/* FINALIZE_TYPE_OBJECT */
#if JS_HAS_XML_SUPPORT /* FINALIZE_XML */
@ -812,7 +810,7 @@ GetFinalizableTraceKind(size_t thingKind)
return map[thingKind];
}
inline uint32
inline JSGCTraceKind
GetGCThingTraceKind(const void *thing);
static inline JSRuntime *
@ -881,6 +879,10 @@ class ArenaList {
bool willBeFinalizedLater() const {
return backgroundFinalizeState == BFS_RUN;
}
bool doneBackgroundFinalize() const {
return backgroundFinalizeState == BFS_DONE;
}
#endif
#ifdef DEBUG
@ -966,13 +968,16 @@ struct FreeLists {
* outside the GC.
*/
void copyToArenas() {
for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) {
FreeSpan *list = &lists[i];
if (!list->isEmpty()) {
ArenaHeader *aheader = list->arenaHeader();
JS_ASSERT(!aheader->hasFreeThings());
aheader->setFirstFreeSpan(list);
}
for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i)
copyToArena(FinalizeKind(i));
}
void copyToArena(FinalizeKind thingKind) {
FreeSpan *list = &lists[thingKind];
if (!list->isEmpty()) {
ArenaHeader *aheader = list->arenaHeader();
JS_ASSERT(!aheader->hasFreeThings());
aheader->setFirstFreeSpan(list);
}
}
@ -981,16 +986,40 @@ struct FreeLists {
* copyToArenas.
*/
void clearInArenas() {
for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i) {
FreeSpan *list = &lists[i];
if (!list->isEmpty()) {
ArenaHeader *aheader = list->arenaHeader();
JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(list));
aheader->setAsFullyUsed();
}
for (size_t i = 0; i != size_t(FINALIZE_LIMIT); ++i)
clearInArena(FinalizeKind(i));
}
void clearInArena(FinalizeKind thingKind) {
FreeSpan *list = &lists[thingKind];
if (!list->isEmpty()) {
ArenaHeader *aheader = list->arenaHeader();
JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(list));
aheader->setAsFullyUsed();
}
}
/*
* Check that the free list is either empty or were synchronized with the
* arena using copyToArena().
*/
bool isSynchronizedWithArena(FinalizeKind thingKind) {
FreeSpan *list = &lists[thingKind];
if (list->isEmpty())
return true;
ArenaHeader *aheader = list->arenaHeader();
if (aheader->hasFreeThings()) {
/*
* If the arena has a free list, it must be the same as one in
* lists.
*/
JS_ASSERT(aheader->getFirstFreeSpan().isSameNonEmptySpan(list));
return true;
}
return false;
}
JS_ALWAYS_INLINE void *getNext(unsigned thingKind, size_t thingSize) {
return lists[thingKind].allocate(thingSize);
}
@ -1079,7 +1108,7 @@ extern bool
CheckAllocation(JSContext *cx);
#endif
extern JS_FRIEND_API(uint32)
extern JS_FRIEND_API(JSGCTraceKind)
js_GetGCThingTraceKind(void *thing);
extern JSBool
@ -1194,10 +1223,6 @@ js_WaitForGC(JSRuntime *rt);
#endif
extern void
js_DestroyScriptsToGC(JSContext *cx, JSCompartment *comp);
namespace js {
#ifdef JS_THREADSAFE
@ -1273,7 +1298,7 @@ class GCHelperThread {
replenishAndFreeLater(ptr);
}
void setContext(JSContext *context) { cx = context; }
bool prepareForBackgroundSweep(JSContext *context);
};
#endif /* JS_THREADSAFE */
@ -1474,10 +1499,10 @@ void
MarkStackRangeConservatively(JSTracer *trc, Value *begin, Value *end);
typedef void (*IterateCompartmentCallback)(JSContext *cx, void *data, JSCompartment *compartment);
typedef void (*IterateArenaCallback)(JSContext *cx, void *data, gc::Arena *arena, size_t traceKind,
size_t thingSize);
typedef void (*IterateCellCallback)(JSContext *cx, void *data, void *thing, size_t traceKind,
size_t thingSize);
typedef void (*IterateArenaCallback)(JSContext *cx, void *data, gc::Arena *arena,
JSGCTraceKind traceKind, size_t thingSize);
typedef void (*IterateCellCallback)(JSContext *cx, void *data, void *thing,
JSGCTraceKind traceKind, size_t thingSize);
/*
* This function calls |compartmentCallback| on every compartment,
@ -1490,8 +1515,11 @@ IterateCompartmentsArenasCells(JSContext *cx, void *data,
IterateArenaCallback arenaCallback,
IterateCellCallback cellCallback);
/* Invoke cellCallback on every in-use object of the specified thing kind. */
void
/*
* Invoke cellCallback on every in-use object of the specified thing kind for
* the given compartment or for all compartments if it is null.
*/
extern JS_FRIEND_API(void)
IterateCells(JSContext *cx, JSCompartment *compartment, gc::FinalizeKind thingKind,
void *data, IterateCellCallback cellCallback);
@ -1505,12 +1533,6 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str);
*/
#define IS_GC_MARKING_TRACER(trc) ((trc)->callback == NULL)
#if JS_HAS_XML_SUPPORT
# define JS_IS_VALID_TRACE_KIND(kind) ((uint32)(kind) < JSTRACE_LIMIT)
#else
# define JS_IS_VALID_TRACE_KIND(kind) ((uint32)(kind) <= JSTRACE_TYPE_OBJECT)
#endif
namespace js {
namespace gc {

View File

@ -112,7 +112,7 @@ struct Shape;
namespace gc {
inline uint32
inline JSGCTraceKind
GetGCThingTraceKind(const void *thing)
{
JS_ASSERT(thing);
@ -256,6 +256,118 @@ ForEachArenaAndCell(JSCompartment *compartment, FinalizeKind thingKind,
}
}
class CellIterImpl
{
size_t thingSize;
ArenaHeader *aheader;
FreeSpan firstSpan;
const FreeSpan *span;
uintptr_t thing;
Cell *cell;
protected:
CellIterImpl() {
}
void init(JSCompartment *comp, FinalizeKind thingKind) {
thingSize = GCThingSizeMap[thingKind];
aheader = comp->arenas[thingKind].getHead();
firstSpan.initAsEmpty();
span = &firstSpan;
thing = span->first;
next();
}
public:
bool done() const {
return !cell;
}
template<typename T> T *get() const {
JS_ASSERT(!done());
return static_cast<T *>(cell);
}
Cell *getCell() const {
JS_ASSERT(!done());
return cell;
}
void next() {
for (;;) {
if (thing != span->first)
break;
if (JS_LIKELY(span->hasNext())) {
thing = span->last + thingSize;
span = span->nextSpan();
break;
}
if (!aheader) {
cell = NULL;
return;
}
firstSpan = aheader->getFirstFreeSpan();
span = &firstSpan;
thing = aheader->getArena()->thingsStart(thingSize);
aheader = aheader->next;
}
cell = reinterpret_cast<Cell *>(thing);
thing += thingSize;
}
};
class CellIterUnderGC : public CellIterImpl {
public:
CellIterUnderGC(JSCompartment *comp, FinalizeKind thingKind) {
JS_ASSERT(comp->rt->gcRunning);
JS_ASSERT(comp->freeLists.lists[thingKind].isEmpty());
init(comp, thingKind);
}
};
/*
* When using the iterator outside the GC the caller must ensure that no GC or
* allocations of GC things are possible and that the background finalization
* for the given thing kind is not enabled or is done.
*/
class CellIter: public CellIterImpl
{
FreeLists *lists;
FinalizeKind thingKind;
#ifdef DEBUG
size_t *counter;
#endif
public:
CellIter(JSContext *cx, JSCompartment *comp, FinalizeKind thingKind)
: lists(&comp->freeLists),
thingKind(thingKind) {
#ifdef JS_THREADSAFE
JS_ASSERT(comp->arenas[thingKind].doneBackgroundFinalize());
#endif
if (lists->isSynchronizedWithArena(thingKind)) {
lists = NULL;
} else {
JS_ASSERT(!comp->rt->gcRunning);
lists->copyToArena(thingKind);
}
#ifdef DEBUG
counter = &JS_THREAD_DATA(cx)->noGCOrAllocationCheck;
++*counter;
#endif
init(comp, thingKind);
}
~CellIter() {
#ifdef DEBUG
JS_ASSERT(*counter > 0);
--*counter;
#endif
if (lists)
lists->clearInArena(thingKind);
}
};
/* Signatures for ArenaOp and CellOp above. */
inline void EmptyArenaOp(Arena *arena) {}
@ -283,6 +395,7 @@ NewGCThing(JSContext *cx, unsigned thingKind, size_t thingSize)
(thingKind == js::gc::FINALIZE_SHORT_STRING));
#endif
JS_ASSERT(!cx->runtime->gcRunning);
JS_ASSERT(!JS_THREAD_DATA(cx)->noGCOrAllocationCheck);
#ifdef JS_GC_ZEAL
if (cx->runtime->needZealousGC())
@ -333,6 +446,12 @@ js_NewGCFunction(JSContext *cx)
return fun;
}
inline JSScript *
js_NewGCScript(JSContext *cx)
{
return NewGCThing<JSScript>(cx, js::gc::FINALIZE_SCRIPT, sizeof(JSScript));
}
inline js::Shape *
js_NewGCShape(JSContext *cx)
{

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
@ -91,6 +92,9 @@ PushMarkStack(GCMarker *gcmarker, JSObject *thing);
static inline void
PushMarkStack(GCMarker *gcmarker, JSFunction *thing);
static inline void
PushMarkStack(GCMarker *gcmarker, JSScript *thing);
static inline void
PushMarkStack(GCMarker *gcmarker, const Shape *thing);
@ -108,7 +112,6 @@ static inline void
CheckMarkedThing(JSTracer *trc, T *thing)
{
JS_ASSERT(thing);
JS_ASSERT(JS_IS_VALID_TRACE_KIND(GetGCThingTraceKind(thing)));
JS_ASSERT(trc->debugPrinter || trc->debugPrintArg);
JS_ASSERT_IF(trc->context->runtime->gcCurrentCompartment, IS_GC_MARKING_TRACER(trc));
@ -194,6 +197,15 @@ MarkObjectWithPrinter(JSTracer *trc, JSObject &obj, JSTraceNamePrinter printer,
Mark(trc, &obj);
}
void
MarkScript(JSTracer *trc, JSScript *script, const char *name)
{
JS_ASSERT(trc);
JS_ASSERT(script);
JS_SET_TRACING_NAME(trc, name);
Mark(trc, script);
}
void
MarkShape(JSTracer *trc, const Shape *shape, const char *name)
{
@ -223,7 +235,7 @@ MarkTypeObject(JSTracer *trc, types::TypeObject *type, const char *name)
if (type->singleton)
MarkObject(trc, *type->singleton, "type_singleton");
if (type->functionScript)
js_TraceScript(trc, type->functionScript, NULL);
MarkScript(trc, type->functionScript, "functionScript");
}
}
@ -287,6 +299,21 @@ PushMarkStack(GCMarker *gcmarker, JSShortString *thing)
(void) thing->markIfUnmarked(gcmarker->getMarkColor());
}
void
PushMarkStack(GCMarker *gcmarker, JSScript *thing)
{
JS_ASSERT_IF(gcmarker->context->runtime->gcCurrentCompartment,
thing->compartment() == gcmarker->context->runtime->gcCurrentCompartment);
/*
* We mark scripts directly rather than pushing on the stack as they can
* refer to other scripts only indirectly (like via nested functions) and
* we cannot get to deep recursion.
*/
if (thing->markIfUnmarked(gcmarker->getMarkColor()))
MarkChildren(gcmarker, thing);
}
static void
ScanShape(GCMarker *gcmarker, const Shape *shape);
@ -301,7 +328,7 @@ PushMarkStack(GCMarker *gcmarker, const Shape *thing)
ScanShape(gcmarker, thing);
}
void
static void
MarkAtomRange(JSTracer *trc, size_t len, JSAtom **vec, const char *name)
{
for (uint32 i = 0; i < len; i++) {
@ -370,30 +397,31 @@ MarkIdRange(JSTracer *trc, size_t len, jsid *vec, const char *name)
}
void
MarkKind(JSTracer *trc, void *thing, uint32 kind)
MarkKind(JSTracer *trc, void *thing, JSGCTraceKind kind)
{
JS_ASSERT(thing);
JS_ASSERT(kind == GetGCThingTraceKind(thing));
switch (kind) {
case JSTRACE_OBJECT:
Mark(trc, reinterpret_cast<JSObject *>(thing));
break;
case JSTRACE_STRING:
MarkString(trc, reinterpret_cast<JSString *>(thing));
break;
case JSTRACE_SHAPE:
Mark(trc, reinterpret_cast<Shape *>(thing));
break;
case JSTRACE_TYPE_OBJECT:
Mark(trc, reinterpret_cast<types::TypeObject *>(thing));
break;
case JSTRACE_OBJECT:
Mark(trc, reinterpret_cast<JSObject *>(thing));
break;
case JSTRACE_STRING:
MarkString(trc, reinterpret_cast<JSString *>(thing));
break;
case JSTRACE_SCRIPT:
Mark(trc, static_cast<JSScript *>(thing));
break;
case JSTRACE_SHAPE:
Mark(trc, reinterpret_cast<Shape *>(thing));
break;
case JSTRACE_TYPE_OBJECT:
Mark(trc, reinterpret_cast<types::TypeObject *>(thing));
break;
#if JS_HAS_XML_SUPPORT
case JSTRACE_XML:
Mark(trc, reinterpret_cast<JSXML *>(thing));
break;
case JSTRACE_XML:
Mark(trc, static_cast<JSXML *>(thing));
break;
#endif
default:
JS_ASSERT(false);
}
}
@ -462,7 +490,7 @@ MarkShapeRange(JSTracer *trc, size_t len, const Shape **vec, const char *name)
/* N.B. Assumes JS_SET_TRACING_NAME/INDEX has already been called. */
void
MarkGCThing(JSTracer *trc, void *thing, uint32 kind)
MarkGCThing(JSTracer *trc, void *thing, JSGCTraceKind kind)
{
if (!thing)
return;
@ -493,7 +521,7 @@ MarkGCThing(JSTracer *trc, void *thing, const char *name, size_t index)
}
void
Mark(JSTracer *trc, void *thing, uint32 kind, const char *name)
Mark(JSTracer *trc, void *thing, JSGCTraceKind kind, const char *name)
{
JS_ASSERT(thing);
JS_SET_TRACING_NAME(trc, name);
@ -512,6 +540,12 @@ MarkRoot(JSTracer *trc, JSString *thing, const char *name)
MarkString(trc, thing, name);
}
void
MarkRoot(JSTracer *trc, JSScript *thing, const char *name)
{
MarkScript(trc, thing, name);
}
void
MarkRoot(JSTracer *trc, const Shape *thing, const char *name)
{
@ -568,19 +602,14 @@ static inline void
ScanValue(GCMarker *gcmarker, const Value &v)
{
if (v.isMarkable()) {
switch (v.gcKind()) {
case JSTRACE_STRING: {
JSGCTraceKind kind = v.gcKind();
if (kind == JSTRACE_STRING) {
JSString *str = (JSString *)v.toGCThing();
if (!str->isStaticAtom())
PushMarkStack(gcmarker, str);
break;
}
case JSTRACE_OBJECT:
} else {
JS_ASSERT(kind == JSTRACE_OBJECT);
PushMarkStack(gcmarker, (JSObject *)v.toGCThing());
break;
case JSTRACE_XML:
PushMarkStack(gcmarker, (JSXML *)v.toGCThing());
break;
}
}
}
@ -792,6 +821,52 @@ MarkChildren(JSTracer *trc, JSString *str)
}
}
void
MarkChildren(JSTracer *trc, JSScript *script)
{
CheckScript(script, NULL);
#ifdef JS_CRASH_DIAGNOSTICS
JSRuntime *rt = trc->context->runtime;
JS_OPT_ASSERT_IF(rt->gcCheckCompartment, script->compartment() == rt->gcCheckCompartment);
#endif
MarkAtomRange(trc, script->natoms, script->atoms, "atoms");
if (JSScript::isValidOffset(script->objectsOffset)) {
JSObjectArray *objarray = script->objects();
MarkObjectRange(trc, objarray->length, objarray->vector, "objects");
}
if (JSScript::isValidOffset(script->regexpsOffset)) {
JSObjectArray *objarray = script->regexps();
MarkObjectRange(trc, objarray->length, objarray->vector, "objects");
}
if (JSScript::isValidOffset(script->constOffset)) {
JSConstArray *constarray = script->consts();
MarkValueRange(trc, constarray->length, constarray->vector, "consts");
}
if (!script->isCachedEval && script->u.object)
MarkObject(trc, *script->u.object, "object");
if (script->hasFunction)
MarkObject(trc, *script->function(), "script_fun");
if (IS_GC_MARKING_TRACER(trc) && script->filename)
js_MarkScriptFilename(script->filename);
script->bindings.trace(trc);
#ifdef JS_METHODJIT
if (script->jitNormal)
script->jitNormal->trace(trc);
if (script->jitCtor)
script->jitCtor->trace(trc);
#endif
}
void
MarkChildren(JSTracer *trc, const Shape *shape)
{
@ -838,7 +913,7 @@ ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
PushMarkStack(gcmarker, type->proto);
if (type->newScript) {
js_TraceScript(gcmarker, type->newScript->script, NULL);
PushMarkStack(gcmarker, type->newScript->script);
PushMarkStack(gcmarker, type->newScript->shape);
}
@ -878,12 +953,12 @@ MarkChildren(JSTracer *trc, types::TypeObject *type)
MarkObject(trc, *type->singleton, "type_singleton");
if (type->newScript) {
js_TraceScript(trc, type->newScript->script, NULL);
MarkScript(trc, type->newScript->script, "type_new_script");
MarkShape(trc, type->newScript->shape, "type_new_shape");
}
if (type->functionScript)
js_TraceScript(trc, type->functionScript, NULL);
MarkScript(trc, type->functionScript, "functionScript");
}
#ifdef JS_HAS_XML_SUPPORT
@ -936,19 +1011,26 @@ GCMarker::drainMarkStack()
} /* namespace js */
JS_PUBLIC_API(void)
JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind)
JS_TraceChildren(JSTracer *trc, void *thing, uint32 kindIndex)
{
JS_ASSERT(kindIndex <= JSTRACE_LAST);
JSGCTraceKind kind = JSGCTraceKind(kindIndex);
switch (kind) {
case JSTRACE_OBJECT:
MarkChildren(trc, (JSObject *)thing);
default:
JS_ASSERT(kind == JSTRACE_OBJECT);
MarkChildren(trc, static_cast<JSObject *>(thing));
break;
case JSTRACE_STRING:
MarkChildren(trc, (JSString *)thing);
MarkChildren(trc, static_cast<JSString *>(thing));
break;
case JSTRACE_SCRIPT:
MarkChildren(trc, static_cast<JSScript *>(thing));
break;
case JSTRACE_SHAPE:
MarkChildren(trc, (js::Shape *)thing);
MarkChildren(trc, static_cast<Shape *>(thing));
break;
case JSTRACE_TYPE_OBJECT:
@ -957,7 +1039,7 @@ JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind)
#if JS_HAS_XML_SUPPORT
case JSTRACE_XML:
MarkChildren(trc, (JSXML *)thing);
MarkChildren(trc, static_cast<JSXML *>(thing));
break;
#endif
}

View File

@ -73,6 +73,9 @@ void
MarkObjectWithPrinter(JSTracer *trc, JSObject &obj, JSTraceNamePrinter printer,
const void *arg, size_t index);
void
MarkScript(JSTracer *trc, JSScript *script, const char *name);
void
MarkShape(JSTracer *trc, const Shape *shape, const char *name);
@ -82,9 +85,6 @@ MarkTypeObject(JSTracer *trc, types::TypeObject *type, const char *name);
void
MarkXML(JSTracer *trc, JSXML *xml, const char *name);
void
MarkAtomRange(JSTracer *trc, size_t len, JSAtom **vec, const char *name);
void
MarkObjectRange(JSTracer *trc, size_t len, JSObject **vec, const char *name);
@ -104,7 +104,7 @@ void
MarkIdRange(JSTracer *trc, size_t len, jsid *vec, const char *name);
void
MarkKind(JSTracer *trc, void *thing, uint32 kind);
MarkKind(JSTracer *trc, void *thing, JSGCTraceKind kind);
void
MarkValueRaw(JSTracer *trc, const js::Value &v);
@ -153,6 +153,9 @@ MarkRoot(JSTracer *trc, JSObject *thing, const char *name);
void
MarkRoot(JSTracer *trc, JSString *thing, const char *name);
void
MarkRoot(JSTracer *trc, JSScript *thing, const char *name);
void
MarkRoot(JSTracer *trc, const Shape *thing, const char *name);
@ -171,6 +174,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

@ -155,18 +155,11 @@ GCMarker::dumpConservativeRoots()
fprintf(fp, " %p: ", thing);
switch (GetGCThingTraceKind(thing)) {
default:
JS_NOT_REACHED("Unknown trace kind");
case JSTRACE_OBJECT: {
JSObject *obj = (JSObject *) thing;
fprintf(fp, "object %s", obj->getClass()->name);
break;
}
case JSTRACE_SHAPE: {
fprintf(fp, "shape");
break;
}
case JSTRACE_STRING: {
JSString *str = (JSString *) thing;
if (str->isLinear()) {
@ -178,6 +171,18 @@ GCMarker::dumpConservativeRoots()
}
break;
}
case JSTRACE_SCRIPT: {
fprintf(fp, "shape");
break;
}
case JSTRACE_SHAPE: {
fprintf(fp, "shape");
break;
}
case JSTRACE_TYPE_OBJECT: {
fprintf(fp, "type_object");
break;
}
# if JS_HAS_XML_SUPPORT
case JSTRACE_XML: {
JSXML *xml = (JSXML *) thing;
@ -258,7 +263,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 +281,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 +304,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 +314,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

@ -2063,16 +2063,14 @@ TypeCompartment::nukeTypes(JSContext *cx)
mjit::ExpandInlineFrames(compartment);
/* Throw away all JIT code in the compartment, but leave everything else alone. */
for (JSCList *cursor = compartment->scripts.next;
cursor != &compartment->scripts;
cursor = cursor->next) {
JSScript *script = reinterpret_cast<JSScript *>(cursor);
for (gc::CellIter i(cx, cx->compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->hasJITCode()) {
mjit::Recompiler recompiler(cx, script);
recompiler.recompile();
}
}
#endif /* JS_METHODJIT */
}
@ -2140,40 +2138,6 @@ TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32 offset,
ObjectStateChange(cx, script->function()->type(), false, true);
}
/*
* State for keeping track of which property type sets contain an object we are
* scrubbing from all properties in the compartment. We make a list of
* properties to update and fix them afterwards, as adding types can't be done
* with the GC locked (as is done in IterateCells), and can potentially make
* new type objects as well.
*/
struct MarkSetsUnknownState
{
TypeObject *target;
Vector<TypeSet *> pending;
MarkSetsUnknownState(JSContext *cx, TypeObject *target)
: target(target), pending(cx)
{}
};
static void
MarkObjectSetsUnknownCallback(JSContext *cx, void *data, void *thing,
size_t traceKind, size_t thingSize)
{
MarkSetsUnknownState *state = (MarkSetsUnknownState *) data;
TypeObject *object = (TypeObject *) thing;
unsigned count = object->getPropertyCount();
for (unsigned i = 0; i < count; i++) {
Property *prop = object->getProperty(i);
if (prop && prop->types.hasType(Type::ObjectType(state->target))) {
if (!state->pending.append(&prop->types))
cx->compartment->types.setPendingNukeTypes(cx);
}
}
}
void
TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
{
@ -2190,20 +2154,30 @@ TypeCompartment::markSetsUnknown(JSContext *cx, TypeObject *target)
* a generic object type. It is not sufficient to mark just the persistent
* sets, as analysis of individual opcodes can pull type objects from
* static information (like initializer objects at various offsets).
*
* We make a list of properties to update and fix them afterwards, as adding
* types can't be done while iterating over cells as it can potentially make
* new type objects as well or trigger GC.
*/
Vector<TypeSet *> pending(cx);
for (gc::CellIter i(cx, cx->compartment, gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
TypeObject *object = i.get<TypeObject>();
MarkSetsUnknownState state(cx, target);
unsigned count = object->getPropertyCount();
for (unsigned i = 0; i < count; i++) {
Property *prop = object->getProperty(i);
if (prop && prop->types.hasType(Type::ObjectType(target))) {
if (!pending.append(&prop->types))
cx->compartment->types.setPendingNukeTypes(cx);
}
}
}
IterateCells(cx, cx->compartment, gc::FINALIZE_TYPE_OBJECT,
(void *) &state, MarkObjectSetsUnknownCallback);
for (unsigned i = 0; i < pending.length(); i++)
pending[i]->addType(cx, Type::AnyObjectType());
for (unsigned i = 0; i < state.pending.length(); i++)
state.pending[i]->addType(cx, Type::AnyObjectType());
for (JSCList *cursor = cx->compartment->scripts.next;
cursor != &cx->compartment->scripts;
cursor = cursor->next) {
JSScript *script = reinterpret_cast<JSScript *>(cursor);
for (gc::CellIter i(cx, cx->compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>();
if (script->types) {
unsigned count = TypeScript::NumTypeSets(script);
TypeSet *typeArray = script->types->typeArray();
@ -2306,11 +2280,23 @@ ScriptAnalysis::addSingletonTypeBarrier(JSContext *cx, const jsbytecode *pc, Typ
code.typeBarriers = barrier;
}
static void
PrintScriptTypeCallback(JSContext *cx, void *data, void *thing,
JSGCTraceKind traceKind, size_t thingSize)
{
JS_ASSERT(!data);
JS_ASSERT(traceKind == JSTRACE_SCRIPT);
JSScript *script = static_cast<JSScript *>(thing);
if (script->hasAnalysis() && script->analysis()->ranInference())
script->analysis()->printTypes(cx);
}
#ifdef DEBUG
static void
PrintObjectCallback(JSContext *cx, void *data, void *thing,
size_t traceKind, size_t thingSize)
JSGCTraceKind traceKind, size_t thingSize)
{
JS_ASSERT(traceKind == JSTRACE_OBJECT);
TypeObject *object = (TypeObject *) thing;
object->print(cx);
}
@ -2321,17 +2307,12 @@ TypeCompartment::print(JSContext *cx, bool force)
{
JSCompartment *compartment = this->compartment();
if (JS_CLIST_IS_EMPTY(&compartment->scripts))
return;
if (!force && !InferSpewActive(ISpewResult))
return;
for (JSScript *script = (JSScript *)compartment->scripts.next;
&script->links != &compartment->scripts;
script = (JSScript *)script->links.next) {
if (script->hasAnalysis() && script->analysis()->ranInference())
script->analysis()->printTypes(cx);
{
AutoUnlockGC unlock(cx->runtime);
IterateCells(cx, compartment, gc::FINALIZE_SCRIPT, cx, PrintScriptTypeCallback);
}
#ifdef DEBUG
@ -4473,7 +4454,7 @@ void
ScriptAnalysis::printTypes(JSContext *cx)
{
AutoEnterAnalysis enter(cx);
TypeCompartment *compartment = &script->compartment->types;
TypeCompartment *compartment = &script->compartment()->types;
/*
* Check if there are warnings for used values with unknown types, and build
@ -5011,7 +4992,6 @@ JSScript::typeSetFunction(JSContext *cx, JSFunction *fun, bool singleton)
JSProto_Function, fun->getProto());
if (!type)
return false;
AutoTypeRooter root(cx, type);
fun->setType(type);
type->functionScript = this;
@ -5531,7 +5511,7 @@ TypeCompartment::~TypeCompartment()
/* static */ void
TypeScript::Sweep(JSContext *cx, JSScript *script)
{
JSCompartment *compartment = script->compartment;
JSCompartment *compartment = script->compartment();
JS_ASSERT(compartment->types.inferenceEnabled);
unsigned num = NumTypeSets(script);
@ -5617,7 +5597,7 @@ GetScriptMemoryStats(JSScript *script, TypeInferenceMemoryStats *stats)
if (!script->types)
return;
if (!script->compartment->types.inferenceEnabled) {
if (!script->compartment()->types.inferenceEnabled) {
stats->scripts += sizeof(TypeScript);
return;
}
@ -5653,12 +5633,8 @@ JS_GetTypeInferenceMemoryStats(JSContext *cx, JSCompartment *compartment,
/* Pending arrays are cleared on GC along with the analysis pool. */
stats->temporary += sizeof(TypeCompartment::PendingWork) * compartment->types.pendingCapacity;
for (JSCList *cursor = compartment->scripts.next;
cursor != &compartment->scripts;
cursor = cursor->next) {
JSScript *script = reinterpret_cast<JSScript *>(cursor);
GetScriptMemoryStats(script, stats);
}
for (gc::CellIter i(cx, compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next())
GetScriptMemoryStats(i.get<JSScript>(), stats);
if (compartment->types.allocationSiteTable)
stats->tables += compartment->types.allocationSiteTable->allocatedSize();

View File

@ -1216,23 +1216,6 @@ TypeObject::setFlagsFromKey(JSContext *cx, JSProtoKey key)
setFlags(cx, flags);
}
class AutoTypeRooter : private AutoGCRooter {
public:
AutoTypeRooter(JSContext *cx, TypeObject *type
JS_GUARD_OBJECT_NOTIFIER_PARAM)
: AutoGCRooter(cx, TYPE), type(type)
{
JS_GUARD_OBJECT_NOTIFIER_INIT;
}
friend void AutoGCRooter::trace(JSTracer *trc);
friend void MarkRuntime(JSTracer *trc);
private:
TypeObject *type;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
};
} } /* namespace js::types */
inline bool

View File

@ -914,7 +914,6 @@ ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value
}
LeaveTrace(cx);
AutoScriptRooter root(cx, script);
ExecuteFrameGuard efg;
if (!cx->stack.pushExecuteFrame(cx, script, thisv, scopeChain, type, evalInFrame, &efg))
@ -1856,14 +1855,13 @@ Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
JS_ASSERT(regs.fp()->hasImacropc() \
? atoms == rt->atomState.commonAtomsStart() && \
GET_INDEX(regs.pc + PCOFF) < js_common_atom_count \
: (size_t)(atoms - script->atomMap.vector) < \
(size_t)(script->atomMap.length - \
GET_INDEX(regs.pc + PCOFF))); \
: (size_t)(atoms - script->atoms) < \
(size_t)(script->natoms - GET_INDEX(regs.pc + PCOFF))); \
atom = atoms[GET_INDEX(regs.pc + PCOFF)]; \
JS_END_MACRO
#define GET_FULL_INDEX(PCOFF) \
(atoms - script->atomMap.vector + GET_INDEX(regs.pc + (PCOFF)))
(atoms - script->atoms + GET_INDEX(regs.pc + (PCOFF)))
#define LOAD_OBJECT(PCOFF, obj) \
(obj = script->getObject(GET_FULL_INDEX(PCOFF)))
@ -2035,7 +2033,7 @@ Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
* access. For less frequent object and regexp loads we have to recover
* the segment from atoms pointer first.
*/
JSAtom **atoms = script->atomMap.vector;
JSAtom **atoms = script->atoms;
#if JS_HAS_GENERATORS
if (JS_UNLIKELY(regs.fp()->isGeneratorFrame())) {
@ -2443,7 +2441,7 @@ BEGIN_CASE(JSOP_STOP)
regs.pc += GetDecomposeLength(imacpc, js_CodeSpec[*imacpc].length);
regs.fp()->clearImacropc();
LEAVE_ON_SAFE_POINT();
atoms = script->atomMap.vector;
atoms = script->atoms;
op = JSOp(*regs.pc);
DO_OP();
}
@ -4359,7 +4357,7 @@ END_CASE(JSOP_INT32)
BEGIN_CASE(JSOP_INDEXBASE)
/*
* Here atoms can exceed script->atomMap.length as we use atoms as a
* Here atoms can exceed script->natoms as we use atoms as a
* segment register for object literals as well.
*/
atoms += GET_INDEXBASE(regs.pc);
@ -4373,7 +4371,7 @@ END_CASE(JSOP_INDEXBASE3)
BEGIN_CASE(JSOP_RESETBASE0)
BEGIN_CASE(JSOP_RESETBASE)
atoms = script->atomMap.vector;
atoms = script->atoms;
END_CASE(JSOP_RESETBASE)
BEGIN_CASE(JSOP_DOUBLE)
@ -4535,7 +4533,7 @@ BEGIN_CASE(JSOP_LOOKUPSWITCH)
* index in it would exceed 64K limit.
*/
JS_ASSERT(!regs.fp()->hasImacropc());
JS_ASSERT(atoms == script->atomMap.vector);
JS_ASSERT(atoms == script->atoms);
jsbytecode *pc2 = regs.pc;
Value lval = regs.sp[-1];
@ -5499,7 +5497,7 @@ END_CASE(JSOP_SHARPINIT)
{
BEGIN_CASE(JSOP_GOSUB)
PUSH_BOOLEAN(false);
jsint i = (regs.pc - script->main) + JSOP_GOSUB_LENGTH;
jsint i = (regs.pc - script->code) + JSOP_GOSUB_LENGTH;
PUSH_INT32(i);
len = GET_JUMP_OFFSET(regs.pc);
END_VARLEN_CASE
@ -5508,7 +5506,7 @@ END_VARLEN_CASE
{
BEGIN_CASE(JSOP_GOSUBX)
PUSH_BOOLEAN(false);
jsint i = (regs.pc - script->main) + JSOP_GOSUBX_LENGTH;
jsint i = (regs.pc - script->code) + JSOP_GOSUBX_LENGTH;
len = GET_JUMPX_OFFSET(regs.pc);
PUSH_INT32(i);
END_VARLEN_CASE
@ -5533,7 +5531,7 @@ BEGIN_CASE(JSOP_RETSUB)
}
JS_ASSERT(rval.isInt32());
len = rval.toInt32();
regs.pc = script->main;
regs.pc = script->code;
END_VARLEN_CASE
}
@ -6162,7 +6160,7 @@ END_CASE(JSOP_ARRAYPUSH)
uint32 offset;
/* Restore atoms local in case we will resume. */
atoms = script->atomMap.vector;
atoms = script->atoms;
/* Call debugger throw hook if set. */
if (cx->debugHooks->throwHook || !cx->compartment->getDebuggees().empty()) {
@ -6197,7 +6195,7 @@ END_CASE(JSOP_ARRAYPUSH)
if (!JSScript::isValidOffset(script->trynotesOffset))
goto no_catch;
offset = (uint32)(regs.pc - script->main);
offset = (uint32)(regs.pc - script->main());
tn = script->trynotes()->vector;
tnlimit = tn + script->trynotes()->length;
do {
@ -6231,7 +6229,7 @@ END_CASE(JSOP_ARRAYPUSH)
* to the beginning of catch or finally or to [enditer] closing
* the for-in loop.
*/
regs.pc = (script)->main + tn->start + tn->length;
regs.pc = (script)->main() + tn->start + tn->length;
JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE);
JS_ASSERT(regs.sp == regs.fp()->base() + tn->stackDepth);

View File

@ -984,7 +984,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 *
@ -1032,7 +1032,7 @@ EvalCacheLookup(JSContext *cx, JSLinearString *str, StackFrame *caller, uintN st
* Get the source string passed for safekeeping in the
* atom map by the prior eval to Compiler::compileScript.
*/
JSAtom *src = script->atomMap.vector[0];
JSAtom *src = script->atoms[0];
if (src == str || EqualStrings(src, str)) {
/*
@ -1056,8 +1056,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;
}
}
@ -1066,7 +1066,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;
}
@ -1080,7 +1080,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
{
@ -1102,11 +1102,8 @@ class EvalScriptGuard
js_CallDestroyScriptHook(cx_, script_);
script_->isActiveEval = false;
script_->isCachedEval = true;
script_->u.nextToGC = *bucket_;
script_->u.evalHashLink = *bucket_;
*bucket_ = script_;
#ifdef CHECK_SCRIPT_OWNER
script_->owner = NULL;
#endif
}
}
@ -5578,12 +5575,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) ||
@ -5646,14 +5639,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

@ -1132,6 +1132,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

@ -655,6 +655,37 @@ JSObject::getFlatClosureUpvars() const
return (js::Value *) getFixedSlot(JSSLOT_FLAT_CLOSURE_UPVARS).toPrivate();
}
inline void
JSObject::finalizeUpvarsIfFlatClosure()
{
/*
* Cloned function objects may be flat closures with upvars to free.
*
* We do not record in the closure objects any flags. Rather we use flags
* stored in the compiled JSFunction that we get via getFunctionPrivate()
* to distinguish between closure types. Then during finalization we must
* ensure that the compiled JSFunction always finalized after the closures
* so we can safely access it here. Currently the GC ensures that through
* finalizing JSFunction instances after finalizing any other objects even
* during the background finalization.
*
* But we must not access JSScript here that is stored in JSFunction. The
* script can be finalized before the function or closure instances. So we
* just check if JSSLOT_FLAT_CLOSURE_UPVARS holds a private value encoded
* as a double. We must also ignore newborn closures that do not have the
* private pointer set.
*
* FIXME bug 648320 - allocate upvars on the GC heap to avoid doing it
* here explicitly.
*/
JSFunction *fun = getFunctionPrivate();
if (fun && fun != this && fun->isFlatClosure()) {
const js::Value &v = getSlot(JSSLOT_FLAT_CLOSURE_UPVARS);
if (v.isDouble())
js::Foreground::free_(v.toPrivate());
}
}
inline js::Value
JSObject::getFlatClosureUpvar(uint32 i) const
{

View File

@ -258,7 +258,6 @@ public:
// No change needed
newPC = origPC;
} else {
script->main += newCode - script->code;
*pc = newPC = origPC + (newCode - script->code);
script->code = newCode;
#ifdef DEBUG
@ -274,7 +273,6 @@ public:
jsbytecode *oldCode = script->code - delta;
cx->free_(script->code);
script->code = oldCode;
script->main -= delta;
#ifdef DEBUG
cx->stackIterAssertionEnabled = assertionBefore;
#endif
@ -313,7 +311,7 @@ js_DisassembleAtPC(JSContext *cx, JSScript *script, JSBool lines, jsbytecode *pc
next = script->code;
end = next + script->length;
while (next < end) {
if (next == script->main)
if (next == script->main())
SprintCString(sp, "main:\n");
if (pc != NULL) {
if (pc == next)
@ -2023,8 +2021,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
JS_BEGIN_MACRO \
jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0); \
\
LOCAL_ASSERT(atomIndex_ < jp->script->atomMap.length); \
(atom) = jp->script->atomMap.vector[atomIndex_]; \
LOCAL_ASSERT(atomIndex_ < jp->script->natoms); \
(atom) = jp->script->atoms[atomIndex_]; \
JS_END_MACRO
/*
@ -4110,8 +4108,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
* Decompile only the main bytecode, to avoid tripping over
* new prolog ops that have stack effects.
*/
ok = Decompile(&ss2, inner->main,
inner->length - (inner->main - inner->code),
ok = Decompile(&ss2, inner->main(), inner->length - inner->mainOffset,
JSOP_NOP)
!= NULL;
jp->script = outer;
@ -4997,7 +4994,7 @@ js_DecompileFunction(JSPrinter *jp)
#endif
/* Print the parameters. */
pc = script->main;
pc = script->main();
AutoScriptUntrapper untrapper(jp->sprinter.context, script, &pc);
endpc = pc + script->length;
ok = JS_TRUE;
@ -5106,7 +5103,7 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v_in,
pc = fp->hasImacropc() ? fp->imacropc() : cx->regs().pc;
JS_ASSERT(script->code <= pc && pc < script->code + script->length);
if (pc < script->main)
if (pc < script->main())
goto do_fallback;
if (spindex != JSDVG_IGNORE_STACK) {

View File

@ -971,7 +971,7 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerF
if (tcflags & TCF_COMPILE_N_GO) {
if (source) {
/*
* Save eval program source in script->atomMap.vector[0] for the
* Save eval program source in script->atoms[0] for the
* eval cache (see EvalCacheLookup in jsobj.cpp).
*/
JSAtom *atom = js_AtomizeString(cx, source);
@ -1114,11 +1114,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);
@ -1128,11 +1125,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;
}

View File

@ -92,7 +92,6 @@ typedef struct JSTreeContext JSTreeContext;
typedef struct JSTryNote JSTryNote;
/* Friend "Advanced API" typedefs. */
typedef struct JSAtomMap JSAtomMap;
typedef struct JSAtomState JSAtomState;
typedef struct JSCodeSpec JSCodeSpec;
typedef struct JSPrinter JSPrinter;

View File

@ -291,30 +291,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) {
if (script->cookie1[0] != JS_SCRIPT_COOKIE || script->cookie2[0] != JS_SCRIPT_COOKIE) {
crash::StackBuffer<sizeof(JSScript), 0x87> buf1(script);
crash::StackBuffer<sizeof(JSScript), 0x88> buf2(prev);
JS_OPT_ASSERT(false);
}
#endif
}
static void
void
CheckScriptOwner(JSScript *script, JSObject *owner)
{
#ifdef JS_CRASH_DIAGNOSTICS
JS_OPT_ASSERT(script->ownerObject == owner);
if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT)
JS_OPT_ASSERT(script->compartment == owner->compartment());
#endif
JS_OPT_ASSERT(script->compartment() == owner->compartment());
}
#endif /* JS_CRASH_DIAGNOSTICS */
} /* namespace js */
#if JS_HAS_XDR
enum ScriptBits {
@ -382,7 +382,6 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp)
JS_ASSERT(nupvars != Bindings::BINDING_COUNT_LIMIT);
Bindings bindings(cx);
AutoBindingsRooter rooter(cx, bindings);
uint32 nameCount = nargs + nvars + nupvars;
if (nameCount > 0) {
struct AutoMark {
@ -472,13 +471,13 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp)
return JS_FALSE;
if (xdr->mode == JSXDR_ENCODE) {
prologLength = script->main - script->code;
prologLength = script->mainOffset;
JS_ASSERT(script->getVersion() != JSVERSION_UNKNOWN);
version = (uint32)script->getVersion() | (script->nfixed << 16);
lineno = (uint32)script->lineno;
nslots = (uint32)script->nslots;
nslots = (uint32)((script->staticLevel << 16) | script->nslots);
natoms = (uint32)script->atomMap.length;
natoms = script->natoms;
notes = script->notes();
nsrcnotes = script->numNotes();
@ -544,8 +543,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,14 +557,13 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp)
return JS_FALSE;
script->bindings.transfer(cx, &bindings);
script->main += prologLength;
JS_ASSERT(!script->mainOffset);
script->mainOffset = prologLength;
script->nfixed = uint16(version >> 16);
/* If we know nsrcnotes, we allocated space for notes in script. */
notes = script->notes();
*scriptp = script;
tvr.setScript(script);
if (scriptBits & (1 << NoScriptRval))
script->noScriptRval = true;
@ -657,7 +653,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp)
}
for (i = 0; i != natoms; ++i) {
if (!js_XDRAtom(xdr, &script->atomMap.vector[i]))
if (!js_XDRAtom(xdr, &script->atoms[i]))
goto error;
}
@ -744,10 +740,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;
}
@ -774,20 +768,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 = {
@ -801,7 +789,7 @@ Class js_ScriptClass = {
EnumerateStub,
ResolveStub,
ConvertStub,
script_finalize,
NULL, /* finalize */
NULL, /* reserved0 */
NULL, /* checkAccess */
NULL, /* call */
@ -877,8 +865,8 @@ js_SweepScriptFilenames(JSCompartment *comp)
* use script->regexps() to access it.
* JSTryNoteArray script try notes' descriptor if JSScript.tryNotesOffset
* != 0, use script->trynotes() to access it.
* JSAtom *a[] array of JSScript.atomMap.length atoms pointed by
* JSScript.atomMap.vector if any.
* JSAtom *a[] array of JSScript.natoms atoms pointed by
* JSScript.atoms if any.
* JSObject *o[] array of script->objects()->length objects if any
* pointed by script->objects()->vector.
* JSObject *r[] array of script->regexps()->length regexps if any
@ -925,16 +913,7 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom
uint32 ntrynotes, uint32 nconsts, uint32 nglobals,
uint16 nClosedArgs, uint16 nClosedVars, uint32 nTypeSets, JSVersion version)
{
size_t size, vectorSize;
JSScript *script;
uint8 *cursor;
unsigned constPadding = 0;
uint32 totalClosed = nClosedArgs + nClosedVars;
size = sizeof(JSScript) +
sizeof(JSAtom *) * natoms;
size_t size = sizeof(JSAtom *) * natoms;
if (nobjects != 0)
size += sizeof(JSObjectArray) + nobjects * sizeof(JSObject *);
if (nupvars != 0)
@ -945,31 +924,66 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom
size += sizeof(JSTryNoteArray) + ntrynotes * sizeof(JSTryNote);
if (nglobals != 0)
size += sizeof(GlobalSlotArray) + nglobals * sizeof(GlobalSlotArray::Entry);
uint32 totalClosed = nClosedArgs + nClosedVars;
if (totalClosed != 0)
size += totalClosed * sizeof(uint32);
if (nconsts != 0) {
size += sizeof(JSConstArray);
/*
* To esnure jsval alignment for the const array we place it immediately
* after JSSomethingArray structs as their sizes all divide sizeof(jsval).
* This works as long as the data itself is allocated with proper
* alignment which we ensure below.
*/
JS_STATIC_ASSERT(sizeof(JSObjectArray) % sizeof(jsval) == 0);
JS_STATIC_ASSERT(sizeof(JSUpvarArray) % sizeof(jsval) == 0);
JS_STATIC_ASSERT(sizeof(JSTryNoteArray) % sizeof(jsval) == 0);
JS_STATIC_ASSERT(sizeof(GlobalSlotArray) % sizeof(jsval) == 0);
JS_STATIC_ASSERT(sizeof(JSConstArray) % sizeof(jsval) == 0);
if (nconsts != 0)
size += sizeof(JSConstArray) + nconsts * sizeof(Value);
size += length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote);
uint8 *data = NULL;
#if JS_SCRIPT_INLINE_DATA_LIMIT
if (size <= JS_SCRIPT_INLINE_DATA_LIMIT) {
/*
* Calculate padding assuming that consts go after the other arrays,
* but before the bytecode and source notes.
* Check that if inlineData is big enough to store const values, we
* can do that without any special alignment requirements given that
* the script as a GC thing is always aligned on Cell::CellSize.
*/
constPadding = (8 - (size % 8)) % 8;
size += constPadding + nconsts * sizeof(Value);
JS_STATIC_ASSERT(Cell::CellSize % sizeof(Value) == 0);
JS_STATIC_ASSERT(JS_SCRIPT_INLINE_DATA_LIMIT < sizeof(Value) ||
offsetof(JSScript, inlineData) % sizeof(Value) == 0);
} else
#endif
{
/*
* We assume that calloc aligns on sizeof(Value) if the size we ask to
* allocate divides sizeof(Value).
*/
JS_STATIC_ASSERT(sizeof(Value) == sizeof(jsdouble));
data = static_cast<uint8 *>(cx->calloc_(JS_ROUNDUP(size, sizeof(Value))));
if (!data)
return NULL;
}
size += length * sizeof(jsbytecode) +
nsrcnotes * sizeof(jssrcnote);
script = (JSScript *) cx->malloc_(size);
if (!script)
JSScript *script = js_NewGCScript(cx);
if (!script) {
Foreground::free_(data);
return NULL;
}
PodZero(script);
#ifdef JS_CRASH_DIAGNOSTICS
script->cookie1 = script->cookie2 = JS_SCRIPT_COOKIE;
script->cookie1[0] = script->cookie2[0] = JS_SCRIPT_COOKIE;
script->ownerObject = JS_NEW_SCRIPT;
#endif
#if JS_SCRIPT_INLINE_DATA_LIMIT
if (!data)
data = script->inlineData;
#endif
script->data = data;
script->length = length;
script->version = version;
new (&script->bindings) Bindings(cx);
@ -977,42 +991,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;
@ -1024,39 +1036,36 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom
sizeof(JSTryNoteArray) +
sizeof(GlobalSlotArray) < 0xFF);
if (natoms != 0) {
script->atomMap.length = natoms;
script->atomMap.vector = (JSAtom **)cursor;
vectorSize = natoms * sizeof(script->atomMap.vector[0]);
/*
* Clear object map's vector so the GC tracing can run when not yet
* all atoms are copied to the array.
*/
memset(cursor, 0, vectorSize);
cursor += vectorSize;
if (nconsts != 0) {
JS_ASSERT(reinterpret_cast<jsuword>(cursor) % sizeof(jsval) == 0);
script->consts()->length = nconsts;
script->consts()->vector = reinterpret_cast<Value *>(cursor);
cursor += nconsts * sizeof(script->consts()->vector[0]);
}
if (natoms != 0) {
script->natoms = natoms;
script->atoms = reinterpret_cast<JSAtom **>(cursor);
cursor += natoms * sizeof(script->atoms[0]);
}
if (nobjects != 0) {
script->objects()->length = nobjects;
script->objects()->vector = (JSObject **)cursor;
vectorSize = nobjects * sizeof(script->objects()->vector[0]);
memset(cursor, 0, vectorSize);
cursor += vectorSize;
script->objects()->vector = reinterpret_cast<JSObject **>(cursor);
cursor += nobjects * sizeof(script->objects()->vector[0]);
}
if (nregexps != 0) {
script->regexps()->length = nregexps;
script->regexps()->vector = (JSObject **)cursor;
vectorSize = nregexps * sizeof(script->regexps()->vector[0]);
memset(cursor, 0, vectorSize);
cursor += vectorSize;
script->regexps()->vector = reinterpret_cast<JSObject **>(cursor);
cursor += nregexps * sizeof(script->regexps()->vector[0]);
}
if (ntrynotes != 0) {
script->trynotes()->length = ntrynotes;
script->trynotes()->vector = (JSTryNote *)cursor;
vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]);
script->trynotes()->vector = reinterpret_cast<JSTryNote *>(cursor);
size_t vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]);
#ifdef DEBUG
memset(cursor, 0, vectorSize);
#endif
@ -1065,15 +1074,14 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom
if (nglobals != 0) {
script->globals()->length = nglobals;
script->globals()->vector = (GlobalSlotArray::Entry *)cursor;
vectorSize = nglobals * sizeof(script->globals()->vector[0]);
cursor += vectorSize;
script->globals()->vector = reinterpret_cast<GlobalSlotArray::Entry *>(cursor);
cursor += nglobals * sizeof(script->globals()->vector[0]);
}
if (totalClosed != 0) {
script->nClosedArgs = nClosedArgs;
script->nClosedVars = nClosedVars;
script->closedSlots = (uint32 *)cursor;
script->closedSlots = reinterpret_cast<uint32 *>(cursor);
cursor += totalClosed * sizeof(uint32);
}
@ -1087,39 +1095,16 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom
if (nupvars != 0) {
script->upvars()->length = nupvars;
script->upvars()->vector = reinterpret_cast<UpvarCookie *>(cursor);
vectorSize = nupvars * sizeof(script->upvars()->vector[0]);
memset(cursor, 0, vectorSize);
cursor += vectorSize;
cursor += nupvars * sizeof(script->upvars()->vector[0]);
}
/* Must go after other arrays; see constPadding definition. */
if (nconsts != 0) {
cursor += constPadding;
script->consts()->length = nconsts;
script->consts()->vector = (Value *)cursor;
JS_ASSERT((size_t)cursor % sizeof(double) == 0);
vectorSize = nconsts * sizeof(script->consts()->vector[0]);
memset(cursor, 0, vectorSize);
cursor += vectorSize;
}
script->code = script->main = (jsbytecode *)cursor;
JS_ASSERT(cursor +
length * sizeof(jsbytecode) +
nsrcnotes * sizeof(jssrcnote) ==
(uint8 *)script + size);
script->compartment = cx->compartment;
#ifdef CHECK_SCRIPT_OWNER
script->owner = cx->thread();
#endif
script->code = (jsbytecode *)cursor;
JS_ASSERT(cursor + length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote) == data + size);
#ifdef DEBUG
script->id_ = ++cx->compartment->types.scriptCount;
#endif
JS_APPEND_LINK(&script->links, &cx->compartment->scripts);
JS_ASSERT(script->getVersion() == version);
return script;
}
@ -1159,29 +1144,28 @@ 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;
JS_ASSERT(script->mainOffset == 0);
script->mainOffset = prologLength;
memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode));
memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode));
memcpy(script->main(), CG_BASE(cg), mainLength * sizeof(jsbytecode));
nfixed = cg->inFunction()
? cg->bindings.countVars()
: cg->sharpSlots();
JS_ASSERT(nfixed < SLOTNO_LIMIT);
script->nfixed = (uint16) nfixed;
js_InitAtomMap(cx, &script->atomMap, cg->atomIndices.getMap());
js_InitAtomMap(cx, cg->atomIndices.getMap(), script->atoms);
filename = cg->parser->tokenStream.getFilename();
if (filename) {
script->filename = SaveScriptFilename(cx, filename);
if (!script->filename)
goto bad;
return NULL;
}
script->lineno = cg->firstLine;
if (script->nfixed + cg->maxStackDepth >= JS_BIT(16)) {
ReportCompileErrorNumber(cx, CG_TS(cg), NULL, JSREPORT_ERROR, JSMSG_NEED_DIET, "script");
goto bad;
return NULL;
}
script->nslots = script->nfixed + cg->maxStackDepth;
script->staticLevel = uint16(cg->staticLevel);
@ -1192,7 +1176,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)
@ -1265,9 +1249,6 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
JS_ASSERT(script->upvars()->length == script->bindings.countUpvars());
else
JS_ASSERT(script->bindings.countUpvars() == 0);
#endif
#ifdef CHECK_SCRIPT_OWNER
script->owner = NULL;
#endif
if (cg->flags & TCF_FUN_HEAVYWEIGHT)
fun->flags |= JSFUN_HEAVYWEIGHT;
@ -1278,7 +1259,7 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
cg->parent->asCodeGenerator()->checkSingletonContext();
if (!script->typeSetFunction(cx, fun, singleton))
goto bad;
return NULL;
fun->u.i.script = script;
script->setOwnerObject(fun);
@ -1288,7 +1269,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. */
@ -1302,20 +1283,19 @@ 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;
#if JS_SCRIPT_INLINE_DATA_LIMIT
if (data == inlineData)
return 0;
#endif
uint8 *dataEnd = code + length * sizeof(jsbytecode) + numNotes() * sizeof(jssrcnote);
JS_ASSERT(dataEnd >= data);
return dataEnd - data;
}
void
@ -1344,200 +1324,67 @@ 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
if (script->types)
script->types->destroy();
if (types)
types->destroy();
#ifdef JS_METHODJIT
mjit::ReleaseScriptCode(cx, script);
mjit::ReleaseScriptCode(cx, this);
#endif
JS_REMOVE_LINK(&script->links);
pcCounters.destroy(cx);
script->pcCounters.destroy(cx);
if (sourceMap)
cx->free_(sourceMap);
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);
#ifdef JS_METHODJIT
/* Keep the hook from trying to recompile while the GC is running. */
mjit::ReleaseScriptCode(cx, script);
#if JS_SCRIPT_INLINE_DATA_LIMIT
if (data != inlineData)
#endif
js_CallDestroyScriptHook(cx, script);
DestroyScript(cx, script, owner, 100);
}
void
js_DestroyCachedScript(JSContext *cx, JSScript *script)
{
JS_ASSERT(cx->runtime->gcRunning);
DestroyScript(cx, script, JS_CACHED_SCRIPT, 101);
}
void
js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner)
{
JS_ASSERT_IF(trc->context->runtime->gcCurrentCompartment, IS_GC_MARKING_TRACER(trc));
CheckScript(script, NULL);
if (owner)
CheckScriptOwner(script, owner);
JSRuntime *rt = trc->context->runtime;
/*
* During per-compartment GCs we may attempt to trace scripts that are out
* of the target compartment. Ignore such attempts, marking the children is
* wasted work and if we mark external type objects they will not get
* unmarked at the end of the GC cycle.
*/
if (rt->gcCurrentCompartment && rt->gcCurrentCompartment != script->compartment)
return;
#ifdef JS_CRASH_DIAGNOSTICS
JS_OPT_ASSERT_IF(rt->gcCheckCompartment, script->compartment == rt->gcCheckCompartment);
#endif
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");
{
JS_POISON(data, 0xdb, dataSize());
cx->free_(data);
}
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");
}
/*
* Mark the object keeping this script alive. The script can be traced
* separately if, e.g. we are GC'ing while type inference code is active,
* and we need to make sure both the script and the object survive the GC.
*/
if (!script->isCachedEval && script->u.object)
MarkObject(trc, *script->u.object, "object");
if (script->hasFunction)
MarkObject(trc, *script->function(), "script_fun");
if (IS_GC_MARKING_TRACER(trc) && script->filename)
js_MarkScriptFilename(script->filename);
script->bindings.trace(trc);
#ifdef JS_METHODJIT
if (script->jitNormal)
script->jitNormal->trace(trc);
if (script->jitCtor)
script->jitCtor->trace(trc);
#endif
}
JSObject *
js_NewScriptObject(JSContext *cx, JSScript *script)
{
AutoScriptRooter root(cx, script);
JS_ASSERT(!script->u.object);
JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_ScriptClass, NULL, NULL);
@ -1553,10 +1400,6 @@ js_NewScriptObject(JSContext *cx, JSScript *script)
*/
obj->clearType();
#ifdef CHECK_SCRIPT_OWNER
script->owner = NULL;
#endif
return obj;
}
@ -1706,7 +1549,7 @@ js_LineNumberToPC(JSScript *script, uintN target)
* Exact-match only if offset is not in the prolog; otherwise use
* nearest greater-or-equal line number match.
*/
if (lineno == target && script->code + offset >= script->main)
if (lineno == target && offset >= ptrdiff_t(script->mainOffset))
goto out;
if (lineno >= target) {
diff = lineno - target;
@ -1838,8 +1681,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));
@ -1879,7 +1721,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);
@ -1942,7 +1784,7 @@ JSScript::changeStepModeCount(JSContext *cx, int delta)
uint32 count = stepMode & stepCountMask;
JS_ASSERT(((count + delta) & stepCountMask) == count + delta);
return tryNewStepMode(cx,
return tryNewStepMode(cx,
(stepMode & stepFlagMask) |
((count + delta) & stepCountMask));
}

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 *****
@ -362,10 +362,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;
@ -432,7 +428,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,
@ -452,42 +448,27 @@ struct JSScript {
static JSScript *NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg);
/* FIXME: bug 586181 */
JSCList links; /* Links for compartment script list */
jsbytecode *code; /* bytecodes and their immediate operands */
uint32 length; /* length of code vector */
#ifdef JS_CRASH_DIAGNOSTICS
uint32 cookie1;
/*
* Make sure that the cookie size does not affect the GC alignment
* requirements.
*/
uint32 cookie1[Cell::CellSize / sizeof(uint32)];
#endif
jsbytecode *code; /* bytecodes and their immediate operands */
uint8 *data; /* pointer to variable-length data array */
uint32 length; /* length of code vector */
private:
size_t useCount_; /* Number of times the script has been called
* or has had backedges taken. Reset if the
* script's JIT code is forcibly discarded. */
uint16 version; /* JS version under which script was compiled */
public:
uint16 nfixed; /* number of slots besides stack operands in
slot array */
uint16 nTypeSets; /* number of type sets used in this script for
dynamic type monitoring */
/*
* When non-zero, compile script in single-step mode. The top bit is set and
* cleared by setStepMode, as used by JSD. The lower bits are a count,
* adjusted by changeStepModeCount, used by the Debugger object. Only
* when the bit is clear and the count is zero may we compile the script
* without single-step support.
*/
uint32 stepMode;
/*
* Offsets to various array structures from the end of this script, or
* JSScript::INVALID_OFFSET if the array has length 0.
*/
public:
uint8 objectsOffset; /* offset to the array of nested function,
block, scope, xml and one-time regexps
objects */
@ -499,6 +480,22 @@ struct JSScript {
uint8 globalsOffset; /* offset to the array of global slots */
uint8 constOffset; /* offset to the array of constants */
uint16 nTypeSets; /* number of type sets used in this script for
dynamic type monitoring */
/*
* When non-zero, compile script in single-step mode. The top bit is set and
* cleared by setStepMode, as used by JSD. The lower bits are a count,
* adjusted by changeStepModeCount, used by the Debugger object. Only
* when the bit is clear and the count is zero may we compile the script
* without single-step support.
*/
uint32 stepMode;
uint32 lineno; /* base line number of script */
uint32 mainOffset; /* offset of main entry point from code, after
predef'ing prolog */
bool noScriptRval:1; /* no need for result value of last
expression statement */
bool savedCallerFun:1; /* can call getCallerFunction() */
@ -524,27 +521,35 @@ struct JSScript {
bool debugMode:1; /* script was compiled in debug mode */
bool failedBoundsCheck:1; /* script has had hoisted bounds checks fail */
#endif
bool callDestroyHook:1;/* need to call destroy hook */
jsbytecode *main; /* main entry point, after predef'ing prolog */
JSAtomMap atomMap; /* maps immediate index to literal struct */
JSCompartment *compartment; /* compartment the script was compiled for */
const char *filename; /* source filename or null */
uint32 lineno; /* base line number of script */
uint32 natoms; /* length of atoms array */
uint16 nslots; /* vars plus maximum stack depth */
uint16 staticLevel;/* static level for display maintenance */
uint16 nClosedArgs; /* number of args which are closed over. */
uint16 nClosedVars; /* number of vars which are closed over. */
/*
* To ensure sizeof(JSScript) % gc::Cell::CellSize == 0 on we must pad
* the script with 4 bytes. We use them to store tiny scripts like empty
* scripts.
*/
#define JS_SCRIPT_INLINE_DATA_LIMIT 4
uint8 inlineData[JS_SCRIPT_INLINE_DATA_LIMIT];
const char *filename; /* source filename or null */
JSAtom **atoms; /* maps immediate index to literal struct */
private:
size_t useCount; /* Number of times the script has been called
* or has had backedges taken. Reset if the
* script's JIT code is forcibly discarded. */
public:
js::Bindings bindings; /* names of top-level variables in this script
(and arguments if this is a function script) */
JSPrincipals *principals;/* principals for this script */
jschar *sourceMap; /* source map file or null */
#ifdef JS_CRASH_DIAGNOSTICS
JSObject *ownerObject;
#endif
void setOwnerObject(JSObject *owner);
union {
/*
* A script object of class js_ScriptClass, to ensure the script is GC'd.
@ -557,24 +562,16 @@ 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. */
/* array of execution counters for every JSOp in the script, by runmode */
JSPCCounters pcCounters;
#ifdef JS_CRASH_DIAGNOSTICS
uint32 cookie2;
#endif
public:
union {
/* Function this script is the body for, if there is one. */
JSFunction *fun;
@ -588,6 +585,15 @@ struct JSScript {
return where.fun;
}
#ifdef JS_CRASH_DIAGNOSTICS
JSObject *ownerObject;
/* All diagnostic fields must be multiples of Cell::CellSize. */
uint32 cookie2[sizeof(JSObject *) == 4 ? 1 : 2];
#endif
void setOwnerObject(JSObject *owner);
/*
* Associates this script with a specific function, constructing a new type
* object for the function.
@ -604,7 +610,8 @@ struct JSScript {
* Unique identifier within the compartment for this script, used for
* printing analysis information.
*/
unsigned id_;
uint32 id_;
uint32 idpad;
unsigned id() { return id_; }
#else
unsigned id() { return 0; }
@ -639,7 +646,9 @@ struct JSScript {
js::mjit::JITScript *jitNormal; /* Extra JIT info for normal scripts */
js::mjit::JITScript *jitCtor; /* Extra JIT info for constructors */
#endif
#ifdef JS_METHODJIT
bool hasJITCode() {
return jitNormal || jitCtor;
}
@ -653,10 +662,10 @@ struct JSScript {
return constructing ? jitCtor : jitNormal;
}
size_t useCount() const { return useCount_; }
size_t incUseCount() { return ++useCount_; }
size_t *addressOfUseCount() { return &useCount_; }
void resetUseCount() { useCount_ = 0; }
size_t getUseCount() const { return useCount; }
size_t incUseCount() { return ++useCount; }
size_t *addressOfUseCount() { return &useCount; }
void resetUseCount() { useCount = 0; }
JITScriptStatus getJITStatus(bool constructing) {
void *addr = constructing ? jitArityCheckCtor : jitArityCheckNormal;
@ -671,7 +680,11 @@ 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 */
jsbytecode *main() {
return code + mainOffset;
}
JS_FRIEND_API(size_t) dataSize(); /* Size of all data sections */
uint32 numNotes(); /* Number of srcnote slots in the srcnotes section */
/* Script notes are allocated right after the code. */
@ -682,37 +695,37 @@ 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) {
JS_ASSERT(index < atomMap.length);
return atomMap.vector[index];
JS_ASSERT(index < natoms);
return atoms[index];
}
JSObject *getObject(size_t index) {
@ -802,11 +815,14 @@ struct JSScript {
#ifdef DEBUG
uint32 stepModeCount() { return stepMode & stepCountMask; }
#endif
void finalize(JSContext *cx);
};
JS_STATIC_ASSERT(sizeof(JSScript) % js::gc::Cell::CellSize == 0);
#define SHARP_NSLOTS 2 /* [#array, #depth] slots if the script
uses sharp variables */
static JS_INLINE uintN
StackDepth(JSScript *script)
{
@ -852,27 +868,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);
extern void
js_TraceScript(JSTracer *trc, 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 JSObject *
js_NewScriptObject(JSContext *cx, JSScript *script);

View File

@ -8097,7 +8097,7 @@ TraceRecorder::updateAtoms()
JS_REQUIRES_STACK void
TraceRecorder::updateAtoms(JSScript *script)
{
atoms = script->atomMap.vector;
atoms = script->atoms;
consts = JSScript::isValidOffset(script->constOffset) ? script->consts()->vector : 0;
strictModeCode_ins = w.name(w.immi(script->strictModeCode), "strict");
}
@ -14516,7 +14516,7 @@ TraceRecorder::record_JSOP_OBJECT()
{
StackFrame* const fp = cx->fp();
JSScript* script = fp->script();
unsigned index = atoms - script->atomMap.vector + GET_INDEX(cx->regs().pc);
unsigned index = atoms - script->atoms + GET_INDEX(cx->regs().pc);
JSObject* obj;
obj = script->getObject(index);
@ -15444,7 +15444,7 @@ jsatomid
TraceRecorder::getFullIndex(ptrdiff_t pcoff)
{
jsatomid index = GET_INDEX(cx->regs().pc + pcoff);
index += atoms - cx->fp()->script()->atomMap.vector;
index += atoms - cx->fp()->script()->atoms;
return index;
}
@ -15831,7 +15831,7 @@ TraceRecorder::record_JSOP_REGEXP()
{
StackFrame* const fp = cx->fp();
JSScript* script = fp->script();
unsigned index = atoms - script->atomMap.vector + GET_INDEX(cx->regs().pc);
unsigned index = atoms - script->atoms + GET_INDEX(cx->regs().pc);
LIns* proto_ins;
CHECK_STATUS_A(getClassPrototype(JSProto_RegExp, proto_ins));
@ -16979,7 +16979,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

@ -565,9 +565,9 @@ class Value
}
JS_ALWAYS_INLINE
int32 gcKind() const {
JSGCTraceKind gcKind() const {
JS_ASSERT(isMarkable());
return JSVAL_TRACE_KIND_IMPL(data);
return JSGCTraceKind(JSVAL_TRACE_KIND_IMPL(data));
}
JS_ALWAYS_INLINE

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

@ -148,7 +148,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

@ -142,7 +142,7 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript, bool isConstructi
/* Once a script starts getting really hot we will inline calls in it. */
if (!debugMode() && cx->typeInferenceEnabled() && globalObj &&
(outerScript->useCount() >= USES_BEFORE_INLINING ||
(outerScript->getUseCount() >= USES_BEFORE_INLINING ||
cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS))) {
inlining_ = true;
}
@ -860,7 +860,7 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
jumpTableOffsets.length() * sizeof(void *);
JSC::ExecutablePool *execPool;
uint8 *result = (uint8 *)script->compartment->jaegerCompartment()->execAlloc()->
uint8 *result = (uint8 *)script->compartment()->jaegerCompartment()->execAlloc()->
alloc(codeSize, &execPool, JSC::METHOD_CODE);
if (!result) {
js_ReportOutOfMemory(cx);
@ -2817,7 +2817,7 @@ mjit::Compiler::fullAtomIndex(jsbytecode *pc)
/* If we ever enable INDEXBASE garbage, use this below. */
#if 0
return GET_SLOTNO(pc) + (atoms - script->atomMap.vector);
return GET_SLOTNO(pc) + (atoms - script->atoms);
#endif
}
@ -5497,7 +5497,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

@ -87,7 +87,7 @@ FindExceptionHandler(JSContext *cx)
top:
if (cx->isExceptionPending() && JSScript::isValidOffset(script->trynotesOffset)) {
// The PC is updated before every stub call, so we can use it here.
unsigned offset = cx->regs().pc - script->main;
unsigned offset = cx->regs().pc - script->main();
JSTryNoteArray *tnarray = script->trynotes();
for (unsigned i = 0; i < tnarray->length; ++i) {
@ -113,7 +113,7 @@ top:
if (tn->stackDepth > cx->regs().sp - fp->base())
continue;
jsbytecode *pc = script->main + tn->start + tn->length;
jsbytecode *pc = script->main() + tn->start + tn->length;
cx->regs().pc = pc;
JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE);
JS_ASSERT(cx->regs().sp == fp->base() + tn->stackDepth);

View File

@ -132,7 +132,7 @@ static uint32 StubCallsForOp[STUB_CALLS_FOR_OP_COUNT];
extern "C" void JS_FASTCALL
PushActiveVMFrame(VMFrame &f)
{
f.entryfp->script()->compartment->jaegerCompartment()->pushActiveFrame(&f);
f.entryfp->script()->compartment()->jaegerCompartment()->pushActiveFrame(&f);
f.entryfp->setNativeReturnAddress(JS_FUNC_TO_DATA_PTR(void*, JaegerTrampolineReturn));
f.regs.clearInlined();
}
@ -140,7 +140,7 @@ PushActiveVMFrame(VMFrame &f)
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

@ -437,7 +437,7 @@ Recompiler::recompile(bool resetUses)
// 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

@ -1935,7 +1935,7 @@ SrcNotes(JSContext *cx, JSScript *script, Sprinter *sp)
case SRC_CATCH:
delta = (uintN) js_GetSrcNoteOffset(sn, 0);
if (delta) {
if (script->main[offset] == JSOP_LEAVEBLOCK)
if (script->main()[offset] == JSOP_LEAVEBLOCK)
Sprint(sp, " stack depth %u", delta);
else
Sprint(sp, " guard delta %u", delta);
@ -3912,11 +3912,16 @@ struct FreeOnReturn {
const char *ptr;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
FreeOnReturn(JSContext *cx, const char *ptr JS_GUARD_OBJECT_NOTIFIER_PARAM)
FreeOnReturn(JSContext *cx, const char *ptr = NULL JS_GUARD_OBJECT_NOTIFIER_PARAM)
: cx(cx), ptr(ptr) {
JS_GUARD_OBJECT_NOTIFIER_INIT;
}
void init(const char *ptr) {
JS_ASSERT(!this->ptr);
this->ptr = ptr;
}
~FreeOnReturn() {
JS_free(cx, (void*)ptr);
}
@ -3926,7 +3931,6 @@ static JSBool
Snarf(JSContext *cx, uintN argc, jsval *vp)
{
JSString *str;
const char *pathname;
if (!argc)
return JS_FALSE;
@ -3942,13 +3946,15 @@ Snarf(JSContext *cx, uintN argc, jsval *vp)
JSStackFrame *fp = JS_GetScriptedCaller(cx, NULL);
JSScript *script = JS_GetFrameScript(cx, fp);
JS_ASSERT(fp && script->filename);
const char *pathname = filename.ptr();
#ifdef XP_UNIX
pathname = MakeAbsolutePathname(cx, script->filename, filename.ptr());
if (!pathname)
return JS_FALSE;
FreeOnReturn pnGuard(cx, pathname);
#else
pathname = filename.ptr();
FreeOnReturn pnGuard(cx);
if (pathname[0] != '/') {
pathname = MakeAbsolutePathname(cx, script->filename, pathname);
if (!pathname)
return JS_FALSE;
pnGuard.init(pathname);
}
#endif
if (argc > 1) {
@ -4060,21 +4066,26 @@ MJitCodeStats(JSContext *cx, uintN argc, jsval *vp)
return true;
}
#ifdef JS_METHODJIT
static void
SumJitDataSizeCallabck(JSContext *cx, void *data, void *thing,
JSGCTraceKind traceKind, size_t thingSize)
{
size_t *sump = static_cast<size_t *>(data);
JS_ASSERT(traceKind == JSTRACE_SCRIPT);
JSScript *script = static_cast<JSScript *>(thing);
*sump += script->jitDataSize();
}
#endif
JSBool
MJitDataStats(JSContext *cx, uintN argc, jsval *vp)
{
#ifdef JS_METHODJIT
JSRuntime *rt = cx->runtime;
AutoLockGC lock(rt);
size_t n = 0;
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
for (JSScript *script = (JSScript *)(*c)->scripts.next;
&script->links != &(*c)->scripts;
script = (JSScript *)script->links.next)
{
n += script->jitDataSize();
}
}
IterateCells(cx, NULL, gc::FINALIZE_TYPE_OBJECT, &n, SumJitDataSizeCallabck);
JS_SET_RVAL(cx, vp, INT_TO_JSVAL(n));
#else
JS_SET_RVAL(cx, vp, JSVAL_VOID);

View File

@ -237,7 +237,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);
}
}
@ -479,7 +479,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);
}
}
@ -837,7 +837,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;
@ -1898,8 +1898,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) {
@ -1931,7 +1931,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) {
@ -1950,7 +1950,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++)
@ -2395,7 +2395,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)
@ -2430,7 +2430,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)) {
@ -2458,7 +2458,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;
}
@ -2468,7 +2468,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;
}
@ -2881,8 +2881,7 @@ EvaluateInScope(JSContext *cx, JSObject *scobj, StackFrame *fp, const jschar *ch
if (!script)
return false;
bool ok = ExecuteKernel(cx, script, *scobj, fp->thisValue(), EXECUTE_DEBUG, fp, rval);
return ok;
return ExecuteKernel(cx, script, *scobj, fp->thisValue(), EXECUTE_DEBUG, fp, rval);
}
}

View File

@ -568,14 +568,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

@ -584,7 +584,7 @@ ContextStack::currentScript(jsbytecode **ppc) const
JS_ASSERT(inlined->inlineIndex < fp->jit()->nInlineFrames);
mjit::InlineFrame *frame = &fp->jit()->inlineFrames()[inlined->inlineIndex];
JSScript *script = frame->fun->script();
if (script->compartment != cx_->compartment)
if (script->compartment() != cx_->compartment)
return NULL;
if (ppc)
*ppc = script->code + inlined->pcOffset;
@ -593,7 +593,7 @@ ContextStack::currentScript(jsbytecode **ppc) const
#endif
JSScript *script = fp->script();
if (script->compartment != cx_->compartment)
if (script->compartment() != cx_->compartment)
return NULL;
if (ppc)

View File

@ -892,7 +892,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

@ -845,11 +845,15 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb)
}
else
{
static const char trace_types[JSTRACE_LIMIT][7] = {
static const char trace_types[][11] = {
"Object",
"String",
"Xml"
"Script",
"Xml",
"Shape",
"TypeObject",
};
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(trace_types) == JSTRACE_LAST + 1);
JS_snprintf(name, sizeof(name), "JS %s", trace_types[traceKind]);
}

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
@ -851,7 +851,7 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status)
if(threadLock)
{
// Do the marking...
{ // scoped lock
MutexAutoLock lock(*threadLock);
@ -870,7 +870,7 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status)
// possibly be valid.
if(ccxp->CanGetTearOff())
{
XPCWrappedNativeTearOff* to =
XPCWrappedNativeTearOff* to =
ccxp->GetTearOff();
if(to)
to->Mark();
@ -1042,7 +1042,7 @@ static JSDHashOperator
DetachedWrappedNativeProtoShutdownMarker(JSDHashTable *table, JSDHashEntryHdr *hdr,
uint32 number, void *arg)
{
XPCWrappedNativeProto* proto =
XPCWrappedNativeProto* proto =
(XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key;
proto->SystemIsBeingShutDown((JSContext*)arg);
@ -1250,43 +1250,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
void
GetCompartmentMjitCodeStats(JSCompartment *c, size_t& method, size_t& regexp,
size_t& unused)
{
c->getMjitCodeStats(method, regexp, unused);
}
PRInt64
GetCompartmentMjitDataSize(JSCompartment *c)
{
PRInt64 n = 0;
for(JSScript *script = (JSScript *)c->scripts.next;
&script->links != &c->scripts;
script = (JSScript *)script->links.next)
{
n += script->jitDataSize();
}
return n;
}
#endif // JS_METHODJIT
#ifdef JS_TRACER
PRInt64
@ -1338,14 +1301,12 @@ CompartmentCallback(JSContext *cx, void *vdata, JSCompartment *compartment)
data->currCompartmentStats = curr;
// Get the compartment-level numbers.
curr->scripts = GetCompartmentScriptsSize(compartment);
#ifdef JS_METHODJIT
size_t method, regexp, unused;
GetCompartmentMjitCodeStats(compartment, method, regexp, unused);
compartment->getMjitCodeStats(method, regexp, unused);
curr->mjitCodeMethod = method;
curr->mjitCodeRegexp = regexp;
curr->mjitCodeUnused = unused;
curr->mjitData = GetCompartmentMjitDataSize(compartment);
#endif
#ifdef JS_TRACER
curr->tjitCode = GetCompartmentTjitCodeSize(compartment);
@ -1358,7 +1319,7 @@ CompartmentCallback(JSContext *cx, void *vdata, JSCompartment *compartment)
void
ArenaCallback(JSContext *cx, void *vdata, js::gc::Arena *arena,
size_t traceKind, size_t thingSize)
JSGCTraceKind traceKind, size_t thingSize)
{
IterateData *data = static_cast<IterateData *>(vdata);
data->currCompartmentStats->gcHeapArenaHeaders +=
@ -1373,39 +1334,52 @@ ArenaCallback(JSContext *cx, void *vdata, js::gc::Arena *arena,
}
void
CellCallback(JSContext *cx, void *vdata, void *thing, size_t traceKind,
CellCallback(JSContext *cx, void *vdata, void *thing, JSGCTraceKind traceKind,
size_t thingSize)
{
IterateData *data = static_cast<IterateData *>(vdata);
CompartmentStats *curr = data->currCompartmentStats;
if(traceKind == JSTRACE_OBJECT)
curr->gcHeapKinds[traceKind] += thingSize;
switch (traceKind)
{
curr->gcHeapObjects += thingSize;
JSObject *obj = static_cast<JSObject *>(thing);
curr->objectSlots += JS_ObjectCountDynamicSlots(obj) * sizeof(js::Value);
}
else if(traceKind == JSTRACE_STRING)
{
curr->gcHeapStrings += thingSize;
JSString *str = static_cast<JSString *>(thing);
curr->stringChars += str->charsHeapSize();
}
else if(traceKind == JSTRACE_SHAPE)
{
curr->gcHeapShapes += thingSize;
js::Shape *shape = static_cast<js::Shape *>(thing);
if(shape->hasTable())
curr->propertyTables += shape->getTable()->sizeOf();
}
else if(traceKind == JSTRACE_TYPE_OBJECT)
{
js::types::TypeObject *obj = static_cast<js::types::TypeObject *>(thing);
JS_GetTypeInferenceObjectStats(obj, &curr->typeInferenceMemory);
}
else
{
JS_ASSERT(traceKind == JSTRACE_XML);
curr->gcHeapXml += thingSize;
case JSTRACE_OBJECT:
{
JSObject *obj = static_cast<JSObject *>(thing);
curr->objectSlots += JS_ObjectCountDynamicSlots(obj) * sizeof(js::Value);
break;
}
case JSTRACE_STRING:
{
JSString *str = static_cast<JSString *>(thing);
curr->stringChars += str->charsHeapSize();
break;
}
case JSTRACE_SHAPE:
{
js::Shape *shape = static_cast<js::Shape *>(thing);
if(shape->hasTable())
curr->propertyTables += shape->getTable()->sizeOf();
break;
}
case JSTRACE_SCRIPT:
{
JSScript *script = static_cast<JSScript *>(thing);
curr->scriptData += script->dataSize();
#ifdef JS_METHODJIT
curr->mjitData += script->jitDataSize();
#endif
break;
}
case JSTRACE_TYPE_OBJECT:
{
js::types::TypeObject *obj = static_cast<js::types::TypeObject *>(thing);
JS_GetTypeInferenceObjectStats(obj, &curr->typeInferenceMemory);
break;
}
case JSTRACE_XML:
{
break;
}
}
// Yes, this is a subtraction: see ArenaCallback() for details.
curr->gcHeapArenaUnused -= thingSize;
@ -1670,12 +1644,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;
}
@ -1686,7 +1661,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.
@ -1728,14 +1703,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 "
@ -1743,17 +1718,32 @@ ReportCompartmentStats(const CompartmentStats &stats,
"under 'gc-heap/string-chars' instead.",
callback, closure);
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
"gc-heap/scripts"),
JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_SCRIPT],
"Memory on the compartment's garbage-collected JavaScript heap that holds "
"JSScript instances. A JSScript is created for each user-defined function "
"in a script. One is also created for the top-level code in a script.",
callback, closure);
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
"gc-heap/shapes"),
JS_GC_HEAP_KIND, stats.gcHeapShapes,
JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_SHAPE],
"Memory on the compartment's garbage-collected JavaScript heap that holds "
"shapes. A shape is an internal data structure that makes JavaScript "
"property accesses fast.",
callback, closure);
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
"gc-heap/shapes"),
JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_TYPE_OBJECT],
"Memory on the compartment's garbage-collected JavaScript heap that holds "
"type inference information.",
callback, closure);
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
"gc-heap/xml"),
JS_GC_HEAP_KIND, stats.gcHeapXml,
JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_XML],
"Memory on the compartment's garbage-collected JavaScript heap that holds "
"E4X XML objects.",
callback, closure);
@ -1795,11 +1785,18 @@ ReportCompartmentStats(const CompartmentStats &stats,
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
"scripts"),
nsIMemoryReporter::KIND_HEAP, stats.scripts,
nsIMemoryReporter::KIND_HEAP,
stats.gcHeapKinds[JSTRACE_SCRIPT],
"Memory allocated for the compartment's JSScripts. A JSScript is created "
"for each user-defined function in a script. One is also created for "
"the top-level code in a script. Each JSScript includes byte-code and "
"various other things.",
"the top-level code in a script.",
callback, closure);
ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
"script-data"),
nsIMemoryReporter::KIND_HEAP, stats.scriptData,
"Memory allocated for JSScript bytecode and various variable-length "
"tables.",
callback, closure);
#ifdef JS_METHODJIT

View File

@ -202,16 +202,13 @@ struct CompartmentStats
PRInt64 gcHeapArenaPadding;
PRInt64 gcHeapArenaUnused;
PRInt64 gcHeapObjects;
PRInt64 gcHeapStrings;
PRInt64 gcHeapShapes;
PRInt64 gcHeapXml;
PRInt64 gcHeapKinds[JSTRACE_LAST + 1];
PRInt64 objectSlots;
PRInt64 stringChars;
PRInt64 propertyTables;
PRInt64 scriptData;
PRInt64 scripts;
#ifdef JS_METHODJIT
PRInt64 mjitCodeMethod;
PRInt64 mjitCodeRegexp;