Add scoped local root support, plus a few cleanups from the huge e4x patch (40757, r=shaver).

This commit is contained in:
brendan%mozilla.org 2004-08-19 17:57:36 +00:00
parent da25ffb6d6
commit 5a95353303
11 changed files with 505 additions and 182 deletions

View File

@ -97,7 +97,7 @@ MSG_DEF(JSMSG_TOO_MANY_LITERALS, 14, 0, JSEXN_INTERNALERR, "too many liter
MSG_DEF(JSMSG_CANT_WATCH, 15, 1, JSEXN_NONE, "can't watch non-native objects of class {0}")
MSG_DEF(JSMSG_STACK_UNDERFLOW, 16, 2, JSEXN_INTERNALERR, "internal error compiling {0}: stack underflow at pc {1}")
MSG_DEF(JSMSG_NEED_DIET, 17, 1, JSEXN_INTERNALERR, "{0} too large")
MSG_DEF(JSMSG_UNUSED18, 18, 0, JSEXN_NONE, "<Error #18 is currently unused>")
MSG_DEF(JSMSG_TOO_MANY_LOCAL_ROOTS, 18, 0, JSEXN_ERR, "out of local root space")
MSG_DEF(JSMSG_READ_ONLY, 19, 1, JSEXN_ERR, "{0} is read-only")
MSG_DEF(JSMSG_BAD_FORMAL, 20, 0, JSEXN_SYNTAXERR, "malformed formal parameter")
MSG_DEF(JSMSG_SAME_FORMAL, 21, 1, JSEXN_NONE, "duplicate formal argument {0}")
@ -225,7 +225,7 @@ MSG_DEF(JSMSG_BAD_REGEXP_FLAG, 142, 0, JSEXN_SYNTAXERR, "invalid flag aft
MSG_DEF(JSMSG_SHARPVAR_TOO_BIG, 143, 0, JSEXN_SYNTAXERR, "overlarge sharp variable number")
MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 144, 0, JSEXN_SYNTAXERR, "illegal character")
MSG_DEF(JSMSG_BAD_OCTAL, 145, 1, JSEXN_NONE, "{0} is not a legal ECMA-262 octal constant")
MSG_DEF(JSMSG_BAD_INDIRECT_CALL, 146, 1, JSEXN_EVALERR, "function {0} must be called directly, and not by way of a function of another name.")
MSG_DEF(JSMSG_BAD_INDIRECT_CALL, 146, 1, JSEXN_EVALERR, "function {0} must be called directly, and not by way of a function of another name")
MSG_DEF(JSMSG_UNCAUGHT_EXCEPTION, 147, 1, JSEXN_NONE, "uncaught exception: {0}")
MSG_DEF(JSMSG_INVALID_BACKREF, 148, 0, JSEXN_SYNTAXERR, "non-octal digit in an escape sequence that doesn't match a back-reference")
MSG_DEF(JSMSG_BAD_BACKREF, 149, 0, JSEXN_SYNTAXERR, "back-reference exceeds number of capturing parentheses")

View File

@ -1561,6 +1561,27 @@ JS_ClearNewbornRoots(JSContext *cx)
cx->lastAtom = NULL;
}
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_ForgetLocalRoot(JSContext *cx, void *thing)
{
CHECK_REQUEST(cx);
js_ForgetLocalRoot(cx, (jsval) thing);
}
#include "jshash.h" /* Added by JSIFY */
#ifdef DEBUG
@ -3288,26 +3309,7 @@ JS_NewScriptObject(JSContext *cx, JSScript *script)
{
JSObject *obj;
/*
* We use a dummy stack frame to protect the script from a GC caused
* by debugger-hook execution.
*
* XXX We really need a way to manage local roots and such more
* XXX automatically, at which point we can remove this one-off hack
* XXX and others within the engine. See bug 40757 for discussion.
*/
JSStackFrame dummy;
CHECK_REQUEST(cx);
memset(&dummy, 0, sizeof dummy);
dummy.down = cx->fp;
dummy.script = script;
cx->fp = &dummy;
obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL);
cx->fp = dummy.down;
if (!obj)
return NULL;

