Bug 551007 - Make JSScopeProperty::attrs private, hide information behind accessor methods. r=jorendorff
authorJeff Walden <jwalden@mit.edu>
Mon, 08 Mar 2010 15:44:59 -0700
changeset 40265 48bb07b49e1f931548701690d051ccfa1739335a
parent 40264 44f70f0e8bb237294e0ebde8dc4e19ad8c50fc21
child 40266 1732da7b3164a646aed33b81a2bc515211fc7b4d
push id12610
push userrsayre@mozilla.com
push dateMon, 05 Apr 2010 17:26:41 +0000
treeherdermozilla-central@1942c0b4e101 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs551007
milestone1.9.3a3pre
Bug 551007 - Make JSScopeProperty::attrs private, hide information behind accessor methods. r=jorendorff
js/src/jsapi.cpp
js/src/jsbuiltins.cpp
js/src/jsdbgapi.cpp
js/src/jsinterp.cpp
js/src/jsobj.cpp
js/src/jsops.cpp
js/src/jsscope.h
js/src/jstracer.cpp
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3034,17 +3034,17 @@ JS_AliasProperty(JSContext *cx, JSObject
     }
     atom = js_Atomize(cx, alias, strlen(alias), 0);
     if (!atom) {
         ok = JS_FALSE;
     } else {
         sprop = (JSScopeProperty *)prop;
         ok = (js_AddNativeProperty(cx, obj, ATOM_TO_JSID(atom),
                                    sprop->getter(), sprop->setter(), sprop->slot,
-                                   sprop->attrs, sprop->getFlags() | JSScopeProperty::ALIAS,
+                                   sprop->attributes(), sprop->getFlags() | JSScopeProperty::ALIAS,
                                    sprop->shortid)
               != NULL);
     }
     obj->dropProperty(cx, prop);
     return ok;
 }
 
 static JSBool
