Bug 519949 - TM: remove LocalRootScopes r=igor

This commit is contained in:
Gregor Wagner 2010-06-23 11:37:35 -05:00
parent 1acb98c5ab
commit 80ca0e83eb
7 changed files with 50 additions and 539 deletions

View File

@ -1925,34 +1925,6 @@ JS_ClearNewbornRoots(JSContext *cx)
JS_CLEAR_WEAK_ROOTS(&cx->weakRoots);
}
JS_PUBLIC_API(JSBool)
JS_EnterLocalRootScope(JSContext *cx)
{
CHECK_REQUEST(cx);
return js_EnterLocalRootScope(cx);
}
JS_PUBLIC_API(void)
JS_LeaveLocalRootScope(JSContext *cx)
{
CHECK_REQUEST(cx);
js_LeaveLocalRootScope(cx);
}
JS_PUBLIC_API(void)
JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval)
{
CHECK_REQUEST(cx);
js_LeaveLocalRootScopeWithResult(cx, rval);
}
JS_PUBLIC_API(void)
JS_ForgetLocalRoot(JSContext *cx, void *thing)
{
CHECK_REQUEST(cx);
js_ForgetLocalRoot(cx, (jsval) thing);
}
#ifdef DEBUG
JS_PUBLIC_API(void)

View File

@ -994,116 +994,14 @@ js_RemoveRoot(JSRuntime *rt, void *rp);
*/
#define JS_TYPED_ROOTING_API
/*
* The last GC thing of each type (object, string, double, external string
* types) created on a given context is kept alive until another thing of the
* same type is created, using a newborn root in the context. These newborn
* roots help native code protect newly-created GC-things from GC invocations
* activated before those things can be rooted using local or global roots.
*
* However, the newborn roots can also entrain great gobs of garbage, so the
* JS_GC entry point clears them for the context on which GC is being forced.
* Embeddings may need to do likewise for all contexts.
*
* See the scoped local root API immediately below for a better way to manage
* newborns in cases where native hooks (functions, getters, setters, etc.)
* create many GC-things, potentially without connecting them to predefined
* local roots such as *rval or argv[i] in an active native function. Using
* JS_EnterLocalRootScope disables updating of the context's per-gc-thing-type
* newborn roots, until control flow unwinds and leaves the outermost nesting
* local root scope.
*/
extern JS_PUBLIC_API(void)
JS_ClearNewbornRoots(JSContext *cx);
/*
* Scoped local root management allows native functions, getter/setters, etc.
* to avoid worrying about the newborn root pigeon-holes, overloading local
* roots allocated in argv and *rval, or ending up having to call JS_Add*Root
* and JS_Remove*Root to manage global roots temporarily.
*
* Instead, calling JS_EnterLocalRootScope and JS_LeaveLocalRootScope around
* the body of the native hook causes the engine to allocate a local root for
* each newborn created in between the two API calls, using a local root stack
* associated with cx. For example:
*
* JSBool
* my_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
* {
* JSBool ok;
*
* if (!JS_EnterLocalRootScope(cx))
* return JS_FALSE;
* ok = my_GetPropertyBody(cx, obj, id, vp);
* JS_LeaveLocalRootScope(cx);
* return ok;
* }
*
* NB: JS_LeaveLocalRootScope must be called once for every prior successful
* call to JS_EnterLocalRootScope. If JS_EnterLocalRootScope fails, you must
* not make the matching JS_LeaveLocalRootScope call.
*
* JS_LeaveLocalRootScopeWithResult(cx, rval) is an alternative way to leave
* a local root scope that protects a result or return value, by effectively
* pushing it in the caller's local root scope.
*
* In case a native hook allocates many objects or other GC-things, but the
* native protects some of those GC-things by storing them as property values
* in an object that is itself protected, the hook can call JS_ForgetLocalRoot
* to free the local root automatically pushed for the now-protected GC-thing.
*
* JS_ForgetLocalRoot works on any GC-thing allocated in the current local
* root scope, but it's more time-efficient when called on references to more
* recently created GC-things. Calling it successively on other than the most
* recently allocated GC-thing will tend to average the time inefficiency, and
* may risk O(n^2) growth rate, but in any event, you shouldn't allocate too
* many local roots if you can root as you go (build a tree of objects from
* the top down, forgetting each latest-allocated GC-thing immediately upon
* linking it to its parent).
*/
extern JS_PUBLIC_API(JSBool)
JS_EnterLocalRootScope(JSContext *cx);
extern JS_PUBLIC_API(void)
JS_LeaveLocalRootScope(JSContext *cx);
extern JS_PUBLIC_API(void)
JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval);
extern JS_PUBLIC_API(void)
JS_ForgetLocalRoot(JSContext *cx, void *thing);
#ifdef __cplusplus
JS_END_EXTERN_C
class JSAutoLocalRootScope {
public:
JSAutoLocalRootScope(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM)
: mContext(cx) {
JS_GUARD_OBJECT_NOTIFIER_INIT;
JS_EnterLocalRootScope(mContext);
}
~JSAutoLocalRootScope() {
JS_LeaveLocalRootScope(mContext);
}
void forget(void *thing) {
JS_ForgetLocalRoot(mContext, thing);
}
protected:
JSContext *mContext;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
#if 0
private:
static void *operator new(size_t) CPP_THROW_NEW { return 0; };
static void operator delete(void *, size_t) { };
#endif
};
JS_BEGIN_EXTERN_C
#endif
/* Obsolete rooting APIs. */
#define JS_EnterLocalRootScope(cx) (true)
#define JS_LeaveLocalRootScope(cx) ((void) 0)
#define JS_LeaveLocalRootScopeWithResult(cx, rval) ((void) 0)
#define JS_ForgetLocalRoot(cx, thing) ((void) 0)
#ifdef DEBUG
extern JS_PUBLIC_API(void)

