cx->gcActive = JS_TRUE;
rt->gcThread = currentThread;
js_GC(JSContext *cx){    JSRuntime *rt;    JSContext *iter, *acx;    JSArena *a, *ma, *fa, **ap, **fap;    jsval v, *vp, *sp;    jsuword begin, end;    JSStackFrame *fp, *chain;    void *mark;    uint8 flags, *flagp;    JSGCThing *thing, *final, **flp, **oflp;    GCFinalizeOp finalizer;    JSBool a_all_clear, f_all_clear;    rt = cx->runtime;    if (rt->gcDisabled)	return;#ifdef JS_THREADSAFE    /* Avoid deadlock. */    JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt));#endif    /* Let the API user decide to defer a GC if it wants to. */    if (rt->gcCallback && !rt->gcCallback(cx, JSGC_BEGIN))	return;    /* Lock out other GC allocator and collector invocations. */    JS_LOCK_GC(rt);    /* Do nothing if no assignment has executed since the last GC. */    if (!rt->gcPoke) {	METER(rt->gcStats.nopoke++);	JS_UNLOCK_GC(rt);	return;    }    rt->gcPoke = JS_FALSE;    METER(rt->gcStats.poke++);#ifdef JS_THREADSAFE    /* Bump gcLevel and return rather than nest on this context. */    if (cx->gcActive) {	rt->gcLevel++;	METER(if (rt->gcLevel > rt->gcStats.maxlevel)		  rt->gcStats.maxlevel = rt->gcLevel);	if (rt->gcLevel > 1) {	    JS_UNLOCK_GC(rt);	    return;	}    }    /* If we're in a request, indicate, temporarily, that we're inactive. */    if (cx->requestDepth) {	rt->requestCount--;	JS_NOTIFY_REQUEST_DONE(rt);    }    /* If another thread is already in GC, don't attempt GC; wait instead. */    if (rt->gcLevel > 0) {	while (rt->gcLevel > 0)	    JS_AWAIT_GC_DONE(rt);	if (cx->requestDepth)	    rt->requestCount++;	JS_UNLOCK_GC(rt);	return;    }    /* No other thread is in GC, so indicate that we're now in GC. */    rt->gcLevel = 1;    /* Also indicate that GC is active on this context. */    cx->gcActive = JS_TRUE;    /* 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 */    /* Reset malloc counter */    rt->gcMallocBytes = 0;    /* Drop atoms held by the property cache, and clear property weak links. */    js_FlushPropertyCache(cx);restart:    rt->gcNumber++;    /* Mark phase. */    JS_HashTableEnumerateEntries(rt->gcRootsHash, gc_root_marker, rt);    js_MarkAtomState(&rt->atomState, gc_mark);    iter = NULL;    while ((acx = js_ContextIterator(rt, &iter)) != NULL) {	/*	 * Iterate frame chain and dormant chains. Temporarily tack current	 * frame onto the head of the dormant list to ease iteration.	 *	 * (NOTE: 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) {	    sp = fp->sp;	    if (sp) {		for (a = acx->stackPool.first.next; a; a = a->next) {		    begin = a->base;		    end = a->avail;		    if (JS_UPTRDIFF(sp, begin) < JS_UPTRDIFF(end, begin))			end = (jsuword)sp;		    for (vp = (jsval *)begin; vp < (jsval *)end; vp++) {			v = *vp;			if (JSVAL_IS_GCTHING(v))			    GC_MARK(rt, JSVAL_TO_GCTHING(v), "stack", NULL);		    }		    if (end == (jsuword)sp)			break;		}	    }	    do {		GC_MARK(rt, fp->scopeChain, "scope chain", NULL);		GC_MARK(rt, fp->thisp, "this", NULL);		if (JSVAL_IS_GCTHING(fp->rval))		    GC_MARK(rt, JSVAL_TO_GCTHING(fp->rval), "rval", NULL);		if (fp->callobj)		    GC_MARK(rt, fp->callobj, "call object", NULL);		if (fp->argsobj)		    GC_MARK(rt, fp->argsobj, "arguments object", NULL);		if (fp->script)		    GC_MARK_SCRIPT(rt, fp->script, NULL);		if (fp->sharpArray)		    GC_MARK(rt, fp->sharpArray, "sharp array", NULL);	    } while ((fp = fp->down) != NULL);	}	/* cleanup temporary link */	if (acx->fp)	    acx->fp->dormantNext = NULL;	GC_MARK(rt, acx->globalObject, "global object", NULL);	GC_MARK(rt, acx->newborn[GCX_OBJECT], "newborn object", NULL);	GC_MARK(rt, acx->newborn[GCX_STRING], "newborn string", NULL);	GC_MARK(rt, acx->newborn[GCX_DOUBLE], "newborn double", NULL);#if JS_HAS_EXCEPTIONS	if (acx->throwing && JSVAL_IS_GCTHING(acx->exception))	    GC_MARK(rt, JSVAL_TO_GCTHING(acx->exception), "exception", NULL);#endif    }    /* Sweep phase.  Mark in tempPool for release at label out:. */    ma = cx->tempPool.current;    mark = JS_ARENA_MARK(&cx->tempPool);    js_SweepAtomState(&rt->atomState);    fa = rt->gcFlagsPool.first.next;    flagp = (uint8 *)fa->base;    for (a = rt->gcArenaPool.first.next; a; a = a->next) {	for (thing = (JSGCThing *)a->base; thing < (JSGCThing *)a->avail;	     thing++) {	    if (flagp >= (uint8 *)fa->avail) {		fa = fa->next;		JS_ASSERT(fa);		if (!fa) {		    METER(rt->gcStats.badflag++);		    goto out;		}		flagp = (uint8 *)fa->base;	    }	    flags = *flagp;	    if (flags & GCF_MARK) {		*flagp &= ~GCF_MARK;	    } else if (!(flags & (GCF_LOCKMASK | GCF_FINAL))) {		JS_ARENA_ALLOCATE(final, &cx->tempPool, sizeof(JSGCThing));		if (!final)		    goto out;		final->next = thing;		final->flagp = flagp;		JS_ASSERT(rt->gcBytes >= sizeof(JSGCThing) + sizeof(uint8));		rt->gcBytes -= sizeof(JSGCThing) + sizeof(uint8);	    }	    flagp++;	}    }    /* Finalize phase.  Don't hold the GC lock while running finalizers! */    JS_UNLOCK_GC(rt);    for (final = (JSGCThing *) mark; ; final++) {	if ((jsuword)final >= ma->avail) {	    ma = ma->next;	    if (!ma)		break;	    final = (JSGCThing *)ma->base;	}	thing = final->next;	flagp = final->flagp;	flags = *flagp;	finalizer = gc_finalizers[flags & GCF_TYPEMASK];	if (finalizer) {	    *flagp |= GCF_FINAL;	    finalizer(cx, thing);	}	/*	 * Set flags to GCF_FINAL, signifying that thing is free, but don't	 * thread thing onto rt->gcFreeList.  We need the GC lock to rebuild	 * the freelist below while also looking for free-able arenas.	 */	*flagp = GCF_FINAL;    }    JS_LOCK_GC(rt);    /* Free unused arenas and rebuild the freelist. */    ap = &rt->gcArenaPool.first.next;    a = *ap;    if (!a)	goto out;    thing = (JSGCThing *)a->base;    a_all_clear = f_all_clear = JS_TRUE;    flp = oflp = &rt->gcFreeList;    *flp = NULL;    METER(rt->gcStats.freelen = 0);    fap = &rt->gcFlagsPool.first.next;    while ((fa = *fap) != NULL) {	/* XXX optimize by unrolling to use word loads */	for (flagp = (uint8 *)fa->base; ; flagp++) {	    JS_ASSERT(a);	    if (!a) {		METER(rt->gcStats.badarena++);		goto out;	    }	    if (thing >= (JSGCThing *)a->avail) {		if (a_all_clear) {		    JS_ARENA_DESTROY(&rt->gcArenaPool, a, ap);		    flp = oflp;		    METER(rt->gcStats.afree++);		} else {		    ap = &a->next;		    a_all_clear = JS_TRUE;		    oflp = flp;		}		a = *ap;		if (!a)		    break;		thing = (JSGCThing *)a->base;	    }	    if (flagp >= (uint8 *)fa->avail)		break;	    if (*flagp != GCF_FINAL) {		a_all_clear = f_all_clear = JS_FALSE;	    } else {		thing->flagp = flagp;		*flp = thing;		flp = &thing->next;		METER(rt->gcStats.freelen++);	    }	    thing++;	}	if (f_all_clear) {	    JS_ARENA_DESTROY(&rt->gcFlagsPool, fa, fap);	    METER(rt->gcStats.fafree++);	} else {	    fap = &fa->next;	    f_all_clear = JS_TRUE;	}    }    /* Terminate the new freelist. */    *flp = NULL;out:    JS_ARENA_RELEASE(&cx->tempPool, mark);    if (rt->gcLevel > 1) {	rt->gcLevel = 1;	goto restart;    }    rt->gcLevel = 0;    rt->gcLastBytes = rt->gcBytes;#ifdef JS_THREADSAFE    /* If we were invoked during a request, undo the temporary decrement. */    if (cx->requestDepth)	rt->requestCount++;    cx->gcActive = JS_FALSE;    JS_NOTIFY_GC_DONE(rt);    JS_UNLOCK_GC(rt);#endif    if (rt->gcCallback)	(void) rt->gcCallback(cx, JSGC_END);}