mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-04 13:07:52 +00:00
Bug 341821: Running close hooks of generator objects outside GC locks. r=brendan sr=mrbkap
This commit is contained in:
parent
a36f9ead4e
commit
765d801e0e
@ -1926,12 +1926,25 @@ JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg)
|
||||
JS_PUBLIC_API(void)
|
||||
JS_GC(JSContext *cx)
|
||||
{
|
||||
#if JS_HAS_GENERATORS
|
||||
/* Run previously scheduled but delayed close hooks. */
|
||||
js_RunCloseHooks(cx);
|
||||
#endif
|
||||
|
||||
/* Don't nuke active arenas if executing or compiling. */
|
||||
if (cx->stackPool.current == &cx->stackPool.first)
|
||||
JS_FinishArenaPool(&cx->stackPool);
|
||||
if (cx->tempPool.current == &cx->tempPool.first)
|
||||
JS_FinishArenaPool(&cx->tempPool);
|
||||
js_ForceGC(cx, 0);
|
||||
js_GC(cx, GC_NORMAL);
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
/*
|
||||
* Run close hooks for objects that became unreachable after the last GC.
|
||||
*/
|
||||
js_RunCloseHooks(cx);
|
||||
#endif
|
||||
JS_ArenaFinish();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
@ -1998,6 +2011,12 @@ JS_MaybeGC(JSContext *cx)
|
||||
rt->gcMallocBytes >= rt->gcMaxMallocBytes) {
|
||||
JS_GC(cx);
|
||||
}
|
||||
#if JS_HAS_GENERATORS
|
||||
else {
|
||||
/* Run scheduled but not yet executed close hooks. */
|
||||
js_RunCloseHooks(cx);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -417,7 +417,7 @@ js_FinishAtomState(JSAtomState *state)
|
||||
}
|
||||
|
||||
typedef struct MarkArgs {
|
||||
uintN gcflags;
|
||||
JSBool keepAtoms;
|
||||
JSGCThingMarker mark;
|
||||
void *data;
|
||||
} MarkArgs;
|
||||
@ -431,8 +431,7 @@ js_atom_marker(JSHashEntry *he, intN i, void *arg)
|
||||
|
||||
atom = (JSAtom *)he;
|
||||
args = (MarkArgs *)arg;
|
||||
if ((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) ||
|
||||
(args->gcflags & GC_KEEP_ATOMS)) {
|
||||
if ((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) || args->keepAtoms) {
|
||||
atom->flags |= ATOM_MARK;
|
||||
key = ATOM_KEY(atom);
|
||||
if (JSVAL_IS_GCTHING(key))
|
||||
@ -442,14 +441,14 @@ js_atom_marker(JSHashEntry *he, intN i, void *arg)
|
||||
}
|
||||
|
||||
void
|
||||
js_MarkAtomState(JSAtomState *state, uintN gcflags, JSGCThingMarker mark,
|
||||
js_MarkAtomState(JSAtomState *state, JSBool keepAtoms, JSGCThingMarker mark,
|
||||
void *data)
|
||||
{
|
||||
MarkArgs args;
|
||||
|
||||
if (!state->table)
|
||||
return;
|
||||
args.gcflags = gcflags;
|
||||
args.keepAtoms = keepAtoms;
|
||||
args.mark = mark;
|
||||
args.data = data;
|
||||
JS_HashTableEnumerateEntries(state->table, js_atom_marker, &args);
|
||||
|
@ -343,7 +343,7 @@ typedef void
|
||||
(*JSGCThingMarker)(void *thing, void *data);
|
||||
|
||||
extern void
|
||||
js_MarkAtomState(JSAtomState *state, uintN gcflags, JSGCThingMarker mark,
|
||||
js_MarkAtomState(JSAtomState *state, JSBool keepAtoms, JSGCThingMarker mark,
|
||||
void *data);
|
||||
|
||||
extern void
|
||||
|
@ -358,12 +358,7 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
||||
#endif
|
||||
|
||||
if (last) {
|
||||
/* Always force, so we wait for any racing GC to finish. */
|
||||
js_ForceGC(cx, GC_LAST_CONTEXT);
|
||||
|
||||
/* Iterate until no JSGC_END-status callback creates more garbage. */
|
||||
while (rt->gcPoke)
|
||||
js_GC(cx, GC_LAST_CONTEXT);
|
||||
js_GC(cx, GC_LAST_CONTEXT);
|
||||
|
||||
/* Try to free atom state, now that no unrooted scripts survive. */
|
||||
if (rt->atomState.liveAtoms == 0)
|
||||
@ -386,14 +381,18 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
||||
JS_UNLOCK_GC(rt);
|
||||
} else {
|
||||
if (mode == JSDCM_FORCE_GC)
|
||||
js_ForceGC(cx, 0);
|
||||
js_GC(cx, GC_NORMAL);
|
||||
else if (mode == JSDCM_MAYBE_GC)
|
||||
JS_MaybeGC(cx);
|
||||
}
|
||||
|
||||
if (last || mode == JSDCM_FORCE_GC)
|
||||
JS_ArenaFinish();
|
||||
|
||||
/* Free the stuff hanging off of cx. */
|
||||
JS_FinishArenaPool(&cx->stackPool);
|
||||
JS_FinishArenaPool(&cx->tempPool);
|
||||
|
||||
if (cx->lastMessage)
|
||||
free(cx->lastMessage);
|
||||
|
||||
|
@ -79,6 +79,11 @@ struct JSThread {
|
||||
* locks on each JS_malloc.
|
||||
*/
|
||||
uint32 gcMallocBytes;
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
/* Flag indicating that the current thread is excuting close hooks. */
|
||||
JSBool gcRunningCloseHooks;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern void JS_DLL_CALLBACK
|
||||
@ -146,8 +151,7 @@ struct JSRuntime {
|
||||
*/
|
||||
JSPackedBool gcPoke;
|
||||
JSPackedBool gcRunning;
|
||||
JSPackedBool gcClosePhase;
|
||||
uint8 gcPadding;
|
||||
uint16 gcPadding;
|
||||
|
||||
JSGCCallback gcCallback;
|
||||
uint32 gcMallocBytes;
|
||||
@ -169,18 +173,17 @@ struct JSRuntime {
|
||||
*/
|
||||
uint32 gcPrivateBytes;
|
||||
|
||||
/*
|
||||
* Table for tracking objects of extended classes that have non-null close
|
||||
* hooks, and need the GC to perform two-phase finalization.
|
||||
*/
|
||||
JSPtrTable gcCloseTable;
|
||||
|
||||
/*
|
||||
* Table for tracking iterators to ensure that we close iterator's state
|
||||
* before finalizing the iterable object.
|
||||
*/
|
||||
JSPtrTable gcIteratorTable;
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
/* Runtime state to support close hooks. */
|
||||
JSGCCloseState gcCloseState;
|
||||
#endif
|
||||
|
||||
#ifdef JS_GCMETER
|
||||
JSGCStats gcStats;
|
||||
#endif
|
||||
|
461
js/src/jsgc.c
461
js/src/jsgc.c
@ -61,6 +61,7 @@
|
||||
#include "jscntxt.h"
|
||||
#include "jsconfig.h"
|
||||
#include "jsdbgapi.h"
|
||||
#include "jsexn.h"
|
||||
#include "jsfun.h"
|
||||
#include "jsgc.h"
|
||||
#include "jsinterp.h"
|
||||
@ -248,16 +249,9 @@ typedef struct JSPtrTableInfo {
|
||||
uint16 linearGrowthThreshold;
|
||||
} JSPtrTableInfo;
|
||||
|
||||
#define GC_CLOSE_TABLE_MIN 4
|
||||
#define GC_CLOSE_TABLE_LINEAR 1024
|
||||
#define GC_ITERATOR_TABLE_MIN 4
|
||||
#define GC_ITERATOR_TABLE_LINEAR 1024
|
||||
|
||||
static const JSPtrTableInfo closeTableInfo = {
|
||||
GC_CLOSE_TABLE_MIN,
|
||||
GC_CLOSE_TABLE_LINEAR
|
||||
};
|
||||
|
||||
static const JSPtrTableInfo iteratorTableInfo = {
|
||||
GC_ITERATOR_TABLE_MIN,
|
||||
GC_ITERATOR_TABLE_LINEAR
|
||||
@ -486,9 +480,6 @@ FinishGCArenaLists(JSRuntime *rt)
|
||||
DestroyGCArena(rt, arenaList, &arenaList->last);
|
||||
arenaList->freeList = NULL;
|
||||
}
|
||||
|
||||
FreePtrTable(&rt->gcCloseTable, &closeTableInfo);
|
||||
FreePtrTable(&rt->gcIteratorTable, &iteratorTableInfo);
|
||||
}
|
||||
|
||||
uint8 *
|
||||
@ -620,6 +611,10 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes)
|
||||
* for default backward API compatibility.
|
||||
*/
|
||||
rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes;
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
rt->gcCloseState.todoTail = &rt->gcCloseState.todoHead;
|
||||
#endif
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
@ -699,6 +694,11 @@ js_DumpGCStats(JSRuntime *rt, FILE *fp)
|
||||
fprintf(fp, " thing arenas freed so far: %lu\n", ULSTAT(afree));
|
||||
fprintf(fp, " stack segments scanned: %lu\n", ULSTAT(stackseg));
|
||||
fprintf(fp, "stack segment slots scanned: %lu\n", ULSTAT(segslots));
|
||||
fprintf(fp, "reachable closeable objects: %lu\n", ULSTAT(nclose));
|
||||
fprintf(fp, " max reachable closeable: %lu\n", ULSTAT(maxnclose));
|
||||
fprintf(fp, " scheduled close hooks: %lu\n",
|
||||
UL(rt->gcCloseState.todoCount));
|
||||
fprintf(fp, " max scheduled close hooks: %lu\n", ULSTAT(maxcloselater));
|
||||
#undef UL
|
||||
#undef US
|
||||
|
||||
@ -733,6 +733,15 @@ js_FinishGC(JSRuntime *rt)
|
||||
#ifdef JS_GCMETER
|
||||
js_DumpGCStats(rt, stdout);
|
||||
#endif
|
||||
|
||||
FreePtrTable(&rt->gcIteratorTable, &iteratorTableInfo);
|
||||
#if JS_HAS_GENERATORS
|
||||
rt->gcCloseState.reachableList = NULL;
|
||||
METER(rt->gcStats.nclose = 0);
|
||||
rt->gcCloseState.todoHead = NULL;
|
||||
rt->gcCloseState.todoTail = &rt->gcCloseState.todoHead;
|
||||
rt->gcCloseState.todoCount = 0;
|
||||
#endif
|
||||
FinishGCArenaLists(rt);
|
||||
|
||||
if (rt->gcRootsHash.ops) {
|
||||
@ -846,7 +855,7 @@ js_RegisterCloseableIterator(JSContext *cx, JSObject *obj)
|
||||
JSBool ok;
|
||||
|
||||
rt = cx->runtime;
|
||||
JS_ASSERT(!rt->gcRunning || rt->gcClosePhase);
|
||||
JS_ASSERT(!rt->gcRunning);
|
||||
|
||||
JS_LOCK_GC(rt);
|
||||
ok = AddToPtrTable(cx, &rt->gcIteratorTable, &iteratorTableInfo, obj);
|
||||
@ -877,157 +886,207 @@ CloseIteratorStates(JSContext *cx)
|
||||
ShrinkPtrTable(&rt->gcIteratorTable, &iteratorTableInfo, newCount);
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_RegisterGeneratorObject(JSContext *cx, JSObject *obj)
|
||||
#if JS_HAS_GENERATORS
|
||||
|
||||
void
|
||||
js_RegisterGeneratorObject(JSContext *cx, JSGenerator *gen)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSBool ok;
|
||||
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_GeneratorClass);
|
||||
|
||||
/*
|
||||
* Return early without doing anything if shutting down, to prevent a bad
|
||||
* close hook from ilooping the GC. This could result in shutdown leaks,
|
||||
* so printf in DEBUG builds.
|
||||
*/
|
||||
rt = cx->runtime;
|
||||
JS_ASSERT(!rt->gcRunning || rt->gcClosePhase);
|
||||
if (rt->state == JSRTS_LANDING)
|
||||
return JS_TRUE;
|
||||
JS_ASSERT(!rt->gcRunning);
|
||||
JS_ASSERT(rt->state != JSRTS_LANDING);
|
||||
|
||||
JS_LOCK_GC(rt);
|
||||
ok = AddToPtrTable(cx, &rt->gcCloseTable, &closeTableInfo, obj);
|
||||
gen->next = rt->gcCloseState.reachableList;
|
||||
rt->gcCloseState.reachableList = gen;
|
||||
METER(rt->gcStats.nclose++);
|
||||
METER(rt->gcStats.maxnclose = JS_MAX(rt->gcStats.maxnclose,
|
||||
rt->gcStats.nclose));
|
||||
JS_UNLOCK_GC(rt);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void
|
||||
MarkCloseList(JSContext *cx, JSGenerator *gen)
|
||||
{
|
||||
for (; gen; gen = gen->next)
|
||||
GC_MARK(cx, gen->obj, "close list generator");
|
||||
}
|
||||
|
||||
/*
|
||||
* Struct to define object to close after the finalization phase of GC:
|
||||
* GC executes the close hooks for elements of JSRuntime.gcCloseTable.array
|
||||
* with indexes from the [startIndex, startIndex + count) range.
|
||||
* Find all unreachable generators and move them to the todo queue from
|
||||
* rt->gcCloseState.reachableList to execute thier close hooks after the GC
|
||||
* cycle completes. To ensure liveness during the sweep phase we mark all
|
||||
* generators we are going to close later.
|
||||
*/
|
||||
typedef struct JSObjectsToClose {
|
||||
size_t count;
|
||||
size_t startIndex;
|
||||
#ifdef DEBUG
|
||||
JSPtrTable tableSnapshot;
|
||||
#endif
|
||||
} JSObjectsToClose;
|
||||
|
||||
static void
|
||||
ScanDelayedChildren(JSContext *cx);
|
||||
|
||||
static void
|
||||
FindAndMarkObjectsToClose(JSContext *cx, JSObjectsToClose *toClose)
|
||||
FindAndMarkObjectsToClose(JSContext *cx, JSGCInvocationKind gckind)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
void **array;
|
||||
JSObject *obj;
|
||||
size_t count, index, pivot;
|
||||
JSGenerator *todo, **genp, *gen;
|
||||
|
||||
/*
|
||||
* Find unmarked objects reachable from rt->gcCloseTable and set them
|
||||
* aside at the end of the table. These are the objects to close.
|
||||
*/
|
||||
rt = cx->runtime;
|
||||
array = rt->gcCloseTable.array;
|
||||
count = pivot = rt->gcCloseTable.count;
|
||||
index = 0;
|
||||
while (index != pivot) {
|
||||
obj = (JSObject *)array[index];
|
||||
if (*js_GetGCThingFlags(obj) & GCF_MARK) {
|
||||
++index;
|
||||
todo = NULL;
|
||||
genp = &rt->gcCloseState.reachableList;
|
||||
while ((gen = *genp) != NULL) {
|
||||
if (*js_GetGCThingFlags(gen->obj) & GCF_MARK) {
|
||||
genp = &gen->next;
|
||||
} else {
|
||||
array[index] = array[--pivot];
|
||||
array[pivot] = obj;
|
||||
*genp = gen->next;
|
||||
gen->next = NULL;
|
||||
*rt->gcCloseState.todoTail = gen;
|
||||
rt->gcCloseState.todoTail = &gen->next;
|
||||
rt->gcCloseState.todoCount++;
|
||||
if (!todo)
|
||||
todo = gen;
|
||||
METER(JS_ASSERT(rt->gcStats.nclose));
|
||||
METER(rt->gcStats.nclose--);
|
||||
METER(rt->gcStats.maxcloselater
|
||||
= JS_MAX(rt->gcStats.maxcloselater,
|
||||
rt->gcCloseState.todoCount));
|
||||
}
|
||||
}
|
||||
|
||||
if (pivot == count) {
|
||||
/* Skip the close phase when threre are no objects to close. */
|
||||
toClose->count = 0;
|
||||
return;
|
||||
if (gckind == GC_LAST_CONTEXT) {
|
||||
/*
|
||||
* Remove scheduled hooks on shutdown as it is too late to run them:
|
||||
* we do not allow execution of arbitrary scripts at this point.
|
||||
*/
|
||||
if (rt->gcCloseState.todoCount != 0) {
|
||||
JS_ASSERT(rt->gcCloseState.todoHead);
|
||||
JS_ASSERT(rt->gcCloseState.todoTail != &rt->gcCloseState.todoHead);
|
||||
rt->gcCloseState.todoHead = NULL;
|
||||
rt->gcCloseState.todoTail = &rt->gcCloseState.todoHead;
|
||||
rt->gcCloseState.todoCount = 0;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Mark just found unreachable generators *after* we scan the global
|
||||
* list so a generator that refers to other unreachable generators
|
||||
* cannot keep them on gcCloseState.reachableList.
|
||||
*/
|
||||
MarkCloseList(cx, todo);
|
||||
}
|
||||
}
|
||||
|
||||
toClose->count = count - pivot;
|
||||
toClose->startIndex = pivot;
|
||||
#ifdef DEBUG
|
||||
toClose->tableSnapshot = rt->gcCloseTable;
|
||||
#ifdef JS_THREADSAFE
|
||||
# define GC_RUNNING_CLOSE_HOOKS_PTR(cx) \
|
||||
(&(cx)->thread->gcRunningCloseHooks)
|
||||
#else
|
||||
# define GC_RUNNING_CLOSE_HOOKS_PTR(cx) \
|
||||
(&(cx)->runtime->gcCloseState.runningCloseHook)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* First half of the close phase: loop over the objects that we set aside
|
||||
* in the close table and mark them to protect them against finalization
|
||||
* during sweeping phase.
|
||||
*/
|
||||
index = pivot;
|
||||
do {
|
||||
GC_MARK(cx, array[index], "close-phase object");
|
||||
} while (++index != count);
|
||||
|
||||
/*
|
||||
* Mark children of things that caused too deep recursion during the
|
||||
* just-completed marking half of the close phase.
|
||||
*/
|
||||
ScanDelayedChildren(cx);
|
||||
}
|
||||
|
||||
static void
|
||||
ExecuteCloseHooks(JSContext *cx, const JSObjectsToClose *toClose)
|
||||
JSBool
|
||||
js_RunCloseHooks(JSContext *cx)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSTempValueRooter tvr;
|
||||
JSStackFrame *fp;
|
||||
uint32 index, endIndex;
|
||||
JSObject *obj;
|
||||
void **array;
|
||||
size_t todoLimit = 0; /* initialized to quell GCC warnings */
|
||||
JSGenerator *gen;
|
||||
JSBool ok;
|
||||
|
||||
rt = cx->runtime;
|
||||
JS_ASSERT(toClose->count > 0);
|
||||
|
||||
/* Close table manupulations are not allowed during the marking phase. */
|
||||
JS_ASSERT(memcmp(&toClose->tableSnapshot, &rt->gcCloseTable,
|
||||
sizeof toClose->tableSnapshot) == 0);
|
||||
|
||||
/*
|
||||
* Execute the close hooks. Temporarily set aside cx->fp here to prevent
|
||||
* the close hooks from running on the GC's interpreter stack.
|
||||
* It is OK to access todoCount outside the lock here. When many threads
|
||||
* update the todo list, accessing some older value of todoCount in the
|
||||
* worst case just delays the excution of close hooks.
|
||||
*/
|
||||
if (rt->gcCloseState.todoCount == 0)
|
||||
return JS_TRUE;
|
||||
|
||||
/*
|
||||
* To prevent an infinite loop when a close hook creats more objects with
|
||||
* close hooks and then triggers GC we ignore recursive invocations of
|
||||
* js_RunCloseHooks and limit number of hooks to execute to the initial
|
||||
* size of the list.
|
||||
*/
|
||||
if (*GC_RUNNING_CLOSE_HOOKS_PTR(cx))
|
||||
return JS_TRUE;
|
||||
|
||||
*GC_RUNNING_CLOSE_HOOKS_PTR(cx) = JS_TRUE;
|
||||
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
|
||||
|
||||
/*
|
||||
* Set aside cx->fp since we do not want a close hook using caller or
|
||||
* other means to backtrace into whatever stack might be active when
|
||||
* running the hook. We store the current frame on the dormant list to
|
||||
* protect against GC that the hook can trigger.
|
||||
*/
|
||||
rt->gcClosePhase = JS_TRUE;
|
||||
fp = cx->fp;
|
||||
if (fp) {
|
||||
JS_ASSERT(!fp->dormantNext);
|
||||
fp->dormantNext = cx->dormantFrameChain;
|
||||
cx->dormantFrameChain = fp;
|
||||
}
|
||||
cx->fp = NULL;
|
||||
|
||||
index = toClose->startIndex;
|
||||
endIndex = index + toClose->count;
|
||||
do {
|
||||
/*
|
||||
* Reload rt->gcCloseTable.array because close hooks may create
|
||||
* new objects that have close hooks and reallocate the table.
|
||||
*/
|
||||
obj = (JSObject *)rt->gcCloseTable.array[index];
|
||||
gen = NULL;
|
||||
ok = JS_TRUE;
|
||||
for (;;) {
|
||||
JS_LOCK_GC(rt);
|
||||
JS_ASSERT(rt->gcCloseState.todoCount > 0 || !rt->gcCloseState.todoHead);
|
||||
JS_ASSERT(rt->gcCloseState.todoCount == 0 || rt->gcCloseState.todoHead);
|
||||
if (!gen) {
|
||||
/* First iteration, init the limit from inside the lock. */
|
||||
todoLimit = rt->gcCloseState.todoCount;
|
||||
}
|
||||
if (todoLimit == 0) {
|
||||
gen = NULL;
|
||||
} else if ((gen = rt->gcCloseState.todoHead) != NULL) {
|
||||
JS_ASSERT(gen->obj);
|
||||
|
||||
/*
|
||||
* Ignore errors until after we call the close method, then force
|
||||
* prompt error reporting, since GC is infallible.
|
||||
*/
|
||||
js_CloseGeneratorObject(cx, obj);
|
||||
if (cx->throwing && !js_ReportUncaughtException(cx))
|
||||
JS_ClearPendingException(cx);
|
||||
} while (++index != endIndex);
|
||||
/* Root obj before unlinking the gen and executing the hook. */
|
||||
tvr.u.value = OBJECT_TO_JSVAL(gen->obj);
|
||||
if (!gen->next)
|
||||
rt->gcCloseState.todoTail = &rt->gcCloseState.todoHead;
|
||||
rt->gcCloseState.todoHead = gen->next;
|
||||
rt->gcCloseState.todoCount--;
|
||||
todoLimit--;
|
||||
rt->gcPoke = JS_TRUE;
|
||||
#ifdef DEBUG
|
||||
gen->next = NULL;
|
||||
#endif
|
||||
}
|
||||
JS_UNLOCK_GC(rt);
|
||||
if (!gen)
|
||||
break;
|
||||
|
||||
ok = js_CloseGeneratorObject(cx, gen);
|
||||
if (cx->throwing) {
|
||||
/*
|
||||
* Report the exception thrown by the close hook and continue to
|
||||
* execute the rest of the hooks.
|
||||
*/
|
||||
if (!js_ReportUncaughtException(cx))
|
||||
JS_ClearPendingException(cx);
|
||||
ok = JS_TRUE;
|
||||
} else if (!ok) {
|
||||
/*
|
||||
* Assume this is a stop signal from the branch callback or other
|
||||
* quit ASAP condition. Break execution until the next invocation
|
||||
* of js_RunCloseHooks.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rt->gcClosePhase = JS_FALSE;
|
||||
cx->fp = fp;
|
||||
if (fp) {
|
||||
JS_ASSERT(cx->dormantFrameChain == fp);
|
||||
cx->dormantFrameChain = fp->dormantNext;
|
||||
fp->dormantNext = NULL;
|
||||
}
|
||||
JS_POP_TEMP_ROOT(cx, &tvr);
|
||||
*GC_RUNNING_CLOSE_HOOKS_PTR(cx) = JS_FALSE;
|
||||
|
||||
/*
|
||||
* Move any added object pointers down over the span just
|
||||
* processed, and update the table's count.
|
||||
*/
|
||||
array = rt->gcCloseTable.array;
|
||||
memmove(array + toClose->startIndex, array + endIndex,
|
||||
(rt->gcCloseTable.count - endIndex) * sizeof array[0]);
|
||||
ShrinkPtrTable(&rt->gcCloseTable, &closeTableInfo,
|
||||
rt->gcCloseTable.count - toClose->count);
|
||||
return ok;
|
||||
}
|
||||
|
||||
#endif /* JS_HAS_GENERATORS */
|
||||
|
||||
#if defined(DEBUG_brendan) || defined(DEBUG_timeless)
|
||||
#define DEBUG_gchist
|
||||
#endif
|
||||
@ -1096,8 +1155,8 @@ js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes)
|
||||
rt->gcMallocBytes += localMallocBytes;
|
||||
}
|
||||
#endif
|
||||
JS_ASSERT(!rt->gcRunning || rt->gcClosePhase);
|
||||
if (rt->gcRunning && !rt->gcClosePhase) {
|
||||
JS_ASSERT(!rt->gcRunning);
|
||||
if (rt->gcRunning) {
|
||||
METER(rt->gcStats.finalfail++);
|
||||
JS_UNLOCK_GC(rt);
|
||||
return NULL;
|
||||
@ -1114,7 +1173,7 @@ js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes)
|
||||
|
||||
arenaList = &rt->gcArenaList[flindex];
|
||||
for (;;) {
|
||||
if (doGC && !rt->gcClosePhase) {
|
||||
if (doGC) {
|
||||
/*
|
||||
* Keep rt->gcLock across the call into js_GC so we don't starve
|
||||
* and lose to racing threads who deplete the heap just after
|
||||
@ -1123,7 +1182,7 @@ js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes)
|
||||
* can happen on certain operating systems. For the gory details,
|
||||
* see bug 162779 at https://bugzilla.mozilla.org/.
|
||||
*/
|
||||
if (!js_GC(cx, GC_KEEP_ATOMS | GC_LAST_DITCH)) {
|
||||
if (!js_GC(cx, GC_LAST_DITCH)) {
|
||||
/*
|
||||
* js_GC ensures that GC is unlocked when the branch callback
|
||||
* wants to stop execution, and in this case we do not report
|
||||
@ -2235,20 +2294,6 @@ gc_lock_marker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num, void *arg)
|
||||
return JS_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
js_ForceGC(JSContext *cx, uintN gcflags)
|
||||
{
|
||||
uintN i;
|
||||
|
||||
JS_ASSERT((gcflags & ~(GC_KEEP_ATOMS | GC_LAST_CONTEXT)) == 0);
|
||||
for (i = 0; i < GCX_NTYPES; i++)
|
||||
cx->newborn[i] = NULL;
|
||||
cx->lastAtom = NULL;
|
||||
cx->runtime->gcPoke = JS_TRUE;
|
||||
js_GC(cx, gcflags);
|
||||
JS_ArenaFinish();
|
||||
}
|
||||
|
||||
#define GC_MARK_JSVALS(cx, len, vec, name) \
|
||||
JS_BEGIN_MACRO \
|
||||
jsval _v, *_vp, *_end; \
|
||||
@ -2345,30 +2390,30 @@ js_MarkStackFrame(JSContext *cx, JSStackFrame *fp)
|
||||
* Return false when the branch callback wants to stop exeution ASAP and true
|
||||
* otherwise.
|
||||
*
|
||||
* When gcflags contains GC_LAST_DITCH, it indicates a call from js_NewGCThing
|
||||
* with rt->gcLock already held. On return to js_NewGCThing the lock is kept
|
||||
* when js_GC returns false and released when it returns true. This asymmetry
|
||||
* helps avoid re-taking the lock just to release it immediately in
|
||||
* js_NewGCThing when the branch callback called outside the lock cancels the
|
||||
* allocation and js_GC returns false. See bug 341896.
|
||||
* When gckind is GC_LAST_DITCH, it indicates a call from js_NewGCThing with
|
||||
* rt->gcLock already held. On return to js_NewGCThing the lock is kept when
|
||||
* js_GC returns false and released when it returns true. This asymmetry helps
|
||||
* avoid re-taking the lock just to release it immediately in js_NewGCThing
|
||||
* when the branch callback called outside the lock cancels the allocation and
|
||||
* js_GC returns false. See bug 341896.
|
||||
*/
|
||||
JSBool
|
||||
js_GC(JSContext *cx, uintN gcflags)
|
||||
js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSBool keepAtoms;
|
||||
uintN i, type;
|
||||
JSContext *iter, *acx;
|
||||
JSStackFrame *fp, *chain;
|
||||
uintN i, type;
|
||||
JSStackHeader *sh;
|
||||
JSTempValueRooter *tvr;
|
||||
JSObjectsToClose objectsToClose;
|
||||
size_t nbytes, limit, offset;
|
||||
JSGCArena *a, **ap;
|
||||
uint8 flags, *flagp, *firstPage;
|
||||
JSGCThing *thing, *freeList;
|
||||
JSGCArenaList *arenaList;
|
||||
GCFinalizeOp finalizer;
|
||||
JSBool allClear, shouldRestart, checkBranchCallback;
|
||||
JSBool allClear, checkBranchCallback;
|
||||
#ifdef JS_THREADSAFE
|
||||
uint32 requestDebit;
|
||||
#endif
|
||||
@ -2379,33 +2424,47 @@ js_GC(JSContext *cx, uintN gcflags)
|
||||
JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt));
|
||||
#endif
|
||||
|
||||
if (gckind == GC_LAST_DITCH) {
|
||||
/* The last ditch GC preserves all atoms and cx->newborn. */
|
||||
keepAtoms = JS_TRUE;
|
||||
} else {
|
||||
for (i = 0; i < GCX_NTYPES; i++)
|
||||
cx->newborn[i] = NULL;
|
||||
cx->lastAtom = NULL;
|
||||
rt->gcPoke = JS_TRUE;
|
||||
|
||||
/* Keep atoms when a suspended compile is running on another context. */
|
||||
keepAtoms = (rt->gcKeepAtoms != 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't collect garbage if the runtime isn't up, and cx is not the last
|
||||
* context in the runtime. The last context must force a GC, and nothing
|
||||
* should suppress that final collection or there may be shutdown leaks,
|
||||
* or runtime bloat until the next context is created.
|
||||
*/
|
||||
if (rt->state != JSRTS_UP && !(gcflags & GC_LAST_CONTEXT))
|
||||
if (rt->state != JSRTS_UP && gckind != GC_LAST_CONTEXT)
|
||||
return JS_TRUE;
|
||||
|
||||
restart_after_callback:
|
||||
/*
|
||||
* Let the API user decide to defer a GC if it wants to (unless this
|
||||
* is the last context). Invoke the callback regardless.
|
||||
*/
|
||||
if (rt->gcCallback &&
|
||||
!rt->gcCallback(cx, JSGC_BEGIN) &&
|
||||
!(gcflags & GC_LAST_CONTEXT)) {
|
||||
gckind != GC_LAST_CONTEXT) {
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/* Lock out other GC allocator and collector invocations. */
|
||||
if (!(gcflags & GC_LAST_DITCH))
|
||||
if (gckind != GC_LAST_DITCH)
|
||||
JS_LOCK_GC(rt);
|
||||
|
||||
/* Do nothing if no mutator has executed since the last GC. */
|
||||
if (!rt->gcPoke) {
|
||||
METER(rt->gcStats.nopoke++);
|
||||
if (!(gcflags & GC_LAST_DITCH))
|
||||
if (gckind != GC_LAST_DITCH)
|
||||
JS_UNLOCK_GC(rt);
|
||||
return JS_TRUE;
|
||||
}
|
||||
@ -2419,7 +2478,7 @@ js_GC(JSContext *cx, uintN gcflags)
|
||||
rt->gcLevel++;
|
||||
METER(if (rt->gcLevel > rt->gcStats.maxlevel)
|
||||
rt->gcStats.maxlevel = rt->gcLevel);
|
||||
if (!(gcflags & GC_LAST_DITCH))
|
||||
if (gckind != GC_LAST_DITCH)
|
||||
JS_UNLOCK_GC(rt);
|
||||
return JS_TRUE;
|
||||
}
|
||||
@ -2476,7 +2535,7 @@ js_GC(JSContext *cx, uintN gcflags)
|
||||
JS_AWAIT_GC_DONE(rt);
|
||||
if (requestDebit)
|
||||
rt->requestCount += requestDebit;
|
||||
if (!(gcflags & GC_LAST_DITCH))
|
||||
if (gckind != GC_LAST_DITCH)
|
||||
JS_UNLOCK_GC(rt);
|
||||
return JS_TRUE;
|
||||
}
|
||||
@ -2512,10 +2571,6 @@ js_GC(JSContext *cx, uintN gcflags)
|
||||
rt->gcRunning = JS_TRUE;
|
||||
JS_UNLOCK_GC(rt);
|
||||
|
||||
/* If a suspended compile is running on another context, keep atoms. */
|
||||
if (rt->gcKeepAtoms)
|
||||
gcflags |= GC_KEEP_ATOMS;
|
||||
|
||||
/* Reset malloc counter. */
|
||||
rt->gcMallocBytes = 0;
|
||||
|
||||
@ -2554,11 +2609,16 @@ restart:
|
||||
JS_DHashTableEnumerate(&rt->gcRootsHash, gc_root_marker, cx);
|
||||
if (rt->gcLocksHash)
|
||||
JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_marker, cx);
|
||||
js_MarkAtomState(&rt->atomState, gcflags, gc_mark_atom_key_thing, cx);
|
||||
js_MarkAtomState(&rt->atomState, keepAtoms, gc_mark_atom_key_thing, cx);
|
||||
js_MarkWatchPoints(rt);
|
||||
js_MarkScriptFilenames(rt, gcflags);
|
||||
js_MarkScriptFilenames(rt, keepAtoms);
|
||||
js_MarkNativeIteratorStates(cx);
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
/* Mark generators whose close hooks were already scheduled to run. */
|
||||
MarkCloseList(cx, rt->gcCloseState.todoHead);
|
||||
#endif
|
||||
|
||||
iter = NULL;
|
||||
while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) {
|
||||
/*
|
||||
@ -2639,18 +2699,19 @@ restart:
|
||||
*/
|
||||
ScanDelayedChildren(cx);
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
/*
|
||||
* Close phase: search and mark part.
|
||||
*
|
||||
* We find all unreachable objects with close hooks and move them to a
|
||||
* private work-list to execute the close hooks after the sweep phase.
|
||||
* To ensure liveness during the sweep phase we mark all objects we are
|
||||
* about to close.
|
||||
* Close phase: search and mark part. See comments in
|
||||
* FindAndMarkObjectsToClose for details.
|
||||
*/
|
||||
#ifdef __GNUC__ /* suppress a bogus gcc warning */
|
||||
objectsToClose.startIndex = 0;
|
||||
FindAndMarkObjectsToClose(cx, gckind);
|
||||
|
||||
/*
|
||||
* Mark children of things that caused too deep recursion during the
|
||||
* just-completed marking part of the close phase.
|
||||
*/
|
||||
ScanDelayedChildren(cx);
|
||||
#endif
|
||||
FindAndMarkObjectsToClose(cx, &objectsToClose);
|
||||
|
||||
JS_ASSERT(!cx->insideGCMarkCallback);
|
||||
if (rt->gcCallback) {
|
||||
@ -2797,36 +2858,13 @@ restart:
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We want to restart GC if any of the finalizers called js_RemoveRoot or
|
||||
* js_UnlockGCThingRT.
|
||||
*/
|
||||
shouldRestart = rt->gcPoke;
|
||||
|
||||
/*
|
||||
* Close phase: execution part.
|
||||
*
|
||||
* The GC allocator works when called during execution of a close hook
|
||||
* but if it exhausts the malloc heap, it will fail without trying a
|
||||
* last-ditch GC.
|
||||
*/
|
||||
if (objectsToClose.count != 0) {
|
||||
ExecuteCloseHooks(cx, &objectsToClose);
|
||||
|
||||
/*
|
||||
* On the last destroy context restart GC to collect just closed
|
||||
* objects. This does not cause infinite loops with close hooks
|
||||
* creating more closeable objects since we do not allow installing
|
||||
* close hooks during the shutdown of runtime.
|
||||
*
|
||||
* See bug 340889 and bug 341675.
|
||||
*/
|
||||
if (gcflags & GC_LAST_CONTEXT)
|
||||
shouldRestart = JS_TRUE;
|
||||
}
|
||||
|
||||
JS_LOCK_GC(rt);
|
||||
if (rt->gcLevel > 1 || shouldRestart) {
|
||||
|
||||
/*
|
||||
* We want to restart GC if js_GC was called recursively or if any of the
|
||||
* finalizers called js_RemoveRoot or js_UnlockGCThingRT.
|
||||
*/
|
||||
if (rt->gcLevel > 1 || rt->gcPoke) {
|
||||
rt->gcLevel = 1;
|
||||
rt->gcPoke = JS_FALSE;
|
||||
JS_UNLOCK_GC(rt);
|
||||
@ -2848,21 +2886,28 @@ restart:
|
||||
* Unlock unless we have GC_LAST_DITCH which requires locked GC on return
|
||||
* after a successful GC cycle.
|
||||
*/
|
||||
if (!(gcflags & GC_LAST_DITCH))
|
||||
if (gckind != GC_LAST_DITCH)
|
||||
JS_UNLOCK_GC(rt);
|
||||
#endif
|
||||
|
||||
checkBranchCallback = (gcflags & GC_LAST_DITCH) &&
|
||||
JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) &&
|
||||
cx->branchCallback;
|
||||
checkBranchCallback = (gckind == GC_LAST_DITCH &&
|
||||
JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) &&
|
||||
cx->branchCallback);
|
||||
|
||||
/* Execute JSGC_END callback and branch callback outside the lock. */
|
||||
if (rt->gcCallback || checkBranchCallback) {
|
||||
|
||||
/* Execute JSGC_END and branch callbacks outside the lock. */
|
||||
if (gcflags & GC_LAST_DITCH)
|
||||
if (gckind == GC_LAST_DITCH)
|
||||
JS_UNLOCK_GC(rt);
|
||||
if (rt->gcCallback)
|
||||
if (rt->gcCallback) {
|
||||
(void) rt->gcCallback(cx, JSGC_END);
|
||||
|
||||
/*
|
||||
* On shutdown iterate until no JSGC_END-status callback creates
|
||||
* more garbage.
|
||||
*/
|
||||
if (gckind == GC_LAST_CONTEXT && rt->gcPoke)
|
||||
goto restart_after_callback;
|
||||
}
|
||||
if (checkBranchCallback &&
|
||||
cx->branchCallback && /* gcCallback can reset the branchCallback */
|
||||
!cx->branchCallback(cx, NULL)) {
|
||||
@ -2871,7 +2916,7 @@ restart:
|
||||
/* Keep GC unlocked when canceled. */
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (gcflags & GC_LAST_DITCH)
|
||||
if (gckind == GC_LAST_DITCH)
|
||||
JS_LOCK_GC(rt);
|
||||
}
|
||||
|
||||
|
@ -140,8 +140,42 @@ typedef struct JSPtrTable {
|
||||
extern JSBool
|
||||
js_RegisterCloseableIterator(JSContext *cx, JSObject *obj);
|
||||
|
||||
extern JSBool
|
||||
js_RegisterGeneratorObject(JSContext *cx, JSObject *obj);
|
||||
#if JS_HAS_GENERATORS
|
||||
|
||||
/*
|
||||
* Runtime state to support generators' close hooks.
|
||||
*/
|
||||
typedef struct JSGCCloseState {
|
||||
/*
|
||||
* Singly linked list of generators that are reachable from GC roots or
|
||||
* were created after the last GC.
|
||||
*/
|
||||
JSGenerator *reachableList;
|
||||
|
||||
/*
|
||||
* Head, pointer to tail and length of the queue of generators that have
|
||||
* already become unreachable but whose close hooks are not yet run.
|
||||
*/
|
||||
JSGenerator *todoHead;
|
||||
JSGenerator **todoTail;
|
||||
size_t todoCount;
|
||||
|
||||
#ifndef JS_THREADSAFE
|
||||
/*
|
||||
* Flag indicating that the current thread is excuting a close hook for
|
||||
* single thread case.
|
||||
*/
|
||||
JSBool runningCloseHook;
|
||||
#endif
|
||||
} JSGCCloseState;
|
||||
|
||||
extern void
|
||||
js_RegisterGeneratorObject(JSContext *cx, JSGenerator *gen);
|
||||
|
||||
JSBool
|
||||
js_RunCloseHooks(JSContext *cx);
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The private JSGCThing struct, which describes a gcFreeList element.
|
||||
@ -206,29 +240,30 @@ extern void
|
||||
js_MarkStackFrame(JSContext *cx, JSStackFrame *fp);
|
||||
|
||||
/*
|
||||
* Flags to modify how a GC marks and sweeps:
|
||||
* GC_KEEP_ATOMS Don't sweep unmarked atoms, they may be in use by the
|
||||
* compiler, or by an API function that calls js_Atomize,
|
||||
* when the GC is called from js_NewGCThing, due to a
|
||||
* malloc failure or the runtime GC-thing limit.
|
||||
* GC_LAST_CONTEXT Called from js_DestroyContext for last JSContext in a
|
||||
* JSRuntime, when it is imperative that rt->gcPoke gets
|
||||
* cleared early in js_GC, if it is set.
|
||||
* GC_LAST_DITCH Called from js_NewGCThing as a last-ditch GC attempt.
|
||||
* See comments before js_GC definition for details.
|
||||
* Kinds of js_GC invocation.
|
||||
*/
|
||||
#define GC_KEEP_ATOMS 0x1
|
||||
#define GC_LAST_CONTEXT 0x2
|
||||
#define GC_LAST_DITCH 0x4
|
||||
typedef enum JSGCInvocationKind {
|
||||
/* Normal invocation. */
|
||||
GC_NORMAL,
|
||||
|
||||
extern void
|
||||
js_ForceGC(JSContext *cx, uintN gcflags);
|
||||
/*
|
||||
* Called from js_DestroyContext for last JSContext in a JSRuntime, when
|
||||
* it is imperative that rt->gcPoke gets cleared early in js_GC.
|
||||
*/
|
||||
GC_LAST_CONTEXT,
|
||||
|
||||
/*
|
||||
* Called from js_NewGCThing as a last-ditch GC attempt. See comments
|
||||
* before js_GC definition for details.
|
||||
*/
|
||||
GC_LAST_DITCH
|
||||
} JSGCInvocationKind;
|
||||
|
||||
/*
|
||||
* Return false when the branch callback cancels GC and true otherwise.
|
||||
*/
|
||||
extern JSBool
|
||||
js_GC(JSContext *cx, uintN gcflags);
|
||||
js_GC(JSContext *cx, JSGCInvocationKind gckind);
|
||||
|
||||
/* Call this after succesful malloc of memory for GC-related things. */
|
||||
extern void
|
||||
@ -267,6 +302,9 @@ typedef struct JSGCStats {
|
||||
uint32 afree; /* thing arenas freed so far */
|
||||
uint32 stackseg; /* total extraordinary stack segments scanned */
|
||||
uint32 segslots; /* total stack segment jsval slots scanned */
|
||||
uint32 nclose; /* number of objects with close hooks */
|
||||
uint32 maxnclose; /* max number of objects with close hooks */
|
||||
uint32 maxcloselater; /* max number of close hooks scheduled to run */
|
||||
} JSGCStats;
|
||||
|
||||
extern JS_FRIEND_API(void)
|
||||
|
@ -594,28 +594,23 @@ js_ThrowStopIteration(JSContext *cx, JSObject *obj)
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
|
||||
typedef enum JSGeneratorState {
|
||||
JSGEN_NEWBORN,
|
||||
JSGEN_RUNNING,
|
||||
JSGEN_CLOSED
|
||||
} JSGeneratorState;
|
||||
|
||||
typedef struct JSGenerator {
|
||||
JSGeneratorState state;
|
||||
JSStackFrame frame;
|
||||
JSArena arena;
|
||||
jsval stack[1];
|
||||
} JSGenerator;
|
||||
|
||||
/*
|
||||
* Execute generator's close hook after GC detects that the object has become
|
||||
* unreachable.
|
||||
*/
|
||||
JSBool
|
||||
js_CloseGeneratorObject(JSContext *cx, JSObject *obj)
|
||||
js_CloseGeneratorObject(JSContext *cx, JSGenerator *gen)
|
||||
{
|
||||
JSObject *obj;
|
||||
jsval fval, rval;
|
||||
const jsid id = ATOM_TO_JSID(cx->runtime->atomState.closeAtom);
|
||||
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_GeneratorClass);
|
||||
JS_ASSERT(JS_GetPrivate(cx, obj));
|
||||
/* JSGenerator.closeLink must be already unlinked from all lists. */
|
||||
JS_ASSERT(!gen->next);
|
||||
JS_ASSERT(gen != cx->runtime->gcCloseState.reachableList);
|
||||
JS_ASSERT(gen != cx->runtime->gcCloseState.todoHead);
|
||||
|
||||
obj = gen->obj;
|
||||
if (!JS_GetMethodById(cx, obj, id, &obj, &fval))
|
||||
return JS_FALSE;
|
||||
|
||||
@ -687,6 +682,8 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp)
|
||||
if (!gen)
|
||||
goto bad;
|
||||
|
||||
gen->obj = obj;
|
||||
|
||||
/* Copy call-invariant object and function references. */
|
||||
gen->frame.callobj = fp->callobj;
|
||||
gen->frame.argsobj = fp->argsobj;
|
||||
@ -743,13 +740,9 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp)
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!js_RegisterGeneratorObject(cx, obj)) {
|
||||
/*
|
||||
* Do not free gen here, as the finalizer will do that since we
|
||||
* called JS_SetPrivate.
|
||||
*/
|
||||
goto bad;
|
||||
}
|
||||
/* Register after we have properly initialized the private slot. */
|
||||
js_RegisterGeneratorObject(cx, gen);
|
||||
|
||||
return obj;
|
||||
|
||||
bad:
|
||||
|
@ -92,11 +92,30 @@ js_CallIteratorNext(JSContext *cx, JSObject *iterobj, uintN flags,
|
||||
extern JSBool
|
||||
js_ThrowStopIteration(JSContext *cx, JSObject *obj);
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
|
||||
typedef enum JSGeneratorState {
|
||||
JSGEN_NEWBORN,
|
||||
JSGEN_RUNNING,
|
||||
JSGEN_CLOSED
|
||||
} JSGeneratorState;
|
||||
|
||||
struct JSGenerator {
|
||||
JSGenerator *next;
|
||||
JSObject *obj;
|
||||
JSGeneratorState state;
|
||||
JSStackFrame frame;
|
||||
JSArena arena;
|
||||
jsval stack[1];
|
||||
};
|
||||
|
||||
extern JSObject *
|
||||
js_NewGenerator(JSContext *cx, JSStackFrame *fp);
|
||||
|
||||
extern JSBool
|
||||
js_CloseGeneratorObject(JSContext *cx, JSObject *obj);
|
||||
js_CloseGeneratorObject(JSContext *cx, JSGenerator *gen);
|
||||
|
||||
#endif
|
||||
|
||||
extern JSClass js_GeneratorClass;
|
||||
extern JSClass js_IteratorClass;
|
||||
|
@ -94,6 +94,7 @@ typedef struct JSDependentString JSDependentString;
|
||||
typedef struct JSGCLockHashEntry JSGCLockHashEntry;
|
||||
typedef struct JSGCRootHashEntry JSGCRootHashEntry;
|
||||
typedef struct JSGCThing JSGCThing;
|
||||
typedef struct JSGenerator JSGenerator;
|
||||
typedef struct JSParseNode JSParseNode;
|
||||
typedef struct JSSharpObjectMap JSSharpObjectMap;
|
||||
typedef struct JSThread JSThread;
|
||||
|
@ -1212,7 +1212,7 @@ js_script_filename_marker(JSHashEntry *he, intN i, void *arg)
|
||||
}
|
||||
|
||||
void
|
||||
js_MarkScriptFilenames(JSRuntime *rt, uintN gcflags)
|
||||
js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms)
|
||||
{
|
||||
JSCList *head, *link;
|
||||
ScriptFilenamePrefix *sfp;
|
||||
@ -1220,7 +1220,7 @@ js_MarkScriptFilenames(JSRuntime *rt, uintN gcflags)
|
||||
if (!rt->scriptFilenameTable)
|
||||
return;
|
||||
|
||||
if (gcflags & GC_KEEP_ATOMS) {
|
||||
if (keepAtoms) {
|
||||
JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
|
||||
js_script_filename_marker,
|
||||
rt);
|
||||
|
@ -139,7 +139,7 @@ extern void
|
||||
js_MarkScriptFilename(const char *filename);
|
||||
|
||||
extern void
|
||||
js_MarkScriptFilenames(JSRuntime *rt, uintN gcflags);
|
||||
js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms);
|
||||
|
||||
extern void
|
||||
js_SweepScriptFilenames(JSRuntime *rt);
|
||||
|
Loading…
x
Reference in New Issue
Block a user