View File

@ -567,13 +567,67 @@ JS_RemoveRootRT(JSRuntime *rt, void *rp);
* JS_GC entry point clears them for the context on which GC is being forced.
* Embeddings may need to do likewise for all contexts.
*
* XXXbe See bug 40757 (http://bugzilla.mozilla.org/show_bug.cgi?id=40757),
* which proposes switching (with an #ifdef, alas, if we want to maintain API
* compatibility) to a JNI-like extensible local root frame stack model.
* 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_RemoveRoot 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.
*
* 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_ForgetLocalRoot(JSContext *cx, void *thing);
#ifdef DEBUG
extern JS_PUBLIC_API(void)
JS_DumpNamedRoots(JSRuntime *rt,

View File

@ -162,6 +162,8 @@ js_DestroyContext(JSContext *cx, JSGCMode gcmode)
JSRuntime *rt;
JSBool last;
JSArgumentFormatMap *map;
JSLocalRootStack *lrs;
JSLocalRootChunk *lrc;
rt = cx->runtime;
@ -275,6 +277,15 @@ js_DestroyContext(JSContext *cx, JSGCMode gcmode)
cx->resolvingTable = NULL;
}
lrs = cx->localRootStack;
if (lrs) {
while ((lrc = lrs->topChunk) != &lrs->firstChunk) {
lrs->topChunk = lrc->down;
JS_free(cx, lrc);
}
JS_free(cx, lrs);
}
/* Finally, free cx itself. */
free(cx);
}
@ -310,6 +321,319 @@ js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp)
return cx;
}
JS_STATIC_DLL_CALLBACK(const void *)
resolving_GetKey(JSDHashTable *table, JSDHashEntryHdr *hdr)
{
JSResolvingEntry *entry = (JSResolvingEntry *)hdr;
return &entry->key;
}
JS_STATIC_DLL_CALLBACK(JSDHashNumber)
resolving_HashKey(JSDHashTable *table, const void *ptr)
{
const JSResolvingKey *key = (const JSResolvingKey *)ptr;
return ((JSDHashNumber)key->obj >> JSVAL_TAGBITS) ^ key->id;
}
JS_PUBLIC_API(JSBool)
resolving_MatchEntry(JSDHashTable *table,
const JSDHashEntryHdr *hdr,
const void *ptr)
{
const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
const JSResolvingKey *key = (const JSResolvingKey *)ptr;
return entry->key.obj == key->obj && entry->key.id == key->id;
}
static const JSDHashTableOps resolving_dhash_ops = {
JS_DHashAllocTable,
JS_DHashFreeTable,
resolving_GetKey,
resolving_HashKey,
resolving_MatchEntry,
JS_DHashMoveEntryStub,
JS_DHashClearEntryStub,
JS_DHashFinalizeStub,
NULL
};
JSBool
js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
JSResolvingEntry **entryp)
{
JSDHashTable *table;
JSResolvingEntry *entry;
table = cx->resolvingTable;
if (!table) {
table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
sizeof(JSResolvingEntry),
JS_DHASH_MIN_SIZE);
if (!table)
goto outofmem;
cx->resolvingTable = table;
}
entry = (JSResolvingEntry *)
JS_DHashTableOperate(table, key, JS_DHASH_ADD);
if (!entry)
goto outofmem;
if (entry->flags & flag) {
/* An entry for (key, flag) exists already -- dampen recursion. */
entry = NULL;
} else {
/* Fill in key if we were the first to add entry, then set flag. */
if (!entry->key.obj)
entry->key = *key;
entry->flags |= flag;
}
*entryp = entry;
return JS_TRUE;
outofmem:
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
void
js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
JSResolvingEntry *entry, uint32 generation)
{
JSDHashTable *table;
/*
* Clear flag from entry->flags and return early if other flags remain.
* We must take care to re-lookup entry if the table has changed since
* it was found by js_StartResolving.
*/
table = cx->resolvingTable;
if (!entry || table->generation != generation) {
entry = (JSResolvingEntry *)
JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);
}
JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr));
entry->flags &= ~flag;
if (entry->flags)
return;
/*
* Do a raw remove only if fewer entries were removed than would cause
* alpha to be less than .5 (alpha is at most .75). Otherwise, we just
* call JS_DHashTableOperate to re-lookup the key and remove its entry,
* compressing or shrinking the table as needed.
*/
if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2)
JS_DHashTableRawRemove(table, &entry->hdr);
else
JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
}
JSBool
js_EnterLocalRootScope(JSContext *cx)
{
JSLocalRootStack *lrs;
int mark;
lrs = cx->localRootStack;
if (!lrs) {
lrs = (JSLocalRootStack *) JS_malloc(cx, sizeof *lrs);
if (!lrs)
return JS_FALSE;
lrs->scopeMark = JSLRS_NULL_MARK;
lrs->rootCount = 0;
lrs->topChunk = &lrs->firstChunk;
lrs->firstChunk.down = NULL;
cx->localRootStack = lrs;
}
/* Push lrs->scopeMark to save it for restore when leaving. */
mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark));
if (mark < 0)
return JS_FALSE;
lrs->scopeMark = (uint16) mark;
return JS_TRUE;
}
void
js_LeaveLocalRootScope(JSContext *cx)
{
JSLocalRootStack *lrs;
unsigned mark, m, n;
JSLocalRootChunk *lrc;
/* Defend against buggy native callers. */
lrs = 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(cx, lrc);
--n;
}
/* Pop the scope, restoring lrs->scopeMark. */
lrc = lrs->topChunk;
m = mark & JSLRS_CHUNK_MASK;
lrs->scopeMark = JSVAL_TO_INT(lrc->roots[m]);
lrc->roots[m] = JSVAL_NULL;
lrs->rootCount = (uint16) 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) {
cx->localRootStack = NULL;
JS_free(cx, lrs);
} else if (m == 0) {
lrs->topChunk = lrc->down;
JS_free(cx, lrc);
}
}
void
js_ForgetLocalRoot(JSContext *cx, jsval v)
{
JSLocalRootStack *lrs;
unsigned i, j, m, n, mark;
JSLocalRootChunk *lrc, *lrc2;
jsval top;
lrs = 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;
JS_free(cx, lrc);
}
}
int
js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v)
{
unsigned 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 ((uint16)(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(cx, sizeof *lrc);
if (!lrc)
return -1;
lrc->down = lrs->topChunk;
lrs->topChunk = lrc;
}
lrs->rootCount = n + 1;
lrc->roots[m] = v;
return (int) m;
}
void
js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs)
{
unsigned n, m, mark;
JSLocalRootChunk *lrc;
n = lrs->rootCount;
if (n == 0)
return;
mark = lrs->scopeMark;
lrc = lrs->topChunk;
while (--n > mark) {
#ifdef GC_MARK_DEBUG
char name[22];
JS_snprintf(name, sizeof name, "<local root %u>", n);
#else
const char *name = NULL;
#endif
m = n & JSLRS_CHUNK_MASK;
JS_ASSERT(JSVAL_IS_GCTHING(lrc->roots[m]));
JS_MarkGCThing(cx, JSVAL_TO_GCTHING(lrc->roots[m]), name, NULL);
if (m == 0)
lrc = lrc->down;
}
}
static void
ReportError(JSContext *cx, const char *message, JSErrorReport *reportp)
{

View File

@ -308,6 +308,26 @@ typedef struct JSResolvingEntry {
#define JSRESFLAG_LOOKUP 0x1 /* resolving id from lookup */
#define JSRESFLAG_WATCH 0x2 /* resolving id from watch */
typedef struct JSLocalRootChunk JSLocalRootChunk;
#define JSLRS_CHUNK_SHIFT 6
#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;
};
typedef struct JSLocalRootStack {
uint16 scopeMark;
uint16 rootCount;
JSLocalRootChunk *topChunk;
JSLocalRootChunk firstChunk;
} JSLocalRootStack;
#define JSLRS_NULL_MARK ((uint16) -1)
struct JSContext {
JSCList links;
@ -417,6 +437,9 @@ struct JSContext {
/* Optional hook to find principals for an object being accessed on cx. */
JSObjectPrincipalsFinder findObjectPrincipals;
/* Optional stack of scoped local GC roots. */
JSLocalRootStack *localRootStack;
};
/*
@ -448,6 +471,35 @@ js_ValidContextPointer(JSRuntime *rt, JSContext *cx);
extern JSContext *
js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp);
/*
* JSClass.resolve and watchpoint recursion damping machinery.
*/
extern JSBool
js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
JSResolvingEntry **entryp);
extern void
js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
JSResolvingEntry *entry, uint32 generation);
/*
* Local root set management.
*/
extern JSBool
js_EnterLocalRootScope(JSContext *cx);
extern void
js_LeaveLocalRootScope(JSContext *cx);
extern void
js_ForgetLocalRoot(JSContext *cx, jsval v);
extern int
js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v);
extern void
js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs);
/*
* Report an exception, which is currently realized as a printf-style format
* string and its arguments.

View File

@ -458,6 +458,7 @@ js_AllocGCThing(JSContext *cx, uintN flags)
JSRuntime *rt;
JSGCThing *thing;
uint8 *flagp;
JSLocalRootStack *lrs;
#ifdef TOO_MUCH_GC
js_GC(cx, GC_KEEP_ATOMS);
@ -531,18 +532,35 @@ retry:
METER(rt->gcStats.retry++);
goto retry;
}
METER(rt->gcStats.fail++);
JS_UNLOCK_GC(rt);
JS_ReportOutOfMemory(cx);
return NULL;
goto fail;
}
/* Find the flags pointer given thing's address. */
flagp = js_GetGCThingFlags(thing);
}
lrs = cx->localRootStack;
if (lrs) {
/*
* If we're in a local root scope, don't set cx->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)
goto fail;
} 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->newborn[flags & GCF_TYPEMASK] = thing;
}
/* We can't fail now, so update flags and rt->gcBytes. */
*flagp = (uint8)flags;
rt->gcBytes += sizeof(JSGCThing) + sizeof(uint8);
cx->newborn[flags & GCF_TYPEMASK] = thing;
/*
* Clear thing before unlocking in case a GC run is about to scan it,
@ -552,6 +570,12 @@ retry:
thing->flagp = NULL;
JS_UNLOCK_GC(rt);
return thing;
fail:
METER(rt->gcStats.fail++);
JS_UNLOCK_GC(rt);
JS_ReportOutOfMemory(cx);
return NULL;
}
JSBool
@ -584,7 +608,7 @@ js_LockGCThingRT(JSRuntime *rt, void *thing)
/* Objects may require "deep locking", i.e., rooting by value. */
if (lockbits == 0) {
if (!rt->gcLocksHash) {
rt->gcLocksHash =
rt->gcLocksHash =
JS_NewDHashTable(JS_DHashGetStubOps(), NULL,
sizeof(JSGCLockHashEntry),
GC_ROOTS_SIZE);
@ -592,7 +616,7 @@ js_LockGCThingRT(JSRuntime *rt, void *thing)
goto error;
} else {
#ifdef DEBUG
JSDHashEntryHdr *hdr =
JSDHashEntryHdr *hdr =
JS_DHashTableOperate(rt->gcLocksHash, thing,
JS_DHASH_LOOKUP);
JS_ASSERT(JS_DHASH_ENTRY_IS_FREE(hdr));
@ -1243,18 +1267,6 @@ restart:
GC_MARK(cx, fp->scopeChain, "scope chain", NULL);
if (fp->sharpArray)
GC_MARK(cx, fp->sharpArray, "sharp array", NULL);
if (fp->objAtomMap) {
JSAtom **vector, *atom;
nslots = fp->objAtomMap->length;
vector = fp->objAtomMap->vector;
for (i = 0; i < nslots; i++) {
atom = vector[i];
if (atom)
GC_MARK_ATOM(cx, atom, NULL);
}
}
} while ((fp = fp->down) != NULL);
}
@ -1287,6 +1299,9 @@ restart:
METER(rt->gcStats.segslots += sh->nslots);
GC_MARK_JSVALS(cx, sh->nslots, JS_STACK_SEGMENT(sh), "stack");
}
if (acx->localRootStack)
js_MarkLocalRoots(cx, acx->localRootStack);
}
#ifdef DUMP_CALL_TABLE
js_DumpCallTable(cx);

View File

@ -1180,7 +1180,6 @@ have_fun:
frame.sharpDepth = 0;
frame.sharpArray = NULL;
frame.dormantNext = NULL;
frame.objAtomMap = NULL;
/* Compute the 'this' parameter and store it in frame as frame.thisp. */
ok = ComputeThis(cx, thisp, &frame);
@ -1479,7 +1478,6 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
frame.sharpDepth = 0;
frame.flags = flags;
frame.dormantNext = NULL;
frame.objAtomMap = NULL;
/*
* Here we wrap the call to js_Interpret with code to (conditionally)

View File

@ -72,8 +72,6 @@ struct JSStackFrame {
JSObject *sharpArray; /* scope for #n= initializer vars */
uint32 flags; /* frame flags -- see below */
JSStackFrame *dormantNext; /* next dormant frame chain */
JSAtomMap *objAtomMap; /* object atom map, non-null only if we
hit a regexp object literal */
};
typedef struct JSInlineFrame {

View File

@ -1125,116 +1125,6 @@ out:
return ok;
}
JS_STATIC_DLL_CALLBACK(const void *)
resolving_GetKey(JSDHashTable *table, JSDHashEntryHdr *hdr)
{
JSResolvingEntry *entry = (JSResolvingEntry *)hdr;
return &entry->key;
}
JS_STATIC_DLL_CALLBACK(JSDHashNumber)
resolving_HashKey(JSDHashTable *table, const void *ptr)
{
const JSResolvingKey *key = (const JSResolvingKey *)ptr;
return ((JSDHashNumber)key->obj >> JSVAL_TAGBITS) ^ key->id;
}
JS_PUBLIC_API(JSBool)
resolving_MatchEntry(JSDHashTable *table,
const JSDHashEntryHdr *hdr,
const void *ptr)
{
const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
const JSResolvingKey *key = (const JSResolvingKey *)ptr;
return entry->key.obj == key->obj && entry->key.id == key->id;
}
static const JSDHashTableOps resolving_dhash_ops = {
JS_DHashAllocTable,
JS_DHashFreeTable,
resolving_GetKey,
resolving_HashKey,
resolving_MatchEntry,
JS_DHashMoveEntryStub,
JS_DHashClearEntryStub,
JS_DHashFinalizeStub,
NULL
};
static JSBool
StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
JSResolvingEntry **entryp)
{
JSDHashTable *table;
JSResolvingEntry *entry;
table = cx->resolvingTable;
if (!table) {
table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
sizeof(JSResolvingEntry),
JS_DHASH_MIN_SIZE);
if (!table)
goto outofmem;
cx->resolvingTable = table;
}
entry = (JSResolvingEntry *)
JS_DHashTableOperate(table, key, JS_DHASH_ADD);
if (!entry)
goto outofmem;
if (entry->flags & flag) {
/* An entry for (key, flag) exists already -- dampen recursion. */
entry = NULL;
} else {
/* Fill in key if we were the first to add entry, then set flag. */
if (!entry->key.obj)
entry->key = *key;
entry->flags |= flag;
}
*entryp = entry;
return JS_TRUE;
outofmem:
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
static void
StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
JSResolvingEntry *entry, uint32 generation)
{
JSDHashTable *table;
/*
* Clear flag from entry->flags and return early if other flags remain.
* We must take care to re-lookup entry if the table has changed since
* it was found by StartResolving.
*/
table = cx->resolvingTable;
if (table->generation != generation) {
entry = (JSResolvingEntry *)
JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);
}
entry->flags &= ~flag;
if (entry->flags)
return;
/*
* Do a raw remove only if fewer entries were removed than would cause
* alpha to be less than .5 (alpha is at most .75). Otherwise, we just
* call JS_DHashTableOperate to re-lookup the key and remove its entry,
* compressing or shrinking the table as needed.
*/
if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2)
JS_DHashTableRawRemove(table, &entry->hdr);
else
JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
}
#if JS_HAS_OBJ_WATCHPOINT
static JSBool
@ -1251,7 +1141,7 @@ obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
/* Avoid recursion on (obj, id) already being watched on cx. */
key.obj = obj;
key.id = id;
if (!StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
return JS_FALSE;
if (!entry)
return JS_TRUE;
@ -1262,7 +1152,7 @@ obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
argv[1] = old;
argv[2] = *nvp;
ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(funobj), 3, argv, nvp);
StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
return ok;
}
@ -1963,8 +1853,11 @@ js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
/* Store newslots after initializing all of 'em, just in case. */
obj->slots = newslots;
if (cx->runtime->objectHook)
if (cx->runtime->objectHook) {
JS_KEEP_ATOMS(cx->runtime);
cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData);
JS_UNKEEP_ATOMS(cx->runtime);
}
return obj;
@ -2495,7 +2388,7 @@ js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
* returning. But note that JS_DHASH_ADD may find an existing
* entry, in which case we bail to suppress runaway recursion.
*/
if (!StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) {
if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) {
JS_UNLOCK_OBJ(cx, obj);
return JS_FALSE;
}
@ -2590,7 +2483,7 @@ js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
}
cleanup:
StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
if (!ok || *propp)
return ok;
}

View File

@ -945,9 +945,11 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
op = JSOP_NOP;
/*
* Pending a better automatic GC root management scheme (see Mozilla bug
* 40757, http://bugzilla.mozilla.org/show_bug.cgi?id=40757), we need to
* atomize here to protect against a GC activation.
* Absent use of the new scoped local GC roots API around compiler calls,
* we need to atomize here to protect against a GC activation. Atoms are
* protected from GC during compilation by the JS_FRIEND_API entry points
* in this file. There doesn't seem to be any gain in switching from the
* atom-keeping method to the bulkier, slower scoped local roots method.
*/
pn->pn_funAtom = js_AtomizeObject(cx, fun->object, 0);
if (!pn->pn_funAtom)

View File

@ -1135,25 +1135,10 @@ js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
rt = cx->runtime;
hook = rt->newScriptHook;
if (hook) {
/*
* We use a dummy stack frame to protect the script from a GC caused
* by debugger-hook execution.
*
* XXX We really need a way to manage local roots and such more
* XXX automatically, at which point we can remove this one-off hack
* XXX and others within the engine. See bug 40757 for discussion.
*/
JSStackFrame dummy;
memset(&dummy, 0, sizeof dummy);
dummy.down = cx->fp;
dummy.script = script;
cx->fp = &dummy;
JS_KEEP_ATOMS(rt);
hook(cx, script->filename, script->lineno, script, fun,
rt->newScriptHookData);
cx->fp = dummy.down;
JS_UNKEEP_ATOMS(rt);
}
}