mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 519949 - TM: remove LocalRootScopes r=igor
This commit is contained in:
parent
1acb98c5ab
commit
80ca0e83eb
@ -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)
|
||||
|
112
js/src/jsapi.h
112
js/src/jsapi.h
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
102
js/src/jsgc.cpp
102
js/src/jsgc.cpp
@ -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 *
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user