if (vp[-1] * sizeof(jsval) <= GC_NBYTES_MAX)
if ((vp[-1] + 1) * sizeof(jsval) <= GC_NBYTES_MAX)
MARK_GC_THING(JSContext *cx, void *thing, uint8 *flagp, void *arg){    JSRuntime *rt;    JSObject *obj;    jsval v, *vp, *end;    JSString *str;    void *next_thing;    uint8 *next_flagp;#ifdef JS_GCMETER    uint32 tailCallNesting;#endif#ifdef GC_MARK_DEBUG    JSScope *scope;    JSScopeProperty *sprop;    char name[32];#endif    int stackDummy;    rt = cx->runtime;    METER(tailCallNesting = 0);    METER(if (++rt->gcStats.cdepth > rt->gcStats.maxcdepth)              rt->gcStats.maxcdepth = rt->gcStats.cdepth);#ifndef GC_MARK_DEBUG  start:#endif    JS_ASSERT(flagp);    METER(if (++rt->gcStats.depth > rt->gcStats.maxdepth)              rt->gcStats.maxdepth = rt->gcStats.depth);    if (*flagp & GCF_MARK) {        /*         * This should happen only if recursive MARK_GC_THING marks flags         * already stored in the caller's *next_flagp.         */        goto out;    }    *flagp |= GCF_MARK;#ifdef GC_MARK_DEBUG    if (js_DumpGCHeap)        gc_dump_thing(thing, *flagp, arg, js_DumpGCHeap);#endif    switch (*flagp & GCF_TYPEMASK) {      case GCX_OBJECT:        /* If obj->slots is null, obj must be a newborn. */        obj = (JSObject *) thing;        vp = obj->slots;        if (!vp)            goto out;        /* Mark slots if they are small enough to be GC-allocated. */        if (vp[-1] * sizeof(jsval) <= GC_NBYTES_MAX)            GC_MARK(cx, vp - 1, "slots", arg);        /* Switch to Deutsch-Schorr-Waite if we exhaust our stack quota. */        if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {            METER(rt->gcStats.dswmark++);            DeutschSchorrWaite(cx, thing, flagp);            goto out;        }        /* Set up local variables to loop over unmarked things. */        end = vp + ((obj->map->ops->mark)                    ? CALL_GC_THING_MARKER(obj->map->ops->mark, cx, obj, arg)                    : JS_MIN(obj->map->freeslot, obj->map->nslots));        vp = NEXT_UNMARKED_GC_THING(vp, end, &thing, &flagp, arg);        if (!vp)            goto out;        v = *vp;        /*         * Here, thing is the first value in obj->slots referring to an         * unmarked GC-thing.         */#ifdef GC_MARK_DEBUG        scope = OBJ_IS_NATIVE(obj) ? OBJ_SCOPE(obj) : NULL;#endif        for (;;) {            /* Check loop invariants. */            JS_ASSERT(v == *vp && JSVAL_IS_GCTHING(v));            JS_ASSERT(thing == JSVAL_TO_GCTHING(v));            JS_ASSERT(flagp == js_GetGCThingFlags(thing));#ifdef GC_MARK_DEBUG            if (scope) {                uint32 slot;                jsval nval;                slot = vp - obj->slots;                for (sprop = SCOPE_LAST_PROP(scope); ; sprop = sprop->parent) {                    if (!sprop) {                        switch (slot) {                          case JSSLOT_PROTO:                            strcpy(name, js_proto_str);                            break;                          case JSSLOT_PARENT:                            strcpy(name, js_parent_str);                            break;                          default:                            JS_snprintf(name, sizeof name,                                        "**UNKNOWN SLOT %ld**",                                        (long)slot);                            break;                        }                        break;                    }                    if (sprop->slot == slot) {                        nval = ID_TO_VALUE(sprop->id);                        if (JSVAL_IS_INT(nval)) {                            JS_snprintf(name, sizeof name, "%ld",                                        (long)JSVAL_TO_INT(nval));                        } else if (JSVAL_IS_STRING(nval)) {                            JS_snprintf(name, sizeof name, "%s",                              JS_GetStringBytes(JSVAL_TO_STRING(nval)));                        } else {                            strcpy(name, "**FINALIZED ATOM KEY**");                        }                        break;                    }                }            } else {                strcpy(name, "**UNKNOWN OBJECT MAP ENTRY**");            }#endif            do {                vp = NEXT_UNMARKED_GC_THING(vp+1, end, &next_thing, &next_flagp,                                            arg);                if (!vp) {                    /*                     * Here thing came from the last unmarked GC-thing slot.                     * We can eliminate tail recursion unless GC_MARK_DEBUG                     * is defined.                     */#ifdef GC_MARK_DEBUG                    GC_MARK(cx, thing, name, arg);                    goto out;#else                    METER(++tailCallNesting);                    goto start;#endif                }            } while (next_thing == thing);            v = *vp;#ifdef GC_MARK_DEBUG            GC_MARK(cx, thing, name, arg);#else            MARK_GC_THING(cx, thing, flagp, arg);#endif            thing = next_thing;            flagp = next_flagp;        }        break;#ifdef DEBUG      case GCX_STRING:        str = (JSString *)thing;        JS_ASSERT(!JSSTRING_IS_DEPENDENT(str));        break;#endif      case GCX_MUTABLE_STRING:        str = (JSString *)thing;        if (JSSTRING_IS_DEPENDENT(str)) {            thing = JSSTRDEP_BASE(str);            flagp = UNMARKED_GC_THING_FLAGS(thing, arg);            if (flagp) {#ifdef GC_MARK_DEBUG                GC_MARK(cx, thing, "base", arg);                goto out;#else                METER(++tailCallNesting);                goto start;#endif            }        }        break;#if JS_HAS_XML_SUPPORT      case GCX_NAMESPACE:        CALL_GC_THING_MARKER(js_MarkXMLNamespace, cx, (JSXMLNamespace *)thing,                             arg);        break;      case GCX_QNAME:        CALL_GC_THING_MARKER(js_MarkXMLQName, cx, (JSXMLQName *)thing, arg);        break;      case GCX_XML:        CALL_GC_THING_MARKER(js_MarkXML, cx, (JSXML *)thing, arg);        break;#endif    }out:    METER(rt->gcStats.depth -= 1 + tailCallNesting);    METER(rt->gcStats.cdepth--);    return JS_TRUE;}