if (slot == SPROP_INVALID_SLOT && !js_AllocSlot(cx, scope->object, &slot)) {
if (slot != SPROP_INVALID_SLOT) JS_ASSERT(overwriting); else if (!js_AllocSlot(cx, scope->object, &slot))
js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id,                    JSPropertyOp getter, JSPropertyOp setter, uint32 slot,                    uintN attrs, uintN flags, intN shortid){    JSScopeProperty **spp, *sprop, *overwriting, **spvec, **spp2, child;    uint32 size, splen, i;    int change;    CHECK_ANCESTOR_LINE(scope, JS_TRUE);    /*     * Normalize stub getter and setter values for faster is-stub testing in     * the SPROP_CALL_[GS]ETTER macros.     */    if (getter == JS_PropertyStub)        getter = NULL;    if (setter == JS_PropertyStub)        setter = NULL;    /*     * Search for id in order to claim its entry, allocating a property tree     * node if one doesn't already exist for our parameters.     */    spp = js_SearchScope(scope, id, JS_TRUE);    sprop = overwriting = SPROP_FETCH(spp);    if (!sprop) {        /* Check whether we need to grow, if the load factor is >= .75. */        JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));        size = JS_BIT(scope->sizeLog2);        if (scope->entryCount + scope->removedCount >= size - (size >> 2)) {            if (scope->removedCount >= size >> 2) {                METER(compresses);                change = 0;            } else {                METER(grows);                change = 1;            }            if (!ChangeScope(cx, scope, change) &&                scope->entryCount + scope->removedCount == size - 1) {                METER(addFailures);                return NULL;            }            spp = js_SearchScope(scope, id, JS_TRUE);            JS_ASSERT(!SPROP_FETCH(spp));        }    } else {        /* Property exists: js_SearchScope must have returned a valid entry. */        JS_ASSERT(!SPROP_IS_REMOVED(*spp));        /*         * If all property members match, this is a redundant add and we can         * return early.  If the caller wants to allocate a slot, but doesn't         * care which slot, copy sprop->slot into slot so we can match sprop,         * if all other members match.         */        if (!(attrs & JSPROP_SHARED) &&            slot == SPROP_INVALID_SLOT &&            SPROP_HAS_VALID_SLOT(sprop, scope)) {            slot = sprop->slot;        }        if (SPROP_MATCH_PARAMS_AFTER_ID(sprop, getter, setter, slot, attrs,                                        flags, shortid)) {            METER(redundantAdds);            return sprop;        }        /*         * Duplicate formal parameters require us to leave the old property         * on the ancestor line, so the decompiler can find it, even though         * its entry in scope->table is overwritten to point at a new property         * descending from the old one.  The SPROP_IS_DUPLICATE flag helps us         * cope with the consequent disparity between ancestor line height and         * scope->entryCount.         */        if (flags & SPROP_IS_DUPLICATE) {            sprop->flags |= SPROP_IS_DUPLICATE;        } else {            /*             * If we are clearing sprop to force an existing property to be             * overwritten (apart from a duplicate formal parameter), we must             * unlink it from the ancestor line at scope->lastProp, lazily if             * sprop is not lastProp.  And we must remove the entry at *spp,             * precisely so the lazy "middle delete" fixup code further below             * won't find sprop in scope->table, in spite of sprop being on             * the ancestor line.             *             * When we finally succeed in finding or creating a new sprop             * and storing its pointer at *spp, we'll use the |overwriting|             * local saved when we first looked up id to decide whether we're             * indeed creating a new entry, or merely overwriting an existing             * property.             */            if (sprop == SCOPE_LAST_PROP(scope)) {                do {                    SCOPE_REMOVE_LAST_PROP(scope);                    if (!SCOPE_HAD_MIDDLE_DELETE(scope))                        break;                    sprop = SCOPE_LAST_PROP(scope);                } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop));            } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) {                /*                 * If we have no hash table yet, we need one now.  The middle                 * delete code is simple-minded that way!                 */                if (!scope->table) {                    if (!CreateScopeTable(scope)) {                        JS_ReportOutOfMemory(cx);                        return NULL;                    }                    spp = js_SearchScope(scope, id, JS_TRUE);                    sprop = overwriting = SPROP_FETCH(spp);                }                SCOPE_SET_MIDDLE_DELETE(scope);            }        }        /*         * If we fail later on trying to find or create a new sprop, we will         * goto fail_overwrite and restore *spp from |overwriting|.  Note that         * we don't bother to keep scope->removedCount in sync, because we'll         * fix up *spp and scope->entryCount shortly, no matter how control         * flow returns from this function.         */        if (scope->table)            SPROP_STORE_PRESERVING_COLLISION(spp, NULL);        scope->entryCount--;        CHECK_ANCESTOR_LINE(scope, JS_TRUE);        sprop = NULL;    }    if (!sprop) {        /*         * If properties were deleted from the middle of the list starting at         * scope->lastProp, we may need to fork the property tree and squeeze         * all deleted properties out of scope's ancestor line.  Otherwise we         * risk adding a node with the same id as a "middle" node, violating         * the rule that properties along an ancestor line have distinct ids         * (unless flagged SPROP_IS_DUPLICATE).         */        if (SCOPE_HAD_MIDDLE_DELETE(scope)) {            JS_ASSERT(scope->table);            CHECK_ANCESTOR_LINE(scope, JS_TRUE);            splen = scope->entryCount;            if (splen == 0) {                scope->lastProp = NULL;            } else {                /*                 * Enumerate live entries in scope->table using a temporary                 * vector, by walking the (possibly sparse, due to deletions)                 * ancestor line from scope->lastProp.                 */                spvec = (JSScopeProperty **)                    JS_malloc(cx, splen * sizeof(JSScopeProperty *));                if (!spvec)                    goto fail_overwrite;                i = splen;                sprop = SCOPE_LAST_PROP(scope);                JS_ASSERT(sprop);                do {                    /*                     * NB: test SCOPE_GET_PROPERTY, not SCOPE_HAS_PROPERTY --                     * the latter insists that sprop->id maps to sprop, while                     * the former simply tests whether sprop->id is bound in                     * scope.  We must allow for duplicate formal parameters                     * along the ancestor line, and fork them as needed.                     */                    if (!SCOPE_GET_PROPERTY(scope, sprop->id))                        continue;                    JS_ASSERT(sprop != overwriting);                    if (i == 0) {                        /*                         * If our original splen estimate, scope->entryCount,                         * is less than the ancestor line height, there must                         * be duplicate formal parameters in this (function                         * object) scope.  Count remaining ancestors in order                         * to realloc spvec.                         */                        do {                            i++;                        } while ((sprop = sprop->parent) != NULL);                        spp2 = (JSScopeProperty **)                            JS_realloc(cx, spvec,                                       (splen + i) * sizeof(JSScopeProperty *));                        if (!spp2) {                            JS_free(cx, spvec);                            goto fail_overwrite;                        }                        spvec = spp2;                        memmove(spvec + i, spvec,                                splen * sizeof(JSScopeProperty *));                        splen += i;                        sprop = spvec[i];                    }                    spvec[--i] = sprop;                } while ((sprop = sprop->parent) != NULL);                JS_ASSERT(i == 0);                /*                 * Now loop forward through spvec, forking the property tree                 * whenever we see a "parent gap" due to deletions from scope.                 * NB: sprop is null on first entry to the loop body.                 */                do {                    if (spvec[i]->parent == sprop) {                        sprop = spvec[i];                    } else {                        sprop = GetPropertyTreeChild(cx, sprop, spvec[i]);                        if (!sprop) {                            JS_free(cx, spvec);                            goto fail_overwrite;                        }                        spp2 = js_SearchScope(scope, sprop->id, JS_FALSE);                        JS_ASSERT(SPROP_FETCH(spp2) == spvec[i]);                        SPROP_STORE_PRESERVING_COLLISION(spp2, sprop);                    }                } while (++i < splen);                JS_free(cx, spvec);                /*                 * Now sprop points to the last property in scope, where the                 * ancestor line from sprop to the root is dense w.r.t. scope:                 * it contains no nodes not mapped by scope->table, apart from                 * any stinking ECMA-mandated duplicate formal parameters.                 */                scope->lastProp = sprop;                CHECK_ANCESTOR_LINE(scope, JS_FALSE);                JS_RUNTIME_METER(cx->runtime, middleDeleteFixups);            }        }        /*         * Aliases share another property's slot, passed in the |slot| param.         * Shared properties have no slot; unshared properties that do not         * alias another property's slot get one here, but may lose it due to         * a JS_ClearScope call.         */        if (!(flags & SPROP_IS_ALIAS)) {            if (attrs & JSPROP_SHARED) {                slot = SPROP_INVALID_SLOT;            } else {                /*                 * We may have set slot from a nearly-matching sprop, above.                 * If so, we're overwriting that nearly-matching sprop, so we                 * can reuse its slot -- we don't need to allocate a new one.                 * Callers should therefore pass SPROP_INVALID_SLOT for all                 * non-alias, unshared property adds.                 */                if (slot == SPROP_INVALID_SLOT &&                    !js_AllocSlot(cx, scope->object, &slot)) {                    goto fail_overwrite;                }            }        }        /*         * Check for a watchpoint on a deleted property; if one exists, change         * setter to js_watch_set.         * XXXbe this could get expensive with lots of watchpoints...         */        if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList) &&            js_FindWatchPoint(cx->runtime, scope, id)) {            setter = js_WrapWatchedSetter(cx, id, attrs, setter);            if (!setter)                goto fail_overwrite;        }        /* Find or create a property tree node labeled by our arguments. */        child.id = id;        child.getter = getter;        child.setter = setter;        child.slot = slot;        child.attrs = attrs;        child.flags = flags;        child.shortid = shortid;        sprop = GetPropertyTreeChild(cx, scope->lastProp, &child);        if (!sprop)            goto fail_overwrite;        /* Store the tree node pointer in the table entry for id. */        if (scope->table)            SPROP_STORE_PRESERVING_COLLISION(spp, sprop);        scope->entryCount++;        scope->lastProp = sprop;        CHECK_ANCESTOR_LINE(scope, JS_FALSE);        if (!overwriting) {            JS_RUNTIME_METER(cx->runtime, liveScopeProps);            JS_RUNTIME_METER(cx->runtime, totalScopeProps);        }        /*         * If we reach the hashing threshold, try to allocate scope->table.         * If we can't (a rare event, preceded by swapping to death on most         * modern OSes), stick with linear search rather than whining about         * this little set-back.  Therefore we must test !scope->table and         * scope->entryCount >= SCOPE_HASH_THRESHOLD, not merely whether the         * entry count just reached the threshold.         */        if (!scope->table && scope->entryCount >= SCOPE_HASH_THRESHOLD)            (void) CreateScopeTable(scope);    }    METER(adds);    return sprop;fail_overwrite:    if (overwriting) {        /*         * We may or may not have forked overwriting out of scope's ancestor         * line, so we must check (the alternative is to set a flag above, but         * that hurts the common, non-error case).  If we did fork overwriting         * out, we'll add it back at scope->lastProp.  This means enumeration         * order can change due to a failure to overwrite an id.         * XXXbe very minor incompatibility         */        for (sprop = SCOPE_LAST_PROP(scope); ; sprop = sprop->parent) {            if (!sprop) {                sprop = SCOPE_LAST_PROP(scope);                if (overwriting->parent == sprop) {                    SCOPE_SET_LAST_PROP(scope, overwriting);                } else {                    sprop = GetPropertyTreeChild(cx, sprop, overwriting);                    if (sprop) {                        JS_ASSERT(sprop != overwriting);                        SCOPE_SET_LAST_PROP(scope, sprop);                    }                    overwriting = sprop;                }                break;            }            if (sprop == overwriting)                break;        }        if (overwriting) {            if (scope->table)                SPROP_STORE_PRESERVING_COLLISION(spp, overwriting);            scope->entryCount++;        }        CHECK_ANCESTOR_LINE(scope, JS_TRUE);    }    METER(addFailures);    return NULL;}