View File

@ -104,9 +104,6 @@ static const size_t TEMP_POOL_CHUNK_SIZE = 4096 - ARENA_HEADER_SIZE_HACK;
static void
FreeContext(JSContext *cx);
static void
MarkLocalRoots(JSTracer *trc, JSLocalRootStack *lrs);
#ifdef DEBUG
JS_REQUIRES_STACK bool
CallStack::contains(const JSStackFrame *fp) const
@ -538,7 +535,6 @@ JSThreadData::finish()
JS_ASSERT(gcFreeLists.isEmpty());
for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i)
JS_ASSERT(!scriptsToGC[i]);
JS_ASSERT(!localRootStack);
JS_ASSERT(!conservativeGC.isEnabled());
#endif
@ -560,14 +556,12 @@ JSThreadData::mark(JSTracer *trc)
#ifdef JS_TRACER
traceMonitor.mark(trc);
#endif
if (localRootStack)
MarkLocalRoots(trc, localRootStack);
}
void
JSThreadData::purge(JSContext *cx)
{
purgeGCFreeLists();
gcFreeLists.purge();
js_PurgeGSNCache(&gsnCache);
@ -592,17 +586,6 @@ JSThreadData::purge(JSContext *cx)
dtoaCache.s = NULL;
}
void
JSThreadData::purgeGCFreeLists()
{
if (!localRootStack) {
gcFreeLists.purge();
} else {
JS_ASSERT(gcFreeLists.isEmpty());
localRootStack->gcFreeLists.purge();
}
}
#ifdef JS_THREADSAFE
static JSThread *
@ -751,7 +734,7 @@ js_PurgeThreads(JSContext *cx)
* The following is potentially suboptimal as it also zeros the
* caches in data, but the code simplicity wins here.
*/
thread->data.purgeGCFreeLists();
thread->data.gcFreeLists.purge();
DestroyThread(thread);
e.removeFront();
} else {
@ -1362,236 +1345,6 @@ js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
}
JSBool
js_EnterLocalRootScope(JSContext *cx)
{
JSThreadData *td = JS_THREAD_DATA(cx);
JSLocalRootStack *lrs = td->localRootStack;
if (!lrs) {
lrs = (JSLocalRootStack *) js_malloc(sizeof *lrs);
if (!lrs) {
js_ReportOutOfMemory(cx);
return false;
}
lrs->scopeMark = JSLRS_NULL_MARK;
lrs->rootCount = 0;
lrs->topChunk = &lrs->firstChunk;
lrs->firstChunk.down = NULL;
td->gcFreeLists.moveTo(&lrs->gcFreeLists);
td->localRootStack = lrs;
}
/* Push lrs->scopeMark to save it for restore when leaving. */
int mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark));
if (mark < 0)
return JS_FALSE;
lrs->scopeMark = (uint32) mark;
return true;
}
void
js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval)
{
JSLocalRootStack *lrs;
uint32 mark, m, n;
JSLocalRootChunk *lrc;
/* Defend against buggy native callers. */
lrs = JS_THREAD_DATA(cx)->localRootStack;
JS_ASSERT(lrs && lrs->rootCount != 0);
if (!lrs || lrs->rootCount == 0)
return;
mark = lrs->scopeMark;
JS_ASSERT(mark != JSLRS_NULL_MARK);
if (mark == JSLRS_NULL_MARK)
return;
/* Free any chunks being popped by this leave operation. */
m = mark >> JSLRS_CHUNK_SHIFT;
n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT;
while (n > m) {
lrc = lrs->topChunk;
JS_ASSERT(lrc != &lrs->firstChunk);
lrs->topChunk = lrc->down;
js_free(lrc);
--n;
}
/*
* Pop the scope, restoring lrs->scopeMark. If rval is a GC-thing, push
* it on the caller's scope, or store it in lastInternalResult if we are
* leaving the outermost scope. We don't need to allocate a new lrc
* because we can overwrite the old mark's slot with rval.
*/
lrc = lrs->topChunk;
m = mark & JSLRS_CHUNK_MASK;
lrs->scopeMark = (uint32) JSVAL_TO_INT(lrc->roots[m]);
if (JSVAL_IS_GCTHING(rval) && !JSVAL_IS_NULL(rval)) {
if (mark == 0) {
cx->weakRoots.lastInternalResult = rval;
} else {
/*
* Increment m to avoid the "else if (m == 0)" case below. If
* rval is not a GC-thing, that case would take care of freeing
* any chunk that contained only the old mark. Since rval *is*
* a GC-thing here, we want to reuse that old mark's slot.
*/
lrc->roots[m++] = rval;
++mark;
}
}
lrs->rootCount = (uint32) mark;
/*
* Free the stack eagerly, risking malloc churn. The alternative would
* require an lrs->entryCount member, maintained by Enter and Leave, and
* tested by the GC in addition to the cx->localRootStack non-null test.
*
* That approach would risk hoarding 264 bytes (net) per context. Right
* now it seems better to give fresh (dirty in CPU write-back cache, and
* the data is no longer needed) memory back to the malloc heap.
*/
if (mark == 0) {
JSThreadData *td = JS_THREAD_DATA(cx);
JS_ASSERT(td->gcFreeLists.isEmpty());
lrs->gcFreeLists.moveTo(&td->gcFreeLists);
td->localRootStack = NULL;
js_free(lrs);
} else if (m == 0) {
lrs->topChunk = lrc->down;
js_free(lrc);
}
}
void
js_ForgetLocalRoot(JSContext *cx, jsval v)
{
JSLocalRootStack *lrs;
uint32 i, j, m, n, mark;
JSLocalRootChunk *lrc, *lrc2;
jsval top;
lrs = JS_THREAD_DATA(cx)->localRootStack;
JS_ASSERT(lrs && lrs->rootCount);
if (!lrs || lrs->rootCount == 0)
return;
/* Prepare to pop the top-most value from the stack. */
n = lrs->rootCount - 1;
m = n & JSLRS_CHUNK_MASK;
lrc = lrs->topChunk;
top = lrc->roots[m];
/* Be paranoid about calls on an empty scope. */
mark = lrs->scopeMark;
JS_ASSERT(mark < n);
if (mark >= n)
return;
/* If v was not the last root pushed in the top scope, find it. */
if (top != v) {
/* Search downward in case v was recently pushed. */
i = n;
j = m;
lrc2 = lrc;
while (--i > mark) {
if (j == 0)
lrc2 = lrc2->down;
j = i & JSLRS_CHUNK_MASK;
if (lrc2->roots[j] == v)
break;
}
/* If we didn't find v in this scope, assert and bail out. */
JS_ASSERT(i != mark);
if (i == mark)
return;
/* Swap top and v so common tail code can pop v. */
lrc2->roots[j] = top;
}
/* Pop the last value from the stack. */
lrc->roots[m] = JSVAL_NULL;
lrs->rootCount = n;
if (m == 0) {
JS_ASSERT(n != 0);
JS_ASSERT(lrc != &lrs->firstChunk);
lrs->topChunk = lrc->down;
cx->free(lrc);
}
}
int
js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v)
{
uint32 n, m;
JSLocalRootChunk *lrc;
n = lrs->rootCount;
m = n & JSLRS_CHUNK_MASK;
if (n == 0 || m != 0) {
/*
* At start of first chunk, or not at start of a non-first top chunk.
* Check for lrs->rootCount overflow.
*/
if ((uint32)(n + 1) == 0) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_TOO_MANY_LOCAL_ROOTS);
return -1;
}
lrc = lrs->topChunk;
JS_ASSERT(n != 0 || lrc == &lrs->firstChunk);
} else {
/*
* After lrs->firstChunk, trying to index at a power-of-two chunk
* boundary: need a new chunk.
*/
lrc = (JSLocalRootChunk *) js_malloc(sizeof *lrc);
if (!lrc) {
js_ReportOutOfMemory(cx);
return -1;
}
lrc->down = lrs->topChunk;
lrs->topChunk = lrc;
}
lrs->rootCount = n + 1;
lrc->roots[m] = v;
return (int) n;
}
static void
MarkLocalRoots(JSTracer *trc, JSLocalRootStack *lrs)
{
uint32 n, m, mark;
JSLocalRootChunk *lrc;
jsval v;
n = lrs->rootCount;
if (n == 0)
return;
mark = lrs->scopeMark;
lrc = lrs->topChunk;
do {
while (--n > mark) {
m = n & JSLRS_CHUNK_MASK;
v = lrc->roots[m];
JS_ASSERT(JSVAL_IS_GCTHING(v) && v != JSVAL_NULL);
JS_SET_TRACING_INDEX(trc, "local_root", n);
js_CallValueTracerIfGCThing(trc, v);
if (m == 0)
lrc = lrc->down;
}
m = n & JSLRS_CHUNK_MASK;
mark = JSVAL_TO_INT(lrc->roots[m]);
if (m == 0)
lrc = lrc->down;
} while (n != 0);
JS_ASSERT(!lrc);
}
static void
ReportError(JSContext *cx, const char *message, JSErrorReport *reportp,
JSErrorCallback callback, void *userRef)
@ -2438,7 +2191,7 @@ JSContext::checkMallocGCPressure(void *p)
* allocator. We cannot touch the free lists on other threads as
* their manipulation is not thread-safe.
*/
JS_THREAD_DATA(this)->purgeGCFreeLists();
JS_THREAD_DATA(this)->gcFreeLists.purge();
js_TriggerGC(this, true);
}
}