@@ -3707,17 +3707,17 @@ JS_AliasElement(JSContext *cx, JSObject 
         JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)alias);
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS,
                              numBuf, name, OBJ_GET_CLASS(cx, obj2)->name);
         return JS_FALSE;
     }
     sprop = (JSScopeProperty *)prop;
     ok = (js_AddNativeProperty(cx, obj, INT_TO_JSID(alias),
                                sprop->getter(), sprop->setter(), sprop->slot,
-                               sprop->attrs, sprop->getFlags() | JSScopeProperty::ALIAS,
+                               sprop->attributes(), sprop->getFlags() | JSScopeProperty::ALIAS,
                                sprop->shortid)
           != NULL);
     obj->dropProperty(cx, prop);
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_AlreadyHasOwnElement(JSContext *cx, JSObject *obj, jsint index,
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -230,17 +230,17 @@ js_AddProperty(JSContext* cx, JSObject* 
                 goto exit_trace;
             }
         }
 
         scope->extend(cx, sprop);
     } else {
         JSScopeProperty *sprop2 =
             scope->addProperty(cx, sprop->id, sprop->getter(), sprop->setter(),
-                               SPROP_INVALID_SLOT, sprop->attrs, sprop->getFlags(),
+                               SPROP_INVALID_SLOT, sprop->attributes(), sprop->getFlags(),
                                sprop->shortid);
         if (sprop2 != sprop)
             goto exit_trace;
     }
 
     if (js_IsPropertyCacheDisabled(cx))
         goto exit_trace;
 
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -468,19 +468,19 @@ DropWatchPointAndUnlock(JSContext *cx, J
 
         /*
          * If the property wasn't found on wp->object, or it isn't still being
          * watched, then someone else must have deleted or unwatched it, and we
          * don't need to change the property attributes.
          */
         JSScopeProperty *wprop = scope->lookup(sprop->id);
         if (wprop &&
-            ((wprop->attrs ^ sprop->attrs) & JSPROP_SETTER) == 0 &&
+            wprop->hasSetterValue() == sprop->hasSetterValue() &&
             IsWatchedProperty(cx, wprop)) {
-            sprop = scope->changeProperty(cx, wprop, 0, wprop->attrs,
+            sprop = scope->changeProperty(cx, wprop, 0, wprop->attributes(),
                                           wprop->getter(), wp->setter);
             if (!sprop)
                 ok = JS_FALSE;
         }
         JS_UNLOCK_SCOPE(cx, scope);
     }
 
     cx->free(wp);
@@ -500,17 +500,17 @@ js_TraceWatchPoints(JSTracer *trc, JSObj
 
     rt = trc->context->runtime;
 
     for (wp = (JSWatchPoint *)rt->watchPointList.next;
          &wp->links != &rt->watchPointList;
          wp = (JSWatchPoint *)wp->links.next) {
         if (wp->object == obj) {
             wp->sprop->trace(trc);
-            if ((wp->sprop->attrs & JSPROP_SETTER) && wp->setter) {
+            if (wp->sprop->hasSetterValue() && wp->setter) {
                 JS_CALL_OBJECT_TRACER(trc, js_CastAsObject(wp->setter),
                                       "wp->setter");
             }
             JS_SET_TRACING_NAME(trc, "wp->closure");
             js_CallValueTracerIfGCThing(trc, OBJECT_TO_JSVAL(wp->closure));
         }
     }
 }
@@ -727,17 +727,17 @@ js_watch_set(JSContext *cx, JSObject *ob
 
                     cx->fp = &frame;
                 }
 #ifdef __GNUC__
                 else
                     argv = NULL;    /* suppress bogus gcc warnings */
 #endif
                 ok = !wp->setter ||
-                     ((sprop->attrs & JSPROP_SETTER)
+                     (sprop->hasSetterValue()
                       ? js_InternalCall(cx, obj,
                                         js_CastAsObjectJSVal(wp->setter),
                                         1, vp, vp)
                       : wp->setter(cx, obj, userid, vp));
                 if (injectFrame) {
                     /* Evil code can cause us to have an arguments object. */
                     frame.putActivationObjects(cx);
                     cx->fp = frame.down;
@@ -766,17 +766,17 @@ js_watch_set_wrapper(JSContext *cx, JSOb
     userid = ATOM_KEY(wrapper->atom);
     *rval = argv[0];
     return js_watch_set(cx, obj, userid, rval);
 }
 
 static bool
 IsWatchedProperty(JSContext *cx, JSScopeProperty *sprop)
 {
-    if (sprop->attrs & JSPROP_SETTER) {
+    if (sprop->hasSetterValue()) {
         JSObject *funobj = sprop->setterObject();
         if (!funobj->isFunction())
             return false;
 
         JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
         return FUN_NATIVE(fun) == js_watch_set_wrapper;
     }
     return sprop->setterOp() == js_watch_set;
@@ -874,17 +874,17 @@ JS_SetWatchPoint(JSContext *cx, JSObject
         intN shortid;
 
         if (OBJ_IS_NATIVE(pobj)) {
             value = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))
                     ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
                     : JSVAL_VOID;
             getter = sprop->getter();
             setter = sprop->setter();
-            attrs = sprop->attrs;
+            attrs = sprop->attributes();
             flags = sprop->getFlags();
             shortid = sprop->shortid;
         } else {
             if (!pobj->getProperty(cx, propid, &value) ||
                 !pobj->getAttributes(cx, propid, prop, &attrs)) {
                 pobj->dropProperty(cx, prop);
                 return JS_FALSE;
             }
@@ -906,17 +906,17 @@ JS_SetWatchPoint(JSContext *cx, JSObject
      * At this point, prop/sprop exists in obj, obj is locked, and we must
      * obj->dropProperty(cx, prop) before returning.
      */
     ok = JS_TRUE;
     DBG_LOCK(rt);
     wp = FindWatchPoint(rt, OBJ_SCOPE(obj), propid);
     if (!wp) {
         DBG_UNLOCK(rt);
-        watcher = js_WrapWatchedSetter(cx, propid, sprop->attrs, sprop->setter());
+        watcher = js_WrapWatchedSetter(cx, propid, sprop->attributes(), sprop->setter());
         if (!watcher) {
             ok = JS_FALSE;
             goto out;
         }
 
         wp = (JSWatchPoint *) cx->malloc(sizeof *wp);
         if (!wp) {
             ok = JS_FALSE;
@@ -925,17 +925,17 @@ JS_SetWatchPoint(JSContext *cx, JSObject
         wp->handler = NULL;
         wp->closure = NULL;
         wp->object = obj;
         JS_ASSERT(sprop->setter() != js_watch_set || pobj != obj);
         wp->setter = sprop->setter();
         wp->flags = JSWP_LIVE;
 
         /* XXXbe nest in obj lock here */
-        sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs,
+        sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attributes(),
                                              sprop->getter(), watcher);
         if (!sprop) {
             /* Self-link so DropWatchPointAndUnlock can JS_REMOVE_LINK it. */
             JS_INIT_CLIST(&wp->links);
             DBG_LOCK(rt);
             DropWatchPointAndUnlock(cx, wp, JSWP_LIVE);
             ok = JS_FALSE;
             goto out;
@@ -1480,19 +1480,19 @@ JS_GetPropertyDesc(JSContext *cx, JSObje
     } else {
         pd->flags = 0;
     }
 
     cx->throwing = wasThrowing;
     if (wasThrowing)
         cx->exception = lastException.value();
 
-    pd->flags |= ((sprop->attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0)
-              | ((sprop->attrs & JSPROP_READONLY)  ? JSPD_READONLY  : 0)
-              | ((sprop->attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0);
+    pd->flags |= (sprop->enumerable() ? JSPD_ENUMERATE : 0)
+              |  (!sprop->writable()  ? JSPD_READONLY  : 0)
+              |  (!sprop->configurable() ? JSPD_PERMANENT : 0);
     pd->spare = 0;
     if (sprop->getter() == js_GetCallArg) {
         pd->slot = sprop->shortid;
         pd->flags |= JSPD_ARGUMENT;
     } else if (sprop->getter() == js_GetCallVar) {
         pd->slot = sprop->shortid;
         pd->flags |= JSPD_VARIABLE;
     } else {
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -204,17 +204,17 @@ js_FillPropertyCache(JSContext *cx, JSOb
                 v = sprop->methodValue();
                 JS_ASSERT(VALUE_IS_FUNCTION(cx, v));
                 JS_ASSERT(v == LOCKED_OBJ_GET_SLOT(pobj, sprop->slot));
                 vword = JSVAL_OBJECT_TO_PCVAL(v);
                 break;
             }
 
             if (!scope->generic() &&
-                SPROP_HAS_STUB_GETTER(sprop) &&
+                sprop->hasDefaultGetter() &&
                 SPROP_HAS_VALID_SLOT(sprop, scope)) {
                 v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
                 if (VALUE_IS_FUNCTION(cx, v)) {
                     /*
                      * Great, we have a function-valued prototype property
                      * where the getter is JS_PropertyStub. The type id in
                      * pobj's scope does not evolve with changes to property
                      * values, however.
@@ -243,17 +243,17 @@ js_FillPropertyCache(JSContext *cx, JSOb
                     vword = JSVAL_OBJECT_TO_PCVAL(v);
                     break;
                 }
             }
         }
 
         /* If getting a value via a stub getter, we can cache the slot. */
         if (!(cs->format & (JOF_SET | JOF_INCDEC | JOF_FOR)) &&
-            SPROP_HAS_STUB_GETTER(sprop) &&
+            sprop->hasDefaultGetter() &&
             SPROP_HAS_VALID_SLOT(sprop, scope)) {
             /* Great, let's cache sprop's slot and use it on cache hit. */
             vword = SLOT_TO_PCVAL(sprop->slot);
         } else {
             /* Best we can do is to cache sprop (still a nice speedup). */
             vword = SPROP_TO_PCVAL(sprop);
             if (adding &&
                 sprop == scope->lastProperty() &&
@@ -2773,17 +2773,17 @@ AssertValidPropertyCacheHit(JSContext *c
         JS_ASSERT(PCVAL_TO_SPROP(entry->vword) == sprop);
         JS_ASSERT_IF(sprop->isMethod(),
                      sprop->methodValue() == LOCKED_OBJ_GET_SLOT(pobj, sprop->slot));
     } else {
         jsval v;
         JS_ASSERT(PCVAL_IS_OBJECT(entry->vword));
         JS_ASSERT(entry->vword != PCVAL_NULL);
         JS_ASSERT(OBJ_SCOPE(pobj)->brandedOrHasMethodBarrier());
-        JS_ASSERT(SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop));
+        JS_ASSERT(sprop->hasDefaultGetterOrIsMethod());
         JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
         v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
         JS_ASSERT(VALUE_IS_FUNCTION(cx, v));
         JS_ASSERT(PCVAL_TO_OBJECT(entry->vword) == JSVAL_TO_OBJECT(v));
 
         if (sprop->isMethod()) {
             JS_ASSERT(js_CodeSpec[*regs.pc].format & JOF_CALLOP);
             JS_ASSERT(sprop->methodValue() == v);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1676,17 +1676,18 @@ js_HasOwnProperty(JSContext *cx, JSLooku
              *
              * It's not really a hack, of course: a permanent property can't
              * be deleted, and JSPROP_SHARED means "don't allocate a slot in
              * any instance, prototype or delegating".  Without a slot, and
              * without the ability to remove and recreate (with differences)
              * the property, there is no way to tell whether it is directly
              * owned, or indirectly delegated.
              */
-            if (!SPROP_IS_SHARED_PERMANENT((JSScopeProperty *) *propp)) {
+            JSScopeProperty *sprop = reinterpret_cast<JSScopeProperty *>(*propp);
+            if (!SPROP_IS_SHARED_PERMANENT(sprop)) {
                 (*objp)->dropProperty(cx, *propp);
                 *propp = NULL;
             }
         } else {
             (*objp)->dropProperty(cx, *propp);
             *propp = NULL;
         }
     }
@@ -1879,17 +1880,17 @@ obj_lookupGetter(JSContext *cx, uintN ar
         return JS_FALSE;
     obj = JS_THIS_OBJECT(cx, vp);
     if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop))
         return JS_FALSE;
     *vp = JSVAL_VOID;
     if (prop) {
         if (OBJ_IS_NATIVE(pobj)) {
             sprop = (JSScopeProperty *) prop;
-            if (sprop->attrs & JSPROP_GETTER)
+            if (sprop->hasGetterValue())
                 *vp = sprop->getterValue();
         }
         pobj->dropProperty(cx, prop);
     }
     return JS_TRUE;
 }
 
 static JSBool
@@ -1904,17 +1905,17 @@ obj_lookupSetter(JSContext *cx, uintN ar
         return JS_FALSE;
     obj = JS_THIS_OBJECT(cx, vp);
     if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop))
         return JS_FALSE;
     *vp = JSVAL_VOID;
     if (prop) {
         if (OBJ_IS_NATIVE(pobj)) {
             sprop = (JSScopeProperty *) prop;
-            if (sprop->attrs & JSPROP_SETTER)
+            if (sprop->hasSetterValue())
                 *vp = sprop->setterValue();
         }
         pobj->dropProperty(cx, prop);
     }
     return JS_TRUE;
 }
 #endif /* JS_HAS_GETTER_SETTER */
 
@@ -2297,27 +2298,23 @@ DefinePropertyObject(JSContext *cx, JSOb
     JSScopeProperty *sprop = reinterpret_cast<JSScopeProperty *>(current);
     do {
         if (desc.isAccessorDescriptor()) {
             if (!sprop->isAccessorDescriptor())
                 break;
 
             if (desc.hasGet &&
                 !js_SameValue(desc.getterValue(),
-                              (sprop->attrs & JSPROP_GETTER)
-                              ? sprop->getterValue()
-                              : JSVAL_VOID, cx)) {
+                              sprop->hasGetterValue() ? sprop->getterValue() : JSVAL_VOID, cx)) {
                 break;
             }
 
             if (desc.hasSet &&
                 !js_SameValue(desc.setterValue(),
-                              (sprop->attrs & JSPROP_SETTER)
-                              ? sprop->setterValue()
-                              : JSVAL_VOID, cx)) {
+                              sprop->hasSetterValue() ? sprop->setterValue() : JSVAL_VOID, cx)) {
                 break;
             }
         } else {
             /*
              * Determine the current value of the property once, if the current
              * value might actually need to be used or preserved later.  NB: we
              * guard on whether the current property is a data descriptor to
              * avoid calling a getter; we won't need the value if it's not a
@@ -2343,17 +2340,17 @@ DefinePropertyObject(JSContext *cx, JSOb
                  * descriptors ([].length, Function.length, /regex/.lastIndex,
                  * &c.).  Longer-term perhaps we should convert such properties
                  * to use data descriptors (at which point representing a
                  * descriptor with native getter/setter as an accessor
                  * descriptor would be fine) and take a small memory hit, but
                  * for now we'll simply forbid their redefinition.
                  */
                 if (!sprop->configurable() &&
-                    (!SPROP_HAS_STUB_GETTER(sprop) || !SPROP_HAS_STUB_SETTER(sprop))) {
+                    (!sprop->hasDefaultGetter() || !sprop->hasDefaultSetter())) {
                     return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
                                   throwError, desc.id, rval);
                 }
 
                 if (!js_NativeGet(cx, obj, obj2, sprop, JSGET_NO_METHOD_BARRIER, &v)) {
                     /* current was dropped when the failure occurred. */
                     return JS_FALSE;
                 }
@@ -2419,22 +2416,21 @@ DefinePropertyObject(JSContext *cx, JSOb
             }
         }
     } else {
         /* 8.12.9 step 11. */
         JS_ASSERT(desc.isAccessorDescriptor() && sprop->isAccessorDescriptor());
         if (!sprop->configurable()) {
             if ((desc.hasSet &&
                  !js_SameValue(desc.setterValue(),
-                               (sprop->attrs & JSPROP_SETTER) ? sprop->setterValue() : JSVAL_VOID,
+                               sprop->hasSetterValue() ? sprop->setterValue() : JSVAL_VOID,
                                cx)) ||
                 (desc.hasGet &&
                  !js_SameValue(desc.getterValue(),
-                               (sprop->attrs & JSPROP_GETTER) ? sprop->getterValue() : JSVAL_VOID,
-                               cx)))
+                               sprop->hasGetterValue() ? sprop->getterValue() : JSVAL_VOID, cx)))
             {
                 return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
                               throwError, desc.id, rval);
             }
         }
     }
 
     /* 8.12.9 step 12. */
@@ -2442,31 +2438,31 @@ DefinePropertyObject(JSContext *cx, JSOb
     JSPropertyOp getter, setter;
     if (desc.isGenericDescriptor()) {
         uintN changed = 0;
         if (desc.hasConfigurable)
             changed |= JSPROP_PERMANENT;
         if (desc.hasEnumerable)
             changed |= JSPROP_ENUMERATE;
 
-        attrs = (sprop->attrs & ~changed) | (desc.attrs & changed);
+        attrs = (sprop->attributes() & ~changed) | (desc.attrs & changed);
         getter = sprop->getter();
         setter = sprop->setter();
     } else if (desc.isDataDescriptor()) {
         uintN unchanged = 0;
         if (!desc.hasConfigurable)
             unchanged |= JSPROP_PERMANENT;
         if (!desc.hasEnumerable)
             unchanged |= JSPROP_ENUMERATE;
         if (!desc.hasWritable)
             unchanged |= JSPROP_READONLY;
 
         if (desc.hasValue)
             v = desc.value;
-        attrs = (desc.attrs & ~unchanged) | (sprop->attrs & unchanged);
+        attrs = (desc.attrs & ~unchanged) | (sprop->attributes() & unchanged);
         getter = setter = JS_PropertyStub;
     } else {
         JS_ASSERT(desc.isAccessorDescriptor());
 
         /*
          * Getters and setters are just like watchpoints from an access
          * control point of view.
          */
@@ -2483,17 +2479,17 @@ DefinePropertyObject(JSContext *cx, JSOb
             changed |= JSPROP_PERMANENT;
         if (desc.hasEnumerable)
             changed |= JSPROP_ENUMERATE;
         if (desc.hasGet)
             changed |= JSPROP_GETTER | JSPROP_SHARED;
         if (desc.hasSet)
             changed |= JSPROP_SETTER | JSPROP_SHARED;
 
-        attrs = (desc.attrs & changed) | (sprop->attrs & ~changed);
+        attrs = (desc.attrs & changed) | (sprop->attributes() & ~changed);
         if (desc.hasGet)
             getter = desc.getterObject() ? desc.getter() : JS_PropertyStub;
         else
             getter = sprop->getter();
         if (desc.hasSet)
             setter = desc.setterObject() ? desc.setter() : JS_PropertyStub;
         else
             setter = sprop->setter();
@@ -4412,19 +4408,17 @@ js_DefineNativeProperty(JSContext *cx, J
          * already in obj and obj has its own (mutable) scope.  So if we are
          * defining a getter whose setter was already defined, or vice versa,
          * finish the job via js_ChangeScopePropertyAttributes, and refresh
          * the property cache line for (obj, id) to map sprop.
          */
         if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
             return JS_FALSE;
         sprop = (JSScopeProperty *) prop;
-        if (sprop &&
-            pobj == obj &&
-            (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
+        if (sprop && pobj == obj && sprop->isAccessorDescriptor()) {
             sprop = OBJ_SCOPE(obj)->changeProperty(cx, sprop, attrs,
                                                    JSPROP_GETTER | JSPROP_SETTER,
                                                    (attrs & JSPROP_GETTER)
                                                    ? getter
                                                    : sprop->getter(),
                                                    (attrs & JSPROP_SETTER)
                                                    ? setter
                                                    : sprop->setter());
@@ -4899,17 +4893,17 @@ js_NativeGet(JSContext *cx, JSObject *ob
     JS_ASSERT(OBJ_IS_NATIVE(pobj));
     JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj));
     scope = OBJ_SCOPE(pobj);
 
     slot = sprop->slot;
     *vp = (slot != SPROP_INVALID_SLOT)
           ? LOCKED_OBJ_GET_SLOT(pobj, slot)
           : JSVAL_VOID;
-    if (SPROP_HAS_STUB_GETTER(sprop))
+    if (sprop->hasDefaultGetter())
         return true;
 
     if (JS_UNLIKELY(sprop->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER)) {
         JS_ASSERT(sprop->methodValue() == *vp);
         return true;
     }
 
     sample = cx->runtime->propertyRemovals;
@@ -4950,32 +4944,32 @@ js_NativeSet(JSContext *cx, JSObject *ob
     JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj));
     scope = OBJ_SCOPE(obj);
 
     slot = sprop->slot;
     if (slot != SPROP_INVALID_SLOT) {
         OBJ_CHECK_SLOT(obj, slot);
 
         /* If sprop has a stub setter, keep scope locked and just store *vp. */
-        if (SPROP_HAS_STUB_SETTER(sprop)) {
+        if (sprop->hasDefaultSetter()) {
             if (!added && !scope->methodWriteBarrier(cx, sprop, *vp)) {
                 JS_UNLOCK_SCOPE(cx, scope);
                 return false;
             }
             LOCKED_OBJ_SET_SLOT(obj, slot, *vp);
             return true;
         }
     } else {
         /*
          * Allow API consumers to create shared properties with stub setters.
          * Such properties effectively function as data descriptors which are
          * not writable, so attempting to set such a property should do nothing
          * or throw if we're in strict mode.
          */
-        if (!(sprop->attrs & JSPROP_GETTER) && SPROP_HAS_STUB_SETTER(sprop))
+        if (!sprop->hasGetterValue() && sprop->hasDefaultSetter())
             return js_ReportGetterOnlyAssignment(cx);
     }
 
     sample = cx->runtime->propertyRemovals;
     JS_UNLOCK_SCOPE(cx, scope);
     {
         AutoScopePropertyRooter tvr(cx, sprop);
         if (!sprop->set(cx, obj, vp))
@@ -5222,31 +5216,29 @@ js_SetPropertyHelper(JSContext *cx, JSOb
     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->sealed() && (attrs & JSPROP_SHARED))) {
+        if (!sprop->writable() || (scope->sealed() && !sprop->hasSlot())) {
             JS_UNLOCK_SCOPE(cx, scope);
 
             /*
              * Here, we'll either return true or goto read_only_error, which
              * reports a strict warning or throws an error.  So we redefine
              * the |flags| local variable to be JSREPORT_* flags to pass to
              * JS_ReportErrorFlagsAndNumberUC at label read_only_error.  We
              * must likewise re-task flags further below for the other 'goto
              * read_only_error;' case.
              */
             flags = JSREPORT_ERROR;
-            if (attrs & JSPROP_READONLY) {
+            if (!sprop->writable()) {
                 if (!JS_HAS_STRICT_OPTION(cx)) {
                     /* Just return true per ECMA if not in strict mode. */
                     PCMETER((defineHow & JSDNP_CACHE_RESULT) && JS_PROPERTY_CACHE(cx).rofills++);
                     if (defineHow & JSDNP_CACHE_RESULT) {
                         JS_ASSERT_NOT_ON_TRACE(cx);
                         TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, sprop);
                     }
                     return JS_TRUE;
@@ -5257,39 +5249,38 @@ js_SetPropertyHelper(JSContext *cx, JSOb
                 }
 
                 /* Strict mode: report a read-only strict warning. */
                 flags = JSREPORT_STRICT | JSREPORT_WARNING;
             }
             goto read_only_error;
         }
 
+        attrs = sprop->attributes();
         if (pobj != obj) {
             /*
              * We found id in a prototype object: prepare to share or shadow.
              *
              * NB: Thanks to the immutable, garbage-collected property tree
              * maintained by jsscope.c in cx->runtime, we needn't worry about
              * sprop going away behind our back after we've unlocked scope.
              */
             JS_UNLOCK_SCOPE(cx, scope);
 
-            /* Don't clone a shared prototype property. */
-            if (attrs & JSPROP_SHARED) {
+            /* Don't clone a prototype property that doesn't have a slot. */
+            if (!sprop->hasSlot()) {
                 if (defineHow & JSDNP_CACHE_RESULT) {
                     JS_ASSERT_NOT_ON_TRACE(cx);
                     JSPropCacheEntry *entry;
                     entry = js_FillPropertyCache(cx, obj, 0, protoIndex, pobj, sprop, false);
                     TRACE_2(SetPropHit, entry, sprop);
                 }
 
-                if (SPROP_HAS_STUB_SETTER(sprop) &&
-                    !(sprop->attrs & JSPROP_GETTER)) {
+                if (sprop->hasDefaultSetter() && !sprop->hasGetterValue())
                     return JS_TRUE;
-                }
 
                 return sprop->set(cx, obj, vp);
             }
 
             /* Restore attrs to the ECMA default for new properties. */
             attrs = JSPROP_ENUMERATE;
 
             /*
@@ -5419,17 +5410,17 @@ js_GetAttributes(JSContext *cx, JSObject
         }
         if (!OBJ_IS_NATIVE(obj)) {
             ok = obj->getAttributes(cx, id, prop, attrsp);
             obj->dropProperty(cx, prop);
             return ok;
         }
     }
     sprop = (JSScopeProperty *)prop;
-    *attrsp = sprop->attrs;
+    *attrsp = sprop->attributes();
     if (noprop)
         obj->dropProperty(cx, prop);
     return JS_TRUE;
 }
 
 JSBool
 js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
                  uintN *attrsp)
@@ -5496,17 +5487,17 @@ js_DeleteProperty(JSContext *cx, JSObjec
          * a prototype, call the class's delProperty hook, passing rval as the
          * result parameter.
          */
         return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, ID_TO_VALUE(id),
                                                    rval);
     }
 
     sprop = (JSScopeProperty *)prop;
-    if (sprop->attrs & JSPROP_PERMANENT) {
+    if (!sprop->configurable()) {
         obj->dropProperty(cx, prop);
         *rval = JSVAL_FALSE;
         return JS_TRUE;
     }
 
     /* XXXbe called with obj locked */
     if (!obj->getClass()->delProperty(cx, obj, SPROP_USERID(sprop), rval)) {
         obj->dropProperty(cx, prop);
@@ -5550,19 +5541,17 @@ js_DefaultValue(JSContext *cx, JSObject 
                 if (pobj && OBJ_GET_CLASS(cx, pobj) == &js_StringClass) {
                     JS_UNLOCK_SCOPE(cx, scope);
                     JS_LOCK_OBJ(cx, pobj);
                     scope = OBJ_SCOPE(pobj);
                     sprop = scope->lookup(toStringId);
                 }
             }
 
-            if (sprop &&
-                SPROP_HAS_STUB_GETTER(sprop) &&
-                SPROP_HAS_VALID_SLOT(sprop, scope)) {
+            if (sprop && sprop->hasDefaultGetter() && SPROP_HAS_VALID_SLOT(sprop, scope)) {
                 jsval fval = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
 
                 if (VALUE_IS_FUNCTION(cx, fval)) {
                     JSObject *funobj = JSVAL_TO_OBJECT(fval);
                     JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
 
                     if (FUN_FAST_NATIVE(fun) == js_str_toString) {
                         JS_UNLOCK_SCOPE(cx, scope);
@@ -5933,17 +5922,17 @@ js_CheckAccess(JSContext *cx, JSObject *
                     *attrsp = 0;
                 }
                 break;
             }
             return pobj->checkAccess(cx, id, mode, vp, attrsp);
         }
 
         sprop = (JSScopeProperty *)prop;
-        *attrsp = sprop->attrs;
+        *attrsp = sprop->attributes();
         if (!writing) {
             *vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)))
                   ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
                   : JSVAL_VOID;
         }
         pobj->dropProperty(cx, prop);
     }
 
@@ -6892,17 +6881,17 @@ js_DumpId(jsid id)
     dumpValue(ID_TO_VALUE(id));
     fputc('\n', stderr);
 }
 
 static void
 dumpScopeProp(JSScopeProperty *sprop)
 {
     jsid id = sprop->id;
-    uint8 attrs = sprop->attrs;
+    uint8 attrs = sprop->attributes();
 
     fprintf(stderr, "    ");
     if (attrs & JSPROP_ENUMERATE) fprintf(stderr, "enumerate ");
     if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly ");
     if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent ");
     if (attrs & JSPROP_GETTER) fprintf(stderr, "getter ");
     if (attrs & JSPROP_SETTER) fprintf(stderr, "setter ");
     if (attrs & JSPROP_SHARED) fprintf(stderr, "shared ");
--- a/js/src/jsops.cpp
+++ b/js/src/jsops.cpp
@@ -606,33 +606,33 @@ END_CASE(JSOP_PICK)
                                                                               \
         /* Get or set the element. */                                         \
         if (!call)                                                            \
             goto error;                                                       \
     JS_END_MACRO
 
 #define NATIVE_GET(cx,obj,pobj,sprop,getHow,vp)                               \
     JS_BEGIN_MACRO                                                            \
-        if (SPROP_HAS_STUB_GETTER(sprop)) {                                   \
+        if (sprop->hasDefaultGetter()) {                                      \
             /* Fast path for Object instance properties. */                   \
             JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT ||                  \
-                      !SPROP_HAS_STUB_SETTER(sprop));                         \
+                      !sprop->hasDefaultSetter());                            \
             *vp = ((sprop)->slot != SPROP_INVALID_SLOT)                       \
                   ? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot)                  \
                   : JSVAL_VOID;                                               \
         } else {                                                              \
             if (!js_NativeGet(cx, obj, pobj, sprop, getHow, vp))              \
                 goto error;                                                   \
         }                                                                     \
     JS_END_MACRO
 
 #define NATIVE_SET(cx,obj,sprop,entry,vp)                                     \
     JS_BEGIN_MACRO                                                            \
         TRACE_2(SetPropHit, entry, sprop);                                    \
-        if (SPROP_HAS_STUB_SETTER(sprop) &&                                   \
+        if (sprop->hasDefaultSetter() &&                                      \
             (sprop)->slot != SPROP_INVALID_SLOT &&                            \
             !OBJ_SCOPE(obj)->brandedOrHasMethodBarrier()) {                   \
             /* Fast path for, e.g., plain Object instance properties. */      \
             LOCKED_OBJ_SET_SLOT(obj, (sprop)->slot, *vp);                     \
         } else {                                                              \
             if (!js_NativeSet(cx, obj, sprop, false, vp))                     \
                 goto error;                                                   \
         }                                                                     \
@@ -1710,31 +1710,30 @@ BEGIN_CASE(JSOP_SETMETHOD)
             PCMETER(cache->pctestentry = entry);
             PCMETER(cache->tests++);
             PCMETER(cache->settests++);
             if (entry->kpc == regs.pc && entry->kshape == kshape) {
                 JS_ASSERT(PCVCAP_TAG(entry->vcap) <= 1);
                 if (js_MatchPropertyCacheShape(cx, obj, kshape)) {
                     JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
                     sprop = PCVAL_TO_SPROP(entry->vword);
-                    JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
-                    JS_ASSERT_IF(!(sprop->attrs & JSPROP_SHARED),
-                                 PCVCAP_TAG(entry->vcap) == 0);
+                    JS_ASSERT(sprop->writable());
+                    JS_ASSERT_IF(sprop->hasSlot(), PCVCAP_TAG(entry->vcap) == 0);
 
                     JSScope *scope = OBJ_SCOPE(obj);
                     JS_ASSERT(!scope->sealed());
 
                     /*
                      * Fastest path: check whether the cached sprop is already
                      * in scope and call NATIVE_SET and break to get out of the
                      * do-while(0). But we can call NATIVE_SET only if obj owns
                      * scope or sprop is shared.
                      */
                     bool checkForAdd;
-                    if (sprop->attrs & JSPROP_SHARED) {
+                    if (!sprop->hasSlot()) {
                         if (PCVCAP_TAG(entry->vcap) == 0 ||
                             ((obj2 = obj->getProto()) &&
                              OBJ_IS_NATIVE(obj2) &&
                              OBJ_SHAPE(obj2) == PCVCAP_SHAPE(entry->vcap))) {
                             goto fast_set_propcache_hit;
                         }
 
                         /* The cache entry doesn't apply. vshape mismatch. */
@@ -1742,35 +1741,32 @@ BEGIN_CASE(JSOP_SETMETHOD)
                     } else if (!scope->isSharedEmpty()) {
                         if (sprop == scope->lastProperty() || scope->hasProperty(sprop)) {
                           fast_set_propcache_hit:
                             PCMETER(cache->pchits++);
                             PCMETER(cache->setpchits++);
                             NATIVE_SET(cx, obj, sprop, entry, &rval);
                             break;
                         }
-                        checkForAdd =
-                            !(sprop->attrs & JSPROP_SHARED) &&
-                            sprop->parent == scope->lastProperty();
+                        checkForAdd = sprop->hasSlot() && sprop->parent == scope->lastProperty();
                     } else {
                         /*
                          * We check that cx own obj here and will continue to
                          * own it after js_GetMutableScope returns so we can
                          * continue to skip JS_UNLOCK_OBJ calls.
                          */
                         JS_ASSERT(CX_OWNS_OBJECT_TITLE(cx, obj));
                         scope = js_GetMutableScope(cx, obj);
                         JS_ASSERT(CX_OWNS_OBJECT_TITLE(cx, obj));
                         if (!scope)
                             goto error;
                         checkForAdd = !sprop->parent;
                     }
 
-                    if (checkForAdd &&
-                        SPROP_HAS_STUB_SETTER(sprop) &&
+                    if (checkForAdd && sprop->hasDefaultSetter() &&
                         (slot = sprop->slot) == scope->freeslot) {
                         /*
                          * Fast path: adding a plain old property that was once
                          * at the frontier of the property tree, whose slot is
                          * next to claim among the allocated slots in obj,
                          * where scope->table has not been created yet.
                          *
                          * We may want to remove hazard conditions above and
@@ -1805,17 +1801,17 @@ BEGIN_CASE(JSOP_SETMETHOD)
                          * shape to have different freeslot values. This is
                          * what causes the slot != sprop->slot case. See
                          * js_GetMutableScope.)
                          */
                         if (slot != sprop->slot || scope->table) {
                             JSScopeProperty *sprop2 =
                                 scope->putProperty(cx, sprop->id,
                                                    sprop->getter(), sprop->setter(),
-                                                   slot, sprop->attrs,
+                                                   slot, sprop->attributes(),
                                                    sprop->getFlags(), sprop->shortid);
                             if (!sprop2) {
                                 js_FreeSlot(cx, obj, slot);
                                 goto error;
                             }
                             sprop = sprop2;
                         } else {
                             scope->extend(cx, sprop);
@@ -1848,17 +1844,17 @@ BEGIN_CASE(JSOP_SETMETHOD)
                 PCMETER(cache->misses++);
                 PCMETER(cache->setmisses++);
             } else {
                 ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
                 sprop = NULL;
                 if (obj == obj2) {
                     JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
                     sprop = PCVAL_TO_SPROP(entry->vword);
-                    JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
+                    JS_ASSERT(sprop->writable());
                     JS_ASSERT(!OBJ_SCOPE(obj2)->sealed());
                     NATIVE_SET(cx, obj, sprop, entry, &rval);
                 }
                 if (sprop)
                     break;
             }
         }
 
@@ -2848,20 +2844,20 @@ BEGIN_CASE(JSOP_DEFVAR)
      * and has stub getter and setter, into a "fast global" accessed
      * by the JSOP_*GVAR opcodes.
      */
     if (!fp->fun &&
         index < GlobalVarCount(fp) &&
         obj2 == obj &&
         OBJ_IS_NATIVE(obj)) {
         sprop = (JSScopeProperty *) prop;
-        if ((sprop->attrs & JSPROP_PERMANENT) &&
+        if (!sprop->configurable() &&
             SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) &&
-            SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop) &&
-            SPROP_HAS_STUB_SETTER(sprop)) {
+            sprop->hasDefaultGetterOrIsMethod() &&
+            sprop->hasDefaultSetter()) {
             /*
              * Fast globals use frame variables to map the global name's atom
              * index to the permanent varobj slot number, tagged as a jsval.
              * The atom index for the global's name literal is identical to its
              * variable index.
              */
             fp->slots[index] = INT_TO_JSVAL(sprop->slot);
         }
@@ -2991,17 +2987,17 @@ BEGIN_CASE(JSOP_DEFFUN)
      * setters that store the value of the property in the interpreter frame,
      * see bug 467495.
      */
     doSet = (attrs == JSPROP_ENUMERATE);
     JS_ASSERT_IF(doSet, fp->flags & JSFRAME_EVAL);
     if (prop) {
         if (parent == pobj &&
             OBJ_GET_CLASS(cx, parent) == &js_CallClass &&
-            (old = ((JSScopeProperty *) prop)->attrs,
+            (old = ((JSScopeProperty *) prop)->attributes(),
              !(old & (JSPROP_GETTER|JSPROP_SETTER)) &&
              (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs)) {
             /*
              * js_CheckRedeclaration must reject attempts to add a getter or
              * setter to an existing property without a getter or setter.
              */
             JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT)));
             JS_ASSERT(!(old & JSPROP_READONLY));
@@ -3405,25 +3401,25 @@ BEGIN_CASE(JSOP_INITMETHOD)
             PCVCAP_SHAPE(entry->vcap) == rt->protoHazardShape) {
             JS_ASSERT(PCVCAP_TAG(entry->vcap) == 0);
 
             PCMETER(cache->pchits++);
             PCMETER(cache->inipchits++);
 
             JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
             sprop = PCVAL_TO_SPROP(entry->vword);
-            JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
+            JS_ASSERT(sprop->writable());
 
             /*
              * If this property has a non-stub setter, it must be __proto__,
              * __parent__, or another "shared prototype" built-in. Force a miss
              * to save code size here and let the standard code path take care
              * of business.
              */
-            if (!SPROP_HAS_STUB_SETTER(sprop))
+            if (!sprop->hasDefaultSetter())
                 goto do_initprop_miss;
 
             /*
              * Detect a repeated property name and force a miss to share the
              * strict warning code and consolidate all the complexity managed
              * by JSScope::addProperty.
              */
             if (sprop->parent != scope->lastProperty())
@@ -3447,17 +3443,17 @@ BEGIN_CASE(JSOP_INITMETHOD)
                 JS_ASSERT(slot == sprop->slot);
             }
 
             JS_ASSERT(!scope->lastProperty() ||
                       scope->shape == scope->lastProperty()->shape);
             if (scope->table) {
                 JSScopeProperty *sprop2 =
                     scope->addProperty(cx, sprop->id, sprop->getter(), sprop->setter(), slot,
-                                       sprop->attrs, sprop->getFlags(), sprop->shortid);
+                                       sprop->attributes(), sprop->getFlags(), sprop->shortid);
                 if (!sprop2) {
                     js_FreeSlot(cx, obj, slot);
                     goto error;
                 }
                 JS_ASSERT(sprop2 == sprop);
             } else {
                 JS_ASSERT(!scope->isSharedEmpty());
                 scope->extend(cx, sprop);
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -562,18 +562,18 @@ struct JSScopeProperty {
 
     jsid            id;                 /* int-tagged jsval/untagged JSAtom* */
 private:
     JSPropertyOp    rawGetter;          /* getter and setter hooks or objects */
     JSPropertyOp    rawSetter;          /* getter is JSObject* and setter is 0
                                            if sprop->isMethod() */
 public:
     uint32          slot;               /* abstract index in object slots */
+private:
     uint8           attrs;              /* attributes, see jsapi.h JSPROP_* */
-private:
     uint8           flags;              /* flags, see below for defines */
 public:
     int16           shortid;            /* tinyid, or local arg/var index */
     JSScopeProperty *parent;            /* parent node, reverse for..in order */
     union {
         JSScopeProperty *kids;          /* null, single child, or a tagged ptr
                                            to many-kids data structure */
         JSScopeProperty **childp;       /* dictionary list starting at lastProp
@@ -642,61 +642,73 @@ public:
     jsval methodValue() const {
         JS_ASSERT(isMethod());
         return js_CastAsObjectJSVal(rawGetter);
     }
 
     JSPropertyOp getter() const { return rawGetter; }
     bool hasDefaultGetter() const { return !rawGetter; }
     JSPropertyOp getterOp() const {
-        JS_ASSERT(!(attrs & JSPROP_GETTER));
+        JS_ASSERT(!hasGetterValue());
         return rawGetter;
     }
     JSObject *getterObject() const {
-        JS_ASSERT(attrs & JSPROP_GETTER);
+        JS_ASSERT(hasGetterValue());
         return js_CastAsObject(rawGetter);
     }
     jsval getterValue() const {
-        JS_ASSERT(attrs & JSPROP_GETTER);
+        JS_ASSERT(hasGetterValue());
         return rawGetter ? js_CastAsObjectJSVal(rawGetter) : JSVAL_VOID;
     }
 
     JSPropertyOp setter() const { return rawSetter; }
     bool hasDefaultSetter() const { return !rawSetter; }
     JSPropertyOp setterOp() const {
-        JS_ASSERT(!(attrs & JSPROP_SETTER));
+        JS_ASSERT(!hasSetterValue());
         return rawSetter;
     }
     JSObject *setterObject() const {
-        JS_ASSERT((attrs & JSPROP_SETTER) && rawSetter);
+        JS_ASSERT(hasSetterValue() && rawSetter);
         return js_CastAsObject(rawSetter);
     }
     jsval setterValue() const {
-        JS_ASSERT(attrs & JSPROP_SETTER);
+        JS_ASSERT(hasSetterValue());
         return rawSetter ? js_CastAsObjectJSVal(rawSetter) : JSVAL_VOID;
     }
 
     inline JSDHashNumber hash() const;
     inline bool matches(const JSScopeProperty *p) const;
     inline bool matchesParamsAfterId(JSPropertyOp agetter, JSPropertyOp asetter, uint32 aslot,
                                      uintN aattrs, uintN aflags, intN ashortid) const;
 
     bool get(JSContext* cx, JSObject* obj, JSObject *pobj, jsval* vp);
     bool set(JSContext* cx, JSObject* obj, jsval* vp);
 
     void trace(JSTracer *trc);
 
-    bool configurable() { return (attrs & JSPROP_PERMANENT) == 0; }
-    bool enumerable() { return (attrs & JSPROP_ENUMERATE) != 0; }
-    bool writable() { return (attrs & JSPROP_READONLY) == 0; }
+    bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
 
-    bool isDataDescriptor() {
+    uint8 attributes() const { return attrs; }
+    bool configurable() const { return (attrs & JSPROP_PERMANENT) == 0; }
+    bool enumerable() const { return (attrs & JSPROP_ENUMERATE) != 0; }
+    bool writable() const {
+        // JS_ASSERT(isDataDescriptor());
+        return (attrs & JSPROP_READONLY) == 0;
+    }
+    bool hasGetterValue() const { return attrs & JSPROP_GETTER; }
+    bool hasSetterValue() const { return attrs & JSPROP_SETTER; }
+
+    bool hasDefaultGetterOrIsMethod() const {
+        return hasDefaultGetter() || isMethod();
+    }
+
+    bool isDataDescriptor() const {
         return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) == 0;
     }
-    bool isAccessorDescriptor() {
+    bool isAccessorDescriptor() const {
         return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) != 0;
     }
 
 #ifdef DEBUG
     void dump(JSContext *cx, FILE *fp);
     void dumpSubtree(JSContext *cx, int level, FILE *fp);
 #endif
 };
@@ -813,22 +825,16 @@ JSScope::insertDictionaryProperty(JSScop
  */
 #define SPROP_USERID(sprop)                                                   \
     ((sprop)->hasShortID() ? INT_TO_JSVAL((sprop)->shortid)                   \
                            : ID_TO_VALUE((sprop)->id))
 
 #define SLOT_IN_SCOPE(slot,scope)         ((slot) < (scope)->freeslot)
 #define SPROP_HAS_VALID_SLOT(sprop,scope) SLOT_IN_SCOPE((sprop)->slot, scope)
 
-#define SPROP_HAS_STUB_GETTER(sprop)    ((sprop)->hasDefaultGetter())
-#define SPROP_HAS_STUB_SETTER(sprop)    ((sprop)->hasDefaultSetter())
-
-#define SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop)                             \
-    (SPROP_HAS_STUB_GETTER(sprop) || (sprop)->isMethod())
-
 #ifndef JS_THREADSAFE
 # define js_GenerateShape(cx, gcLocked)    js_GenerateShape (cx)
 #endif
 
 extern uint32
 js_GenerateShape(JSContext *cx, bool gcLocked);
 
 #ifdef DEBUG
@@ -891,20 +897,20 @@ inline bool
 JSScope::canProvideEmptyScope(JSObjectOps *ops, JSClass *clasp)
 {
     return this->ops == ops && (!emptyScope || emptyScope->clasp == clasp);
 }
 
 inline bool
 JSScopeProperty::get(JSContext* cx, JSObject* obj, JSObject *pobj, jsval* vp)
 {
-    JS_ASSERT(!SPROP_HAS_STUB_GETTER(this));
     JS_ASSERT(!JSVAL_IS_NULL(this->id));
+    JS_ASSERT(!hasDefaultGetter());
 
-    if (attrs & JSPROP_GETTER) {
+    if (hasGetterValue()) {
         JS_ASSERT(!isMethod());
         jsval fval = getterValue();
         return js_InternalGetOrSet(cx, obj, id, fval, JSACC_READ, 0, 0, vp);
     }
 
     if (isMethod()) {
         *vp = methodValue();
 
@@ -922,35 +928,38 @@ JSScopeProperty::get(JSContext* cx, JSOb
     if (STOBJ_GET_CLASS(obj) == &js_WithClass)
         obj = obj->map->ops->thisObject(cx, obj);
     return getterOp()(cx, obj, SPROP_USERID(this), vp);
 }
 
 inline bool
 JSScopeProperty::set(JSContext* cx, JSObject* obj, jsval* vp)
 {
-    JS_ASSERT_IF(SPROP_HAS_STUB_SETTER(this), attrs & JSPROP_GETTER);
+    JS_ASSERT_IF(hasDefaultSetter(), hasGetterValue());
 
     if (attrs & JSPROP_SETTER) {
         jsval fval = setterValue();
         return js_InternalGetOrSet(cx, obj, id, fval, JSACC_WRITE, 1, vp, vp);
     }
 
     if (attrs & JSPROP_GETTER)
         return !!js_ReportGetterOnlyAssignment(cx);
 
     /* See the comment in JSScopeProperty::get as to why we can check for With. */
     if (STOBJ_GET_CLASS(obj) == &js_WithClass)
         obj = obj->map->ops->thisObject(cx, obj);
     return setterOp()(cx, obj, SPROP_USERID(this), vp);
 }
 
 /* Macro for common expression to test for shared permanent attributes. */
-#define SPROP_IS_SHARED_PERMANENT(sprop)                                      \
-    ((~(sprop)->attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0)
+inline bool
+SPROP_IS_SHARED_PERMANENT(JSScopeProperty *sprop)
+{
+    return !sprop->hasSlot() && !sprop->configurable();
+}
 
 extern JSScope *
 js_GetMutableScope(JSContext *cx, JSObject *obj);
 
 extern void
 js_TraceId(JSTracer *trc, jsid id);
 
 extern void
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -3492,30 +3492,30 @@ TraceRecorder::import(TreeFragment* tree
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::isValidSlot(JSScope* scope, JSScopeProperty* sprop)
 {
     uint32 setflags = (js_CodeSpec[*cx->fp->regs->pc].format & (JOF_SET | JOF_INCDEC | JOF_FOR));
 
     if (setflags) {
-        if (!SPROP_HAS_STUB_SETTER(sprop))
+        if (!sprop->hasDefaultSetter())
             RETURN_VALUE("non-stub setter", false);
-        if (sprop->attrs & JSPROP_READONLY)
+        if (!sprop->writable())
             RETURN_VALUE("writing to a read-only property", false);
     }
 
     /* This check applies even when setflags == 0. */
-    if (setflags != JOF_SET && !SPROP_HAS_STUB_GETTER(sprop)) {
+    if (setflags != JOF_SET && !sprop->hasDefaultGetter()) {
         JS_ASSERT(!sprop->isMethod());
         RETURN_VALUE("non-stub getter", false);
     }
 
     if (!SPROP_HAS_VALID_SLOT(sprop, scope))
-        RETURN_VALUE("slotless obj property", false);
+        RETURN_VALUE("invalid-slot obj property", false);
 
     return true;
 }
 
 /* Lazily import a global slot if we don't already have it in the tracker. */
 JS_REQUIRES_STACK void
 TraceRecorder::importGlobalSlot(unsigned slot)
 {
@@ -5228,18 +5228,17 @@ TraceRecorder::hasMethod(JSObject* obj, 
     if (protoIndex < 0 || !prop)
         return false;
 
     bool found = false;
     if (OBJ_IS_NATIVE(pobj)) {
         JSScope* scope = OBJ_SCOPE(pobj);
         JSScopeProperty* sprop = (JSScopeProperty*) prop;
 
-        if (SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop) &&
-            SPROP_HAS_VALID_SLOT(sprop, scope)) {
+        if (sprop->hasDefaultGetterOrIsMethod() && SPROP_HAS_VALID_SLOT(sprop, scope)) {
             jsval v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
             if (VALUE_IS_FUNCTION(cx, v)) {
                 found = true;
                 if (!scope->generic() && !scope->branded()) {
                     scope->brandingShapeChange(cx, sprop->slot, v);
                     scope->setBranded();
                 }
             }
@@ -7858,17 +7857,17 @@ TraceRecorder::scopeChainProp(JSObject* 
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::callProp(JSObject* obj, JSProperty* prop, jsid id, jsval*& vp,
                         LIns*& ins, NameResult& nr)
 {
     JSScopeProperty *sprop = (JSScopeProperty*) prop;
 
     JSOp op = JSOp(*cx->fp->regs->pc);
     uint32 setflags = (js_CodeSpec[op].format & (JOF_SET | JOF_INCDEC | JOF_FOR));
-    if (setflags && (sprop->attrs & JSPROP_READONLY))
+    if (setflags && !sprop->writable())
         RETURN_STOP("writing to a read-only property");
 
     uintN slot = uint16(sprop->shortid);
 
     vp = NULL;
     uintN upvar_slot = SPROP_INVALID_SLOT;
     JSStackFrame* cfp = (JSStackFrame*) obj->getPrivate();
     if (cfp) {
@@ -10474,18 +10473,18 @@ TraceRecorder::propagateFailureToBuiltin
                                       1));
     lir->insStorei(status_ins, lirbuf->state, (int) offsetof(InterpState, builtinStatus));
 }
 
 JS_REQUIRES_STACK void
 TraceRecorder::emitNativePropertyOp(JSScope* scope, JSScopeProperty* sprop, LIns* obj_ins,
                                     bool setflag, LIns* boxed_ins)
 {
-    JS_ASSERT(!(sprop->attrs & (setflag ? JSPROP_SETTER : JSPROP_GETTER)));
-    JS_ASSERT(setflag ? !SPROP_HAS_STUB_SETTER(sprop) : !SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop));
+    JS_ASSERT(setflag ? !sprop->hasSetterValue() : !sprop->hasGetterValue());
+    JS_ASSERT(setflag ? !sprop->hasDefaultSetter() : !sprop->hasDefaultGetterOrIsMethod());
 
     enterDeepBailCall();
 
     // It is unsafe to pass the address of an object slot as the out parameter,
     // because the getter or setter could end up resizing the object's dslots.
     // Instead, use a word of stack and root it in nativeVp.
     LIns* vp_ins = lir->insAlloc(sizeof(jsval));
     lir->insStorei(vp_ins, lirbuf->state, offsetof(InterpState, nativeVp));
@@ -11174,31 +11173,31 @@ TraceRecorder::nativeSet(JSObject* obj, 
      * If obj is the global object, there are two additional problems. We would
      * have to emit still more code to store the result in the object (not the
      * native global frame) if the setter returned successfully after
      * deep-bailing.  And we would have to cope if the run-time type of the
      * setter's return value differed from the record-time type of v, in which
      * case unboxing would fail and, having called a native setter, we could
      * not just retry the instruction in the interpreter.
      */
-    JS_ASSERT(SPROP_HAS_STUB_SETTER(sprop) || slot == SPROP_INVALID_SLOT);
+    JS_ASSERT(sprop->hasDefaultSetter() || slot == SPROP_INVALID_SLOT);
 
     // Box the value to be stored, if necessary.
     LIns* boxed_ins = NULL;
-    if (!SPROP_HAS_STUB_SETTER(sprop) || (slot != SPROP_INVALID_SLOT && obj != globalObj))
+    if (!sprop->hasDefaultSetter() || (slot != SPROP_INVALID_SLOT && obj != globalObj))
         boxed_ins = box_jsval(v, v_ins);
 
     // Call the setter, if any.
-    if (!SPROP_HAS_STUB_SETTER(sprop))
+    if (!sprop->hasDefaultSetter())
         emitNativePropertyOp(scope, sprop, obj_ins, true, boxed_ins);
 
     // Store the value, if this property has a slot.
     if (slot != SPROP_INVALID_SLOT) {
         JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, scope));
-        JS_ASSERT(!(sprop->attrs & JSPROP_SHARED));
+        JS_ASSERT(sprop->hasSlot());
         if (obj == globalObj) {
             if (!lazilyImportGlobalSlot(slot))
                 RETURN_STOP("lazy import of global slot failed");
             set(&STOBJ_GET_SLOT(obj, slot), v_ins);
         } else {
             LIns* dslots_ins = NULL;
             stobj_set_slot(obj_ins, slot, dslots_ins, boxed_ins);
         }
@@ -11218,26 +11217,26 @@ JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, 
                      0, ACC_STORE_ANY)
 
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::setProp(jsval &l, JSPropCacheEntry* entry, JSScopeProperty* sprop,
                        jsval &v, LIns*& v_ins)
 {
     if (entry == JS_NO_PROP_CACHE_FILL)
         RETURN_STOP("can't trace uncacheable property set");
-    JS_ASSERT_IF(PCVCAP_TAG(entry->vcap) >= 1, sprop->attrs & JSPROP_SHARED);
-    if (!SPROP_HAS_STUB_SETTER(sprop) && sprop->slot != SPROP_INVALID_SLOT)
+    JS_ASSERT_IF(PCVCAP_TAG(entry->vcap) >= 1, !sprop->hasSlot());
+    if (!sprop->hasDefaultSetter() && sprop->slot != SPROP_INVALID_SLOT)
         RETURN_STOP("can't trace set of property with setter and slot");
-    if (sprop->attrs & JSPROP_SETTER)
+    if (sprop->hasSetterValue())
         RETURN_STOP("can't trace JavaScript function setter");
 
     // These two cases are errors and can't be traced.
-    if (sprop->attrs & JSPROP_GETTER)
+    if (sprop->hasGetterValue())
         RETURN_STOP("can't assign to property with script getter but no setter");
-    if (sprop->attrs & JSPROP_READONLY)
+    if (!sprop->writable())
         RETURN_STOP("can't assign to readonly property");
 
     JS_ASSERT(!JSVAL_IS_PRIMITIVE(l));
     JSObject* obj = JSVAL_TO_OBJECT(l);
     LIns* obj_ins = get(&l);
     JSScope* scope = OBJ_SCOPE(obj);
 
     JS_ASSERT_IF(entry->vcap == PCVCAP_MAKE(entry->kshape, 0, 0), scope->hasProperty(sprop));
@@ -11258,17 +11257,17 @@ TraceRecorder::setProp(jsval &l, JSPropC
 
     // Guard before anything else.
     LIns* map_ins = map(obj_ins);
     CHECK_STATUS(guardNativePropertyOp(obj, map_ins));
     jsuword pcval;
     CHECK_STATUS(guardPropertyCacheHit(obj_ins, map_ins, obj, obj2, entry, pcval));
     JS_ASSERT(scope->object == obj2);
     JS_ASSERT(scope->hasProperty(sprop));
-    JS_ASSERT_IF(obj2 != obj, sprop->attrs & JSPROP_SHARED);
+    JS_ASSERT_IF(obj2 != obj, !sprop->hasSlot());
 
     /*
      * Setting a function-valued property might need to rebrand the object, so
      * we emit a call to the method write barrier. There's no need to guard on
      * this, because functions have distinct trace-type from other values and
      * branded-ness is implied by the shape, which we've already guarded on.
      */
     if (scope->brandedOrHasMethodBarrier() && VALUE_IS_FUNCTION(cx, v) && entry->directHit()) {
@@ -11279,17 +11278,17 @@ TraceRecorder::setProp(jsval &l, JSPropC
         LIns* args[] = { v_ins, INS_CONSTSPROP(sprop), obj_ins, cx_ins };
         LIns* ok_ins = lir->insCall(&MethodWriteBarrier_ci, args);
         guard(false, lir->ins_eq0(ok_ins), OOM_EXIT);
         leaveDeepBailCall();
     }
 
     // Add a property to the object if necessary.
     if (entry->adding()) {
-        JS_ASSERT(!(sprop->attrs & JSPROP_SHARED));
+        JS_ASSERT(sprop->hasSlot());
         if (obj == globalObj)
             RETURN_STOP("adding a property to the global object");
 
         LIns* args[] = { INS_CONSTSPROP(sprop), obj_ins, cx_ins };
         LIns* ok_ins = lir->insCall(&js_AddProperty_ci, args);
         guard(false, lir->ins_eq0(ok_ins), OOM_EXIT);
     }
 
@@ -11666,19 +11665,19 @@ GetPropertyWithNativeGetter(JSContext* c
     return cx->interpState->builtinStatus == 0;
 }
 JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, GetPropertyWithNativeGetter,
                      CONTEXT, OBJECT, SCOPEPROP, JSVALPTR, 0, ACC_STORE_ANY)
 
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::getPropertyWithNativeGetter(LIns* obj_ins, JSScopeProperty* sprop, jsval* outp)
 {
-    JS_ASSERT(!(sprop->attrs & JSPROP_GETTER));
+    JS_ASSERT(!sprop->hasGetterValue());
     JS_ASSERT(sprop->slot == SPROP_INVALID_SLOT);
-    JS_ASSERT(!SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop));
+    JS_ASSERT(!sprop->hasDefaultGetterOrIsMethod());
 
     // Call GetPropertyWithNativeGetter. See note in getPropertyByName about vp.
     // FIXME - We should call the getter directly. Using a builtin function for
     // now because it buys some extra asserts. See bug 508310.
     enterDeepBailCall();
     LIns* vp_ins = addName(lir->insAlloc(sizeof(jsval)), "vp");
     LIns* args[] = {vp_ins, INS_CONSTPTR(sprop), obj_ins, cx_ins};
     LIns* ok_ins = lir->insCall(&GetPropertyWithNativeGetter_ci, args);
@@ -12837,24 +12836,24 @@ TraceRecorder::propTail(JSObject* obj, L
     JSScopeProperty* sprop;
     uint32 slot;
     bool isMethod;
 
     if (PCVAL_IS_SPROP(pcval)) {
         sprop = PCVAL_TO_SPROP(pcval);
         JS_ASSERT(OBJ_SCOPE(obj2)->hasProperty(sprop));
 
-        if (setflags && !SPROP_HAS_STUB_SETTER(sprop))
+        if (setflags && !sprop->hasDefaultSetter())
             RETURN_STOP_A("non-stub setter");
-        if (setflags && (sprop->attrs & JSPROP_READONLY))
+        if (setflags && !sprop->writable())
             RETURN_STOP_A("writing to a readonly property");
-        if (!SPROP_HAS_STUB_GETTER_OR_IS_METHOD(sprop)) {
+        if (!sprop->hasDefaultGetterOrIsMethod()) {
             if (slotp)
                 RETURN_STOP_A("can't trace non-stub getter for this opcode");
-            if (sprop->attrs & JSPROP_GETTER)
+            if (sprop->hasGetterValue())
                 RETURN_STOP_A("script getter");
             if (sprop->slot == SPROP_INVALID_SLOT)
                 return InjectStatus(getPropertyWithNativeGetter(obj_ins, sprop, outp));
             return InjectStatus(getPropertyById(obj_ins, outp));
         }
         if (!SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2)))
             RETURN_STOP_A("no valid slot");
         slot = sprop->slot;