if (gcflags & GC_KEEP_ATOMS) js_MarkScriptFilenames(rt);
js_MarkScriptFilenames(rt, gcflags);
js_GC(JSContext *cx, uintN gcflags){    JSRuntime *rt;    JSContext *iter, *acx;    JSStackFrame *fp, *chain;    uintN i, depth, nslots, type;    JSStackHeader *sh;    size_t nbytes, nflags;    JSArena *a, **ap;    uint8 flags, *flagp, *split;    JSGCThing *thing, *limit, **flp, **oflp;    GCFinalizeOp finalizer;    uint32 *bytesptr;    JSBool all_clear;#ifdef JS_THREADSAFE    jsword currentThread;    uint32 requestDebit;#endif    rt = cx->runtime;#ifdef JS_THREADSAFE    /* Avoid deadlock. */    JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt));#endif    /*     * 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))        return;    /*     * 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) {        if (!rt->gcCallback(cx, JSGC_BEGIN) && !(gcflags & GC_LAST_CONTEXT))            return;    }    /* Lock out other GC allocator and collector invocations. */    if (!(gcflags & GC_ALREADY_LOCKED))        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_ALREADY_LOCKED))            JS_UNLOCK_GC(rt);        return;    }    METER(rt->gcStats.poke++);#ifdef JS_THREADSAFE    /* Bump gcLevel and return rather than nest on this thread. */    currentThread = js_CurrentThreadId();    if (rt->gcThread == currentThread) {        JS_ASSERT(rt->gcLevel > 0);        rt->gcLevel++;        METER(if (rt->gcLevel > rt->gcStats.maxlevel)                  rt->gcStats.maxlevel = rt->gcLevel);        if (!(gcflags & GC_ALREADY_LOCKED))            JS_UNLOCK_GC(rt);        return;    }    /*     * If we're in one or more requests (possibly on more than one context)     * running on the current thread, indicate, temporarily, that all these     * requests are inactive.  NB: if cx->thread is 0, then cx is not using     * the request model, and does not contribute to rt->requestCount.     */    requestDebit = 0;    if (cx->thread) {        /*         * Check all contexts for any with the same thread-id.  XXX should we         * keep a sub-list of contexts having the same id?         */        iter = NULL;        while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) {            if (acx->thread == cx->thread && acx->requestDepth)                requestDebit++;        }    } else {        /*         * We assert, but check anyway, in case someone is misusing the API.         * Avoiding the loop over all of rt's contexts is a win in the event         * that the GC runs only on request-less contexts with 0 thread-ids,         * in a special thread such as might be used by the UI/DOM/Layout         * "mozilla" or "main" thread in Mozilla-the-browser.         */        JS_ASSERT(cx->requestDepth == 0);        if (cx->requestDepth)            requestDebit = 1;    }    if (requestDebit) {        JS_ASSERT(requestDebit <= rt->requestCount);        rt->requestCount -= requestDebit;        if (rt->requestCount == 0)            JS_NOTIFY_REQUEST_DONE(rt);    }    /* If another thread is already in GC, don't attempt GC; wait instead. */    if (rt->gcLevel > 0) {        /* Bump gcLevel to restart the current GC, so it finds new garbage. */        rt->gcLevel++;        METER(if (rt->gcLevel > rt->gcStats.maxlevel)                  rt->gcStats.maxlevel = rt->gcLevel);        /* Wait for the other thread to finish, then resume our request. */        while (rt->gcLevel > 0)            JS_AWAIT_GC_DONE(rt);        if (requestDebit)            rt->requestCount += requestDebit;        if (!(gcflags & GC_ALREADY_LOCKED))            JS_UNLOCK_GC(rt);        return;    }    /* No other thread is in GC, so indicate that we're now in GC. */    rt->gcLevel = 1;    rt->gcThread = currentThread;    /* Wait for all other requests to finish. */    while (rt->requestCount > 0)        JS_AWAIT_REQUEST_DONE(rt);#else  /* !JS_THREADSAFE */    /* Bump gcLevel and return rather than nest; the outer gc will restart. */    rt->gcLevel++;    METER(if (rt->gcLevel > rt->gcStats.maxlevel)              rt->gcStats.maxlevel = rt->gcLevel);    if (rt->gcLevel > 1)        return;#endif /* !JS_THREADSAFE */    /*     * Set rt->gcRunning here within the GC lock, and after waiting for any     * active requests to end, so that new requests that try to JS_AddRoot,     * JS_RemoveRoot, or JS_RemoveRootRT block in JS_BeginRequest waiting for     * rt->gcLevel to drop to zero, while request-less calls to the *Root*     * APIs block in js_AddRoot or js_RemoveRoot (see above in this file),     * waiting for GC to finish.     */    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;    /* Drop atoms held by the property cache, and clear property weak links. */    js_DisablePropertyCache(cx);    js_FlushPropertyCache(cx);#ifdef DEBUG_notme  { extern void js_DumpScopeMeters(JSRuntime *rt);    js_DumpScopeMeters(rt);  }#endifrestart:    rt->gcNumber++;    /*     * Mark phase.     */    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_MarkWatchPoints(rt);    if (gcflags & GC_KEEP_ATOMS)        js_MarkScriptFilenames(rt);    iter = NULL;    while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) {        /*         * Iterate frame chain and dormant chains. Temporarily tack current         * frame onto the head of the dormant list to ease iteration.         *         * (NB: see comment on this whole "dormant" thing in js_Execute.)         */        chain = acx->fp;        if (chain) {            JS_ASSERT(!chain->dormantNext);            chain->dormantNext = acx->dormantFrameChain;        } else {            chain = acx->dormantFrameChain;        }        for (fp = chain; fp; fp = chain = chain->dormantNext) {            do {                if (fp->callobj)                    GC_MARK(cx, fp->callobj, "call object", NULL);                if (fp->argsobj)                    GC_MARK(cx, fp->argsobj, "arguments object", NULL);                if (fp->varobj)                    GC_MARK(cx, fp->varobj, "variables object", NULL);                if (fp->script) {                    js_MarkScript(cx, fp->script, NULL);                    if (fp->spbase) {                        /*                         * Don't mark what has not been pushed yet, or what                         * has been popped already.                         */                        depth = fp->script->depth;                        nslots = (JS_UPTRDIFF(fp->sp, fp->spbase)                                  < depth * sizeof(jsval))                                 ? (uintN)(fp->sp - fp->spbase)                                 : depth;                        GC_MARK_JSVALS(cx, nslots, fp->spbase, "operand");                    }                }                GC_MARK(cx, fp->thisp, "this", NULL);                if (fp->argv) {                    nslots = fp->argc;                    if (fp->fun && fp->fun->nargs > nslots)                        nslots = fp->fun->nargs;                    GC_MARK_JSVALS(cx, nslots, fp->argv, "arg");                }                if (JSVAL_IS_GCTHING(fp->rval))                    GC_MARK(cx, JSVAL_TO_GCTHING(fp->rval), "rval", NULL);                if (fp->vars)                    GC_MARK_JSVALS(cx, fp->nvars, fp->vars, "var");                GC_MARK(cx, fp->scopeChain, "scope chain", NULL);                if (fp->sharpArray)                    GC_MARK(cx, fp->sharpArray, "sharp array", NULL);                if (fp->xmlNamespace)                    GC_MARK(cx, fp->xmlNamespace, "xmlNamespace", NULL);            } while ((fp = fp->down) != NULL);        }        /* Cleanup temporary "dormant" linkage. */        if (acx->fp)            acx->fp->dormantNext = NULL;        /* Mark other roots-by-definition in acx. */        GC_MARK(cx, acx->globalObject, "global object", NULL);        for (i = 0; i < GCX_NTYPES; i++)            GC_MARK(cx, acx->newborn[i], gc_typenames[i], NULL);        if (acx->lastAtom)            GC_MARK_ATOM(cx, acx->lastAtom, NULL);#if JS_HAS_EXCEPTIONS        if (acx->throwing && JSVAL_IS_GCTHING(acx->exception))            GC_MARK(cx, JSVAL_TO_GCTHING(acx->exception), "exception", NULL);#endif#if JS_HAS_LVALUE_RETURN        if (acx->rval2set && JSVAL_IS_GCTHING(acx->rval2))            GC_MARK(cx, JSVAL_TO_GCTHING(acx->rval2), "rval2", NULL);#endif        for (sh = acx->stackHeaders; sh; sh = sh->down) {            METER(rt->gcStats.stackseg++);            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);#endif    if (rt->gcCallback)        (void) rt->gcCallback(cx, JSGC_MARK_END);    /*     * Sweep phase.     *     * Finalize as we sweep, outside of rt->gcLock, but with rt->gcRunning set     * so that any attempt to allocate a GC-thing from a finalizer will fail,     * rather than nest badly and leave the unmarked newborn to be swept.     *     * Finalize smaller objects before larger, to guarantee finalization of     * GC-allocated obj->slots after obj.  See FreeSlots in jsobj.c.     */    js_SweepAtomState(&rt->atomState);    js_SweepScopeProperties(rt);    js_SweepScriptFilenames(rt);    for (i = 0; i < GC_NUM_FREELISTS; i++) {        nbytes = GC_FREELIST_NBYTES(i);        nflags = nbytes / sizeof(JSGCThing);        for (a = rt->gcArenaPool[i].first.next; a; a = a->next) {            flagp = (uint8 *) a->base;            split = (uint8 *) FIRST_THING_PAGE(a);            limit = (JSGCThing *) a->avail;            for (thing = (JSGCThing *) split; thing < limit; thing += nflags) {                if (((jsuword)thing & GC_PAGE_MASK) == 0) {                    thing = (JSGCThing *) FIRST_THING((jsuword)thing, nbytes);                    flagp = js_GetGCThingFlags(thing);                }                flags = *flagp;                if (flags & GCF_MARK) {                    *flagp &= ~GCF_MARK;                } else if (!(flags & (GCF_LOCKMASK | GCF_FINAL))) {                    /* Call the finalizer with GCF_FINAL ORed into flags. */                    type = flags & GCF_TYPEMASK;                    finalizer = gc_finalizers[type];                    if (finalizer) {                        *flagp = (uint8)(flags | GCF_FINAL);                        if (type >= GCX_EXTERNAL_STRING)                            js_PurgeDeflatedStringCache((JSString *)thing);                        finalizer(cx, thing);                    }                    /* Set flags to GCF_FINAL, signifying that thing is free. */                    *flagp = GCF_FINAL;                    bytesptr = (type == GCX_PRIVATE)                               ? &rt->gcPrivateBytes                               : &rt->gcBytes;                    JS_ASSERT(*bytesptr >= nbytes + nflags);                    *bytesptr -= nbytes + nflags;                }                flagp += nflags;                if (JS_UPTRDIFF(flagp, split) < nflags)                    flagp += GC_THINGS_SIZE;            }        }    }    /*     * Free phase.     * Free any unused arenas and rebuild the JSGCThing freelist.     */    for (i = 0; i < GC_NUM_FREELISTS; i++) {        ap = &rt->gcArenaPool[i].first.next;        a = *ap;        if (!a)            continue;        all_clear = JS_TRUE;        flp = oflp = &rt->gcFreeList[i];        *flp = NULL;        METER(rt->gcStats.freelen[i] = 0);        nbytes = GC_FREELIST_NBYTES(i);        nflags = nbytes / sizeof(JSGCThing);        do {            flagp = (uint8 *) a->base;            split = (uint8 *) FIRST_THING_PAGE(a);            limit = (JSGCThing *) a->avail;            for (thing = (JSGCThing *) split; thing < limit; thing += nflags) {                if (((jsuword)thing & GC_PAGE_MASK) == 0) {                    thing = (JSGCThing *) FIRST_THING((jsuword)thing, nbytes);                    flagp = js_GetGCThingFlags(thing);                }                if (*flagp != GCF_FINAL) {                    all_clear = JS_FALSE;                } else {                    thing->flagp = flagp;                    *flp = thing;                    flp = &thing->next;                    METER(rt->gcStats.freelen[i]++);                }                flagp += nflags;                if (JS_UPTRDIFF(flagp, split) < nflags)                    flagp += GC_THINGS_SIZE;            }            if (all_clear) {                JS_ARENA_DESTROY(&rt->gcArenaPool[i], a, ap);                flp = oflp;                METER(rt->gcStats.afree++);            } else {                ap = &a->next;                all_clear = JS_TRUE;                oflp = flp;            }        } while ((a = *ap) != NULL);        /* Terminate the new freelist. */        *flp = NULL;    }    if (rt->gcCallback)        (void) rt->gcCallback(cx, JSGC_FINALIZE_END);#ifdef DEBUG_notme  { extern void DumpSrcNoteSizeHist();    DumpSrcNoteSizeHist();    printf("GC HEAP SIZE %lu (%lu)\n",           (unsigned long)rt->gcBytes, (unsigned long)rt->gcPrivateBytes);  }#endif    JS_LOCK_GC(rt);    if (rt->gcLevel > 1) {        rt->gcLevel = 1;        JS_UNLOCK_GC(rt);        goto restart;    }    js_EnablePropertyCache(cx);    rt->gcLevel = 0;    rt->gcLastBytes = rt->gcBytes;    rt->gcPoke = rt->gcRunning = JS_FALSE;#ifdef JS_THREADSAFE    /* If we were invoked during a request, pay back the temporary debit. */    if (requestDebit)        rt->requestCount += requestDebit;    rt->gcThread = 0;    JS_NOTIFY_GC_DONE(rt);    if (!(gcflags & GC_ALREADY_LOCKED))        JS_UNLOCK_GC(rt);#endif    if (rt->gcCallback) {        if (gcflags & GC_ALREADY_LOCKED)            JS_UNLOCK_GC(rt);        (void) rt->gcCallback(cx, JSGC_END);        if (gcflags & GC_ALREADY_LOCKED)            JS_LOCK_GC(rt);    }}