jsint slot;
uint32 slot;
js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp){    JSRuntime *rt;    JSScope *scope, *protoscope;    JSScopeProperty *sprop, *protosprop;    JSHashNumber hash;    JSSymbol *sym, *protosym;    JSObject *proto, *tmp;    jsval protoid;    JSPropertyOp protogetter, protosetter;    uintN protoattrs;    JSClass *clasp;    jsval pval;    jsint slot;    JSString *str;    rt = cx->runtime;    JS_LOCK_OBJ(cx, obj);    protoid = protoattrs = 0;   /* Suppress use-before-set gcc warning */    protogetter = protosetter = NULL; /* Suppress use-before-set gcc warning */    scope = js_GetMutableScope(cx, obj);    if (!scope) {	JS_UNLOCK_OBJ(cx, obj);	return JS_FALSE;    }    /*     * Handle old bug that treated empty string as zero index.     * Also convert string indices to numbers if applicable.     */    CHECK_FOR_FUNNY_INDEX(id);    hash = js_HashValue(id);    sym = scope->ops->lookup(cx, scope, id, hash);    if (sym) {	sprop = sym_property(sym);#if JS_HAS_OBJ_WATCHPOINT	if (!sprop) {	    uint32 slot, nslots;	    jsval *slots;	    /*	     * Deleted property place-holder, could have a watchpoint that	     * holds the deleted-but-watched property.  If so, slots may have	     * shrunk, or at least freeslot may have shrunk due to the delete	     * operation destroying the property.	     */	    sprop = js_FindWatchPoint(rt, obj, js_IdToValue(id));	    if (sprop && (slot = sprop->slot) >= scope->map.freeslot) {		if (slot >= scope->map.nslots) {		    nslots = slot + slot / 2;		    slots = JS_realloc(cx, obj->slots, nslots * sizeof(jsval));		    if (!slots) {			JS_UNLOCK_OBJ(cx, obj);			return JS_FALSE;		    }		    scope->map.nslots = nslots;		    obj->slots = slots;		}		scope->map.freeslot = slot + 1;	    }	}#endif    } else {	sprop = NULL;    }    if (!sprop) {	/* Find a prototype property with the same id. */	proto = LOCKED_OBJ_GET_PROTO(obj);	protosprop = NULL;	JS_UNLOCK_OBJ(cx, obj);	while (proto) {	    JS_LOCK_OBJ(cx, proto);	    protoscope = (JSScope *)proto->map;	    if (MAP_IS_NATIVE(&protoscope->map)) {		protosym = protoscope->ops->lookup(cx, protoscope, id, hash);		if (protosym) {		    protosprop = sym_property(protosym);		    if (protosprop) {			protoid = protosprop->id;			protogetter = protosprop->getter;			protosetter = protosprop->setter;			protoattrs = protosprop->attrs;			JS_UNLOCK_OBJ(cx, proto);			break;		    }		}	    }	    tmp = LOCKED_OBJ_GET_PROTO(proto);	    JS_UNLOCK_OBJ(cx, proto);	    proto = tmp;	}	JS_LOCK_OBJ(cx, obj);	/* Make a new property descriptor with the right heritage. */	clasp = LOCKED_OBJ_GET_CLASS(obj);	if (protosprop) {	    if (protoattrs & JSPROP_READONLY)		goto read_only;	    sprop = js_NewScopeProperty(cx, scope, id,					protogetter, protosetter,					protoattrs);	    sprop->id = protoid;	} else {	    sprop = js_NewScopeProperty(cx, scope, id,					clasp->getProperty, clasp->setProperty,					JSPROP_ENUMERATE);	}	if (!sprop) {	    JS_UNLOCK_OBJ(cx, obj);	    return JS_FALSE;	}	/* XXXbe called with obj locked */	if (!clasp->addProperty(cx, obj, sprop->id, vp)) {	    js_DestroyScopeProperty(cx, scope, sprop);	    JS_UNLOCK_OBJ(cx, obj);	    return JS_FALSE;	}	/* Initialize new properties to undefined. */	LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID);	if (sym) {	    /* Null-valued symbol left behind from a delete operation. */	    sym->entry.value = js_HoldScopeProperty(cx, scope, sprop);	}    }    if (!sym) {	/* Need a new symbol as well as a new property. */	sym = scope->ops->add(cx, scope, id, sprop);	if (!sym) {	    js_DestroyScopeProperty(cx, scope, sprop);	    JS_UNLOCK_OBJ(cx, obj);	    return JS_FALSE;	}#if JS_BUG_AUTO_INDEX_PROPS	{	    jsid id2 = (jsid) INT_TO_JSVAL(sprop->slot - JSSLOT_START);	    if (!scope->ops->add(cx, scope, id2, sprop)) {		scope->ops->remove(cx, scope, id);		JS_UNLOCK_OBJ(cx, obj);		return JS_FALSE;	    }	    PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id2,				(JSProperty *)sprop);	}#endif	PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id,			    (JSProperty *)sprop);    }    /* Check for readonly now that we have sprop. */    if (sprop->attrs & JSPROP_READONLY) {read_only:	JS_UNLOCK_OBJ(cx, obj);	if (JSVERSION_IS_ECMA(cx->version))	    return JS_TRUE;	str = js_DecompileValueGenerator(cx, JS_FALSE, js_IdToValue(id), NULL);	if (str)	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,				 JSMSG_READ_ONLY, JS_GetStringBytes(str));	return JS_FALSE;    }    /* Get the current property value from its slot. */    JS_ASSERT(sprop->slot < obj->map->freeslot);    slot = sprop->slot;    pval = LOCKED_OBJ_GET_SLOT(obj, slot);#ifdef JS_THREADSAFE    /* Hold sprop across setter callout, and drop after, in case of delete. */    JS_ATOMIC_ADDREF(&sprop->nrefs, 1);#endif    /* Avoid deadlock by unlocking obj while calling sprop's setter. */    JS_UNLOCK_OBJ(cx, obj);    /* Let the setter modify vp before copying from it to obj->slots[slot]. */    if (!SPROP_SET(cx, sprop, obj, obj, vp)) {#ifdef JS_THREADSAFE	JS_LOCK_OBJ_VOID(cx, obj, js_DropScopeProperty(cx, scope, sprop));#endif	return JS_FALSE;    }    /* Relock obj until we are done with sprop. */    JS_LOCK_OBJ(cx, obj);#ifdef JS_THREADSAFE    sprop = js_DropScopeProperty(cx, scope, sprop);    if (!sprop) {	/* Lost a race with someone who deleted sprop. */	JS_UNLOCK_OBJ(cx, obj);	return JS_TRUE;    }#endif    GC_POKE(cx, pval);    LOCKED_OBJ_SET_SLOT(obj, slot, *vp);#if JS_BUG_SET_ENUMERATE    /* Setting a property makes it enumerable. */    sprop->attrs |= JSPROP_ENUMERATE;#endif    JS_UNLOCK_OBJ(cx, obj);    return JS_TRUE;}