goto readonly;
goto read_only;
js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp){    JSObject *pobj;    JSScopeProperty *sprop;    JSRuntime *rt;    JSClass *clasp;    JSScope *scope;    uintN attrs, flags;    intN shortid;    JSPropertyOp getter, setter;    jsval pval;    uint32 slot;    /*     * Handle old bug that took empty string as zero index.  Also convert     * string indices to integers if appropriate.     */    CHECK_FOR_FUNNY_INDEX(id);    if (!js_LookupProperty(cx, obj, id, &pobj, (JSProperty **)&sprop))        return JS_FALSE;    if (sprop && !OBJ_IS_NATIVE(pobj)) {        OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);        sprop = NULL;    }    rt = cx->runtime;    clasp = OBJ_GET_CLASS(cx, obj);    /*     * Now either sprop is null, meaning id was not found in obj or one of its     * prototypes; or sprop is non-null, meaning id was found in pobj's scope.     * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop     * is held: we must OBJ_DROP_PROPERTY or JS_UNLOCK_SCOPE before we return     * (the two are equivalent for native objects, but we use JS_UNLOCK_SCOPE     * because it is cheaper).     */    attrs = JSPROP_ENUMERATE;    flags = 0;    shortid = 0;    getter = clasp->getProperty;    setter = clasp->setProperty;    if (sprop) {        /*         * Set scope for use below.  It was locked by js_LookupProperty, and         * we know pobj owns it (i.e., scope->object == pobj).  Therefore we         * optimize JS_UNLOCK_OBJ(cx, pobj) into JS_UNLOCK_SCOPE(cx, scope).         */        scope = OBJ_SCOPE(pobj);        attrs = sprop->attrs;        if ((attrs & JSPROP_READONLY) || SCOPE_IS_SEALED(scope)) {            /* XXXbe ECMA violation: readonly proto-property stops set cold. */            OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);            goto readonly;        }        if (pobj != obj) {            /* Don't clone a shared prototype property. */            if (attrs & JSPROP_SHARED) {                JS_UNLOCK_SCOPE(cx, scope);                return SPROP_SET(cx, sprop, obj, pobj, vp);            }            /* XXXbe ECMA violation: inherit attrs, getter, setter. */            flags = sprop->flags;            shortid = sprop->shortid;            getter = sprop->getter;            setter = sprop->setter;            JS_UNLOCK_SCOPE(cx, scope);            /* Recover watched setter *after* releasing scope's lock. */            if ((attrs & JSPROP_SETTER)                ? ((JSFunction *)JS_GetPrivate(cx, (JSObject *)setter))->native                  == js_watch_set_wrapper                : setter == js_watch_set) {                setter = js_GetWatchedSetter(rt, scope, sprop);            }            sprop = NULL;        }#ifdef __GNUC__         /* suppress bogus gcc warnings */    } else {        scope = NULL;#endif    }    if (!sprop) {        if (SCOPE_IS_SEALED(OBJ_SCOPE(obj))) {readonly:            if (!JSVERSION_IS_ECMA(cx->version)) {                JSString *str = js_DecompileValueGenerator(cx,                                                           JSDVG_IGNORE_STACK,                                                           ID_TO_VALUE(id),                                                           NULL);                if (str) {                    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,                                         JSMSG_READ_ONLY,                                         JS_GetStringBytes(str));                }                return JS_FALSE;            }            return JS_TRUE;        }        /* Find or make a property descriptor with the right heritage. */        JS_LOCK_OBJ(cx, obj);        scope = js_GetMutableScope(cx, obj);        if (!scope) {            JS_UNLOCK_OBJ(cx, obj);            return JS_FALSE;        }        if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)            attrs |= JSPROP_SHARED;        sprop = js_AddScopeProperty(cx, scope, id, getter, setter,                                    SPROP_INVALID_SLOT, attrs, flags, shortid);        if (!sprop) {            JS_UNLOCK_SCOPE(cx, scope);            return JS_FALSE;        }        /* XXXbe called with obj locked */        if (!clasp->addProperty(cx, obj, SPROP_USERID(sprop), vp)) {            (void) js_RemoveScopeProperty(cx, scope, id);            JS_UNLOCK_SCOPE(cx, scope);            return JS_FALSE;        }        /* Initialize new property value (passed to setter) to undefined. */        if (SPROP_HAS_VALID_SLOT(sprop, scope))            LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID);        PROPERTY_CACHE_FILL(&rt->propertyCache, obj, id, sprop);    }    /* Get the current property value from its slot. */    slot = sprop->slot;    if (slot != SPROP_INVALID_SLOT) {        JS_ASSERT(slot < obj->map->freeslot);        pval = LOCKED_OBJ_GET_SLOT(obj, slot);        /* If sprop has a stub setter, keep scope locked and just store *vp. */        if (!sprop->setter) {            GC_POKE(cx, pval);            LOCKED_OBJ_SET_SLOT(obj, slot, *vp);            JS_UNLOCK_SCOPE(cx, scope);            return JS_TRUE;        }    }    /* Avoid deadlock by unlocking obj's scope while calling sprop's setter. */    JS_UNLOCK_SCOPE(cx, scope);    /* Let the setter modify vp before copying from it to obj->slots[slot]. */    if (!SPROP_SET(cx, sprop, obj, obj, vp))        return JS_FALSE;    /* Relock obj's scope until we are done with sprop. */    JS_LOCK_SCOPE(cx, scope);    /*     * Check whether sprop is still around (was not deleted), and whether it     * has a slot (it may never have had one, or we may have lost a race with     * someone who cleared scope).     */    if (SPROP_HAS_VALID_SLOT(sprop, scope)) {        GC_POKE(cx, pval);        LOCKED_OBJ_SET_SLOT(obj, slot, *vp);    }    JS_UNLOCK_SCOPE(cx, scope);    return JS_TRUE;}