View File

@ -957,29 +957,6 @@ struct JSFunctionMeter {
# undef identity
#endif
struct JSLocalRootChunk;
#define JSLRS_CHUNK_SHIFT 8
#define JSLRS_CHUNK_SIZE JS_BIT(JSLRS_CHUNK_SHIFT)
#define JSLRS_CHUNK_MASK JS_BITMASK(JSLRS_CHUNK_SHIFT)
struct JSLocalRootChunk {
jsval roots[JSLRS_CHUNK_SIZE];
JSLocalRootChunk *down;
};
struct JSLocalRootStack {
uint32 scopeMark;
uint32 rootCount;
JSLocalRootChunk *topChunk;
JSLocalRootChunk firstChunk;
/* See comments in js_NewFinalizableGCThing. */
JSGCFreeLists gcFreeLists;
};
const uint32 JSLRS_NULL_MARK = uint32(-1);
#define NATIVE_ITER_CACHE_LOG2 8
#define NATIVE_ITER_CACHE_MASK JS_BITMASK(NATIVE_ITER_CACHE_LOG2)
#define NATIVE_ITER_CACHE_SIZE JS_BIT(NATIVE_ITER_CACHE_LOG2)
@ -1011,9 +988,6 @@ struct JSThreadData {
/* Property cache for faster call/get/set invocation. */
js::PropertyCache propertyCache;
/* Optional stack of heap-allocated scoped local GC roots. */
JSLocalRootStack *localRootStack;
#ifdef JS_TRACER
/* Trace-tree JIT recorder/interpreter state. */
js::TraceMonitor traceMonitor;
@ -1055,7 +1029,6 @@ struct JSThreadData {
void finish();
void mark(JSTracer *trc);
void purge(JSContext *cx);
void purgeGCFreeLists();
};
#ifdef JS_THREADSAFE
@ -2767,29 +2740,6 @@ extern void
js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
JSResolvingEntry *entry, uint32 generation);
/*
* Local root set management.
*
* NB: the jsval parameters below may be properly tagged jsvals, or GC-thing
* pointers cast to (jsval). This relies on JSObject's tag being zero, but
* on the up side it lets us push int-jsval-encoded scopeMark values on the
* local root stack.
*/
extern JSBool
js_EnterLocalRootScope(JSContext *cx);
#define js_LeaveLocalRootScope(cx) \
js_LeaveLocalRootScopeWithResult(cx, JSVAL_NULL)
extern void
js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval);
extern void
js_ForgetLocalRoot(JSContext *cx, jsval v);
extern int
js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v);
/*
* Report an exception, which is currently realized as a printf-style format
* string and its arguments.

View File

@ -1747,16 +1747,6 @@ IsGCThresholdReached(JSRuntime *rt)
return rt->isGCMallocLimitReached() || rt->gcBytes >= rt->gcTriggerBytes;
}
static inline JSGCFreeLists *
GetGCFreeLists(JSContext *cx)
{
JSThreadData *td = JS_THREAD_DATA(cx);
if (!td->localRootStack)
return &td->gcFreeLists;
JS_ASSERT(td->gcFreeLists.isEmpty());
return &td->localRootStack->gcFreeLists;
}
static void
LastDitchGC(JSContext *cx)
{
@ -1779,7 +1769,7 @@ LastDitchGC(JSContext *cx)
static JSGCThing *
RefillFinalizableFreeList(JSContext *cx, unsigned thingKind)
{
JS_ASSERT(!GetGCFreeLists(cx)->finalizables[thingKind]);
JS_ASSERT(!JS_THREAD_DATA(cx)->gcFreeLists.finalizables[thingKind]);
JSRuntime *rt = cx->runtime;
JSGCArenaList *arenaList;
JSGCArena *a;
@ -1806,7 +1796,7 @@ RefillFinalizableFreeList(JSContext *cx, unsigned thingKind)
* things and populate the free list. If that happens, just
* return that list head.
*/
JSGCThing *freeList = GetGCFreeLists(cx)->finalizables[thingKind];
JSGCThing *freeList = JS_THREAD_DATA(cx)->gcFreeLists.finalizables[thingKind];
if (freeList)
return freeList;
}
@ -1878,7 +1868,6 @@ js_NewFinalizableGCThing(JSContext *cx, unsigned thingKind)
JS_THREAD_DATA(cx)->gcFreeLists.finalizables + thingKind;
JSGCThing *thing = *freeListp;
if (thing) {
JS_ASSERT(!JS_THREAD_DATA(cx)->localRootStack);
*freeListp = thing->link;
cx->weakRoots.finalizableNewborns[thingKind] = thing;
CheckGCFreeListLink(thing);
@ -1886,60 +1875,22 @@ js_NewFinalizableGCThing(JSContext *cx, unsigned thingKind)
return thing;
}
/*
* To avoid for the local roots on each GC allocation when the local roots
* are not active we move the GC free lists from JSThreadData to lrs in
* JS_EnterLocalRootScope(). This way with inactive local roots we only
* check for non-null lrs only when we exhaust the free list.
*/
JSLocalRootStack *lrs = JS_THREAD_DATA(cx)->localRootStack;
for (;;) {
if (lrs) {
freeListp = lrs->gcFreeLists.finalizables + thingKind;
thing = *freeListp;
if (thing) {
*freeListp = thing->link;
METER(cx->runtime->gcStats.arenaStats[thingKind].localalloc++);
break;
}
}
thing = RefillFinalizableFreeList(cx, thingKind);
if (thing) {
/*
* See comments in RefillFinalizableFreeList about a possibility
* of *freeListp == thing.
*/
JS_ASSERT(!*freeListp || *freeListp == thing);
*freeListp = thing->link;
break;
}
thing = RefillFinalizableFreeList(cx, thingKind);
if (!thing) {
js_ReportOutOfMemory(cx);
return NULL;
}
/*
* See comments in RefillFinalizableFreeList about a possibility
* of *freeListp == thing.
*/
JS_ASSERT(!*freeListp || *freeListp == thing);
*freeListp = thing->link;
CheckGCFreeListLink(thing);
if (lrs) {
/*
* If we're in a local root scope, don't set newborn[type] at all, to
* avoid entraining garbage from it for an unbounded amount of time
* on this context. A caller will leave the local root scope and pop
* this reference, allowing thing to be GC'd if it has no other refs.
* See JS_EnterLocalRootScope and related APIs.
*/
if (js_PushLocalRoot(cx, lrs, (jsval) thing) < 0) {
JS_ASSERT(thing->link == *freeListp);
*freeListp = thing;
return NULL;
}
} else {
/*
* No local root scope, so we're stuck with the old, fragile model of
* depending on a pigeon-hole newborn per type per context.
*/
cx->weakRoots.finalizableNewborns[thingKind] = thing;
}
cx->weakRoots.finalizableNewborns[thingKind] = thing;
return thing;
}
@ -2001,7 +1952,7 @@ TurnUsedArenaIntoDoubleList(JSGCArena *a)
static JSGCThing *
RefillDoubleFreeList(JSContext *cx)
{
JS_ASSERT(!GetGCFreeLists(cx)->doubles);
JS_ASSERT(!JS_THREAD_DATA(cx)->gcFreeLists.doubles);
JSRuntime *rt = cx->runtime;
JS_ASSERT(!rt->gcRunning);
@ -2018,7 +1969,7 @@ RefillDoubleFreeList(JSContext *cx)
canGC = false;
/* See comments in RefillFinalizableFreeList. */
JSGCThing *freeList = GetGCFreeLists(cx)->doubles;
JSGCThing *freeList = JS_THREAD_DATA(cx)->gcFreeLists.doubles;
if (freeList) {
JS_UNLOCK_GC(rt);
return freeList;
@ -2069,7 +2020,6 @@ js_NewDoubleInRootedValue(JSContext *cx, jsdouble d, jsval *vp)
JSGCThing *thing = *freeListp;
if (thing) {
METER(cx->runtime->gcStats.doubleArenaStats.localalloc++);
JS_ASSERT(!JS_THREAD_DATA(cx)->localRootStack);
CheckGCFreeListLink(thing);
*freeListp = thing->link;
@ -2079,22 +2029,8 @@ js_NewDoubleInRootedValue(JSContext *cx, jsdouble d, jsval *vp)
return true;
}
JSLocalRootStack *lrs = JS_THREAD_DATA(cx)->localRootStack;
for (;;) {
if (lrs) {
freeListp = &lrs->gcFreeLists.doubles;
thing = *freeListp;
if (thing) {
METER(cx->runtime->gcStats.doubleArenaStats.localalloc++);
break;
}
}
thing = RefillDoubleFreeList(cx);
if (thing) {
JS_ASSERT(!*freeListp || *freeListp == thing);
break;
}
thing = RefillDoubleFreeList(cx);
if (!thing) {
if (!JS_ON_TRACE(cx)) {
/* Trace code handle this on its own. */
js_ReportOutOfMemory(cx);
@ -2103,13 +2039,15 @@ js_NewDoubleInRootedValue(JSContext *cx, jsdouble d, jsval *vp)
return false;
}
JS_ASSERT(!*freeListp || *freeListp == thing);
CheckGCFreeListLink(thing);
*freeListp = thing->link;
jsdouble *dp = reinterpret_cast<jsdouble *>(thing);
*dp = d;
*vp = DOUBLE_TO_JSVAL(dp);
return !lrs || js_PushLocalRoot(cx, lrs, *vp) >= 0;
return true;
}
jsdouble *

View File

@ -696,22 +696,14 @@ js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags,
return JS_FALSE;
/*
* Store *rval in the a scoped local root if a scope is open, else in
* the lastInternalResult pigeon-hole GC root, solely so users of
* js_InternalInvoke and its direct and indirect (js_ValueToString for
* example) callers do not need to manage roots for local, temporary
* references to such results.
* Store *rval in the lastInternalResult pigeon-hole GC root, solely
* so users of js_InternalInvoke and its direct and indirect
* (js_ValueToString for example) callers do not need to manage roots
* for local, temporary references to such results.
*/
*rval = *args.getvp();
if (JSVAL_IS_GCTHING(*rval) && *rval != JSVAL_NULL) {
JSLocalRootStack *lrs = JS_THREAD_DATA(cx)->localRootStack;
if (lrs) {
if (js_PushLocalRoot(cx, lrs, *rval) < 0)
return JS_FALSE;
} else {
cx->weakRoots.lastInternalResult = *rval;
}
}
if (JSVAL_IS_GCTHING(*rval) && *rval != JSVAL_NULL)
cx->weakRoots.lastInternalResult = *rval;
return JS_TRUE;
}

View File

@ -91,6 +91,22 @@ using namespace js;
* - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM!
*/
static inline bool
js_EnterLocalRootScope(JSContext *cx)
{
return true;
}
static inline void
js_LeaveLocalRootScope(JSContext *cx)
{
}
static inline void
js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval)
{
}
#ifdef XML_METERING
static struct {
jsrefcount qname;
@ -2944,8 +2960,6 @@ DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent,
JSXML *kid2;
JSString *str;
JS_ASSERT(JS_THREAD_DATA(cx)->localRootStack);
n = from->length;
if (!to->setCapacity(cx, n))
return JS_FALSE;
@ -3001,9 +3015,6 @@ DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags)
uint32 i, n;
JSObject *ns, *ns2;
/* Our caller must be protecting newborn objects. */
JS_ASSERT(JS_THREAD_DATA(cx)->localRootStack);
JS_CHECK_RECURSION(cx, return NULL);
copy = js_NewXML(cx, JSXMLClass(xml->xml_class));
@ -4380,9 +4391,6 @@ ResolveValue(JSContext *cx, JSXML *list, JSXML **result)
JSObject *targetprop;
jsval id, tv;
/* Our caller must be protecting newborn objects. */
JS_ASSERT(JS_THREAD_DATA(cx)->localRootStack);
if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) {
if (!js_GetXMLObject(cx, list))
return JS_FALSE;