Bug 554996 - Eliminate native-ops check before testing property cache. Part 1: interpreter. r=gal.
authorJason Orendorff <jorendorff@mozilla.com>
Mon, 29 Mar 2010 10:35:16 -0500
changeset 40392 9c131032063f2979c03f14c521f7cba264b3d7f1
parent 40391 9d053a2a56cce4511af697a72b5089f1eb0efb07
child 40393 eb5999d8b46fa99a700b597e7915bc8b4a7d2436
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)
reviewersgal
bugs554996
milestone1.9.3a3pre
Bug 554996 - Eliminate native-ops check before testing property cache. Part 1: interpreter. r=gal.
js/src/jsops.cpp
js/src/jspropertycache.cpp
js/src/jspropertycacheinlines.h
--- a/js/src/jsops.cpp
+++ b/js/src/jsops.cpp
@@ -703,26 +703,23 @@ BEGIN_CASE(JSOP_BINDNAME)
          * of name = rhs, could have used global.name = rhs given a global
          * object reference, which also calls the hooks only after evaluating
          * the rhs. We desire such resolve hook equivalence between the two
          * forms.
          */
         obj = fp->scopeChain;
         if (!obj->getParent())
             break;
-        if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
-            JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
-            if (!atom) {
-                ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
-                break;
-            }
-        } else {
-            entry = NULL;
-            LOAD_ATOM(0);
+
+        JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
+        if (!atom) {
+            ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
+            break;
         }
+
         id = ATOM_TO_JSID(atom);
         obj = js_FindIdentifierBase(cx, fp->scopeChain, id);
         if (!obj)
             goto error;
     } while (0);
     PUSH_OPND(OBJECT_TO_JSVAL(obj));
 END_CASE(JSOP_BINDNAME)
 
@@ -1214,40 +1211,38 @@ BEGIN_CASE(JSOP_PROPDEC)
 BEGIN_CASE(JSOP_INCNAME)
 BEGIN_CASE(JSOP_DECNAME)
 BEGIN_CASE(JSOP_NAMEINC)
 BEGIN_CASE(JSOP_NAMEDEC)
 {
     PropertyCacheEntry *entry;
 
     obj = fp->scopeChain;
-    if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
-        JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
-        if (!atom) {
-            ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
-            if (obj == obj2 && entry->vword.isSlot()) {
-                slot = entry->vword.toSlot();
-                JS_ASSERT(slot < OBJ_SCOPE(obj)->freeslot);
-                rval = LOCKED_OBJ_GET_SLOT(obj, slot);
-                if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
+
+    JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
+    if (!atom) {
+        ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
+        if (obj == obj2 && entry->vword.isSlot()) {
+            slot = entry->vword.toSlot();
+            JS_ASSERT(slot < OBJ_SCOPE(obj)->freeslot);
+            rval = LOCKED_OBJ_GET_SLOT(obj, slot);
+            if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
+                rtmp = rval;
+                rval += (js_CodeSpec[op].format & JOF_INC) ? 2 : -2;
+                if (!(js_CodeSpec[op].format & JOF_POST))
                     rtmp = rval;
-                    rval += (js_CodeSpec[op].format & JOF_INC) ? 2 : -2;
-                    if (!(js_CodeSpec[op].format & JOF_POST))
-                        rtmp = rval;
-                    LOCKED_OBJ_SET_SLOT(obj, slot, rval);
-                    PUSH_OPND(rtmp);
-                    len = JSOP_INCNAME_LENGTH;
-                    DO_NEXT_OP(len);
-                }
+                LOCKED_OBJ_SET_SLOT(obj, slot, rval);
+                PUSH_OPND(rtmp);
+                len = JSOP_INCNAME_LENGTH;
+                DO_NEXT_OP(len);
             }
-            LOAD_ATOM(0);
         }
-    } else {
         LOAD_ATOM(0);
     }
+
     id = ATOM_TO_JSID(atom);
     if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
         goto error;
     if (!prop)
         goto atom_not_defined;
     obj2->dropProperty(cx, prop);
 }
 
@@ -1478,44 +1473,38 @@ BEGIN_CASE(JSOP_GETXPROP)
         PropertyCacheEntry *entry;
 
         /*
          * We do not impose the method read barrier if in an imacro,
          * assuming any property gets it does (e.g., for 'toString'
          * from JSOP_NEW) will not be leaked to the calling script.
          */
         aobj = js_GetProtoIfDenseArray(obj);
-        if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {
-            JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
-            if (!atom) {
-                ASSERT_VALID_PROPERTY_CACHE_HIT(i, aobj, obj2, entry);
-                if (entry->vword.isObject()) {
-                    rval = entry->vword.toJsval();
-                } else if (entry->vword.isSlot()) {
-                    slot = entry->vword.toSlot();
-                    JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
-                    rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
-                } else {
-                    JS_ASSERT(entry->vword.isSprop());
-                    sprop = entry->vword.toSprop();
-                    NATIVE_GET(cx, obj, obj2, sprop,
-                               fp->imacpc ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
-                               &rval);
-                }
-                break;
+
+        JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
+        if (!atom) {
+            ASSERT_VALID_PROPERTY_CACHE_HIT(i, aobj, obj2, entry);
+            if (entry->vword.isObject()) {
+                rval = entry->vword.toJsval();
+            } else if (entry->vword.isSlot()) {
+                slot = entry->vword.toSlot();
+                JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
+                rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
+            } else {
+                JS_ASSERT(entry->vword.isSprop());
+                sprop = entry->vword.toSprop();
+                NATIVE_GET(cx, obj, obj2, sprop,
+                           fp->imacpc ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
+                           &rval);
             }
-        } else {
-            entry = NULL;
-            if (i < 0)
-                atom = rt->atomState.lengthAtom;
-            else
-                LOAD_ATOM(i);
+            break;
         }
+
         id = ATOM_TO_JSID(atom);
-        if (entry
+        if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)
             ? !js_GetPropertyHelper(cx, obj, id,
                                     fp->imacpc
                                     ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
                                     : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER,
                                     &rval)
             : !obj->getProperty(cx, id, &rval)) {
             goto error;
         }
@@ -1574,49 +1563,45 @@ BEGIN_CASE(JSOP_CALLPROP)
             js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
             goto error;
         }
         if (!js_GetClassPrototype(cx, NULL, protoKey, &obj))
             goto error;
     }
 
     aobj = js_GetProtoIfDenseArray(obj);
-    if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {
-        JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
-        if (!atom) {
-            ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry);
-            if (entry->vword.isObject()) {
-                rval = entry->vword.toJsval();
-            } else if (entry->vword.isSlot()) {
-                slot = entry->vword.toSlot();
-                JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
-                rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
-            } else {
-                JS_ASSERT(entry->vword.isSprop());
-                sprop = entry->vword.toSprop();
-                NATIVE_GET(cx, obj, obj2, sprop, JSGET_NO_METHOD_BARRIER, &rval);
-            }
-            STORE_OPND(-1, rval);
-            PUSH_OPND(lval);
-            goto end_callprop;
+
+    JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
+    if (!atom) {
+        ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry);
+        if (entry->vword.isObject()) {
+            rval = entry->vword.toJsval();
+        } else if (entry->vword.isSlot()) {
+            slot = entry->vword.toSlot();
+            JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
+            rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
+        } else {
+            JS_ASSERT(entry->vword.isSprop());
+            sprop = entry->vword.toSprop();
+            NATIVE_GET(cx, obj, obj2, sprop, JSGET_NO_METHOD_BARRIER, &rval);
         }
-    } else {
-        entry = NULL;
-        LOAD_ATOM(0);
+        STORE_OPND(-1, rval);
+        PUSH_OPND(lval);
+        goto end_callprop;
     }
 
     /*
      * Cache miss: use the immediate atom that was loaded for us under
      * PropertyCache::test.
      */
     id = ATOM_TO_JSID(atom);
     PUSH(JSVAL_NULL);
     if (!JSVAL_IS_PRIMITIVE(lval)) {
         if (!js_GetMethod(cx, obj, id,
-                          entry
+                          JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)
                           ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
                           : JSGET_NO_METHOD_BARRIER,
                           &rval)) {
             goto error;
         }
         STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
         STORE_OPND(-2, rval);
     } else {
@@ -1666,199 +1651,195 @@ BEGIN_CASE(JSOP_SETPROP)
 BEGIN_CASE(JSOP_SETMETHOD)
     rval = FETCH_OPND(-1);
     JS_ASSERT_IF(op == JSOP_SETMETHOD, VALUE_IS_FUNCTION(cx, rval));
     lval = FETCH_OPND(-2);
     JS_ASSERT_IF(op == JSOP_SETNAME, !JSVAL_IS_PRIMITIVE(lval));
     VALUE_TO_OBJECT(cx, -2, lval, obj);
 
     do {
-        PropertyCacheEntry *entry;
-
-        entry = NULL;
+        PropertyCache *cache = &JS_PROPERTY_CACHE(cx);
+        PropertyCacheEntry *entry = NULL;
         atom = NULL;
-        if (JS_LIKELY(obj->map->ops->setProperty == js_SetProperty)) {
-            PropertyCache *cache = &JS_PROPERTY_CACHE(cx);
+
+        /*
+         * Probe the property cache, specializing for two important
+         * set-property cases. First:
+         *
+         *   function f(a, b, c) {
+         *     var o = {p:a, q:b, r:c};
+         *     return o;
+         *   }
+         *
+         * or similar real-world cases, which evolve a newborn native
+         * object predicatably through some bounded number of property
+         * additions. And second:
+         *
+         *   o.p = x;
+         *
+         * in a frequently executed method or loop body, where p will
+         * (possibly after the first iteration) always exist in native
+         * object o.
+         */
+        if (cache->testForSet(cx, regs.pc, obj, &entry, &obj2, &atom)) {
+            /*
+             * Fast property cache hit, only partially confirmed by
+             * testForSet. We know that the entry applies to regs.pc and
+             * that obj's shape matches.
+             *
+             * The entry predicts either a new property to be added
+             * directly to obj by this set, or on an existing "own"
+             * property, or on a prototype property that has a setter.
+             */
+            JS_ASSERT(entry->vword.isSprop());
+            sprop = entry->vword.toSprop();
+            JS_ASSERT(sprop->writable());
+            JS_ASSERT_IF(sprop->hasSlot(), entry->vcapTag() == 0);
+
+            JSScope *scope = OBJ_SCOPE(obj);
+            JS_ASSERT(!scope->sealed());
 
             /*
-             * Probe the property cache, specializing for two important
-             * set-property cases. First:
-             *
-             *   function f(a, b, c) {
-             *     var o = {p:a, q:b, r:c};
-             *     return o;
-             *   }
-             *
-             * or similar real-world cases, which evolve a newborn native
-             * object predicatably through some bounded number of property
-             * additions. And second:
-             *
-             *   o.p = x;
-             *
-             * in a frequently executed method or loop body, where p will
-             * (possibly after the first iteration) always exist in native
-             * object o.
+             * 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.
              */
-            if (cache->testForSet(cx, regs.pc, obj, &entry, &obj2, &atom)) {
+            bool checkForAdd;
+            if (!sprop->hasSlot()) {
+                if (entry->vcapTag() == 0 ||
+                    ((obj2 = obj->getProto()) &&
+                     OBJ_IS_NATIVE(obj2) &&
+                     OBJ_SHAPE(obj2) == entry->vshape())) {
+                    goto fast_set_propcache_hit;
+                }
+
+                /* The cache entry doesn't apply. vshape mismatch. */
+                checkForAdd = false;
+            } 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->hasSlot() && sprop->parent == scope->lastProperty();
+            } else {
                 /*
-                 * Fast property cache hit, only partially confirmed by
-                 * testForSet. We know that the entry applies to regs.pc and
-                 * that obj's shape matches.
+                 * 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 &&
+                entry->vshape() == rt->protoHazardShape &&
+                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.
                  *
-                 * The entry predicts either a new property to be added
-                 * directly to obj by this set, or on an existing "own"
-                 * property, or on a prototype property that has a setter.
+                 * We may want to remove hazard conditions above and
+                 * inline compensation code here, depending on
+                 * real-world workloads.
                  */
-                JS_ASSERT(entry->vword.isSprop());
-                sprop = entry->vword.toSprop();
-                JS_ASSERT(sprop->writable());
-                JS_ASSERT_IF(sprop->hasSlot(), entry->vcapTag() == 0);
-
-                JSScope *scope = OBJ_SCOPE(obj);
-                JS_ASSERT(!scope->sealed());
+                JS_ASSERT(!(obj->getClass()->flags &
+                            JSCLASS_SHARE_ALL_PROPERTIES));
+
+                PCMETER(cache->pchits++);
+                PCMETER(cache->addpchits++);
 
                 /*
-                 * 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.
+                 * Beware classes such as Function that use the
+                 * reserveSlots hook to allocate a number of reserved
+                 * slots that may vary with obj.
                  */
-                bool checkForAdd;
-                if (!sprop->hasSlot()) {
-                    if (entry->vcapTag() == 0 ||
-                        ((obj2 = obj->getProto()) &&
-                         OBJ_IS_NATIVE(obj2) &&
-                         OBJ_SHAPE(obj2) == entry->vshape())) {
-                        goto fast_set_propcache_hit;
+                if (slot < STOBJ_NSLOTS(obj) &&
+                    !OBJ_GET_CLASS(cx, obj)->reserveSlots) {
+                    ++scope->freeslot;
+                } else {
+                    if (!js_AllocSlot(cx, obj, &slot))
+                        goto error;
+                }
+
+                /*
+                 * If this obj's number of reserved slots differed, or
+                 * if something created a hash table for scope, we must
+                 * pay the price of JSScope::putProperty.
+                 *
+                 * (A reserveSlots hook can cause scopes of the same
+                 * 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->attributes(),
+                                           sprop->getFlags(), sprop->shortid);
+                    if (!sprop2) {
+                        js_FreeSlot(cx, obj, slot);
+                        goto error;
                     }
-
-                    /* The cache entry doesn't apply. vshape mismatch. */
-                    checkForAdd = false;
-                } 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->hasSlot() && sprop->parent == scope->lastProperty();
+                    sprop = sprop2;
                 } 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;
+                    scope->extend(cx, sprop);
                 }
 
-                if (checkForAdd &&
-                    entry->vshape() == rt->protoHazardShape &&
-                    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
-                     * inline compensation code here, depending on
-                     * real-world workloads.
-                     */
-                    JS_ASSERT(!(obj->getClass()->flags &
-                                JSCLASS_SHARE_ALL_PROPERTIES));
-
-                    PCMETER(cache->pchits++);
-                    PCMETER(cache->addpchits++);
-
-                    /*
-                     * Beware classes such as Function that use the
-                     * reserveSlots hook to allocate a number of reserved
-                     * slots that may vary with obj.
-                     */
-                    if (slot < STOBJ_NSLOTS(obj) &&
-                        !OBJ_GET_CLASS(cx, obj)->reserveSlots) {
-                        ++scope->freeslot;
-                    } else {
-                        if (!js_AllocSlot(cx, obj, &slot))
-                            goto error;
-                    }
-
-                    /*
-                     * If this obj's number of reserved slots differed, or
-                     * if something created a hash table for scope, we must
-                     * pay the price of JSScope::putProperty.
-                     *
-                     * (A reserveSlots hook can cause scopes of the same
-                     * 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->attributes(),
-                                               sprop->getFlags(), sprop->shortid);
-                        if (!sprop2) {
-                            js_FreeSlot(cx, obj, slot);
-                            goto error;
-                        }
-                        sprop = sprop2;
-                    } else {
-                        scope->extend(cx, sprop);
-                    }
-
-                    /*
-                     * No method change check here because here we are
-                     * adding a new property, not updating an existing
-                     * slot's value that might contain a method of a
-                     * branded scope.
-                     */
-                    TRACE_2(SetPropHit, entry, sprop);
-                    LOCKED_OBJ_SET_SLOT(obj, slot, rval);
-
-                    /*
-                     * Purge the property cache of the id we may have just
-                     * shadowed in obj's scope and proto chains. We do this
-                     * after unlocking obj's scope to avoid lock nesting.
-                     */
-                    js_PurgeScopeChain(cx, obj, sprop->id);
-                    break;
-                }
-                PCMETER(cache->setpcmisses++);
-                atom = NULL;
-            } else if (!atom) {
+                /*
+                 * No method change check here because here we are
+                 * adding a new property, not updating an existing
+                 * slot's value that might contain a method of a
+                 * branded scope.
+                 */
+                TRACE_2(SetPropHit, entry, sprop);
+                LOCKED_OBJ_SET_SLOT(obj, slot, rval);
+
                 /*
-                 * Slower property cache hit, fully confirmed by testForSet (in
-                 * the slow path, via fullTest).
+                 * Purge the property cache of the id we may have just
+                 * shadowed in obj's scope and proto chains. We do this
+                 * after unlocking obj's scope to avoid lock nesting.
                  */
-                ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
-                sprop = NULL;
-                if (obj == obj2) {
-                    sprop = entry->vword.toSprop();
-                    JS_ASSERT(sprop->writable());
-                    JS_ASSERT(!OBJ_SCOPE(obj2)->sealed());
-                    NATIVE_SET(cx, obj, sprop, entry, &rval);
-                }
-                if (sprop)
-                    break;
+                js_PurgeScopeChain(cx, obj, sprop->id);
+                break;
             }
+            PCMETER(cache->setpcmisses++);
+            atom = NULL;
+        } else if (!atom) {
+            /*
+             * Slower property cache hit, fully confirmed by testForSet (in
+             * the slow path, via fullTest).
+             */
+            ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
+            sprop = NULL;
+            if (obj == obj2) {
+                sprop = entry->vword.toSprop();
+                JS_ASSERT(sprop->writable());
+                JS_ASSERT(!OBJ_SCOPE(obj2)->sealed());
+                NATIVE_SET(cx, obj, sprop, entry, &rval);
+            }
+            if (sprop)
+                break;
         }
 
         if (!atom)
             LOAD_ATOM(0);
         id = ATOM_TO_JSID(atom);
-        if (entry) {
+        if (entry && JS_LIKELY(obj->map->ops->setProperty == js_SetProperty)) {
             uintN defineHow = (op == JSOP_SETMETHOD)
                               ? JSDNP_CACHE_RESULT | JSDNP_SET_METHOD
                               : JSDNP_CACHE_RESULT;
             if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rval))
                 goto error;
         } else {
             if (!obj->setProperty(cx, id, &rval))
                 goto error;
@@ -2282,38 +2263,35 @@ BEGIN_CASE(JSOP_SETCALL)
 END_CASE(JSOP_SETCALL)
 
 BEGIN_CASE(JSOP_NAME)
 BEGIN_CASE(JSOP_CALLNAME)
 {
     PropertyCacheEntry *entry;
 
     obj = fp->scopeChain;
-    if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
-        JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
-        if (!atom) {
-            ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
-            if (entry->vword.isObject()) {
-                rval = entry->vword.toJsval();
-                goto do_push_rval;
-            }
-
-            if (entry->vword.isSlot()) {
-                slot = entry->vword.toSlot();
-                JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
-                rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
-                goto do_push_rval;
-            }
-
-            JS_ASSERT(entry->vword.isSprop());
-            sprop = entry->vword.toSprop();
-            goto do_native_get;
+
+    JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
+    if (!atom) {
+        ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
+        if (entry->vword.isObject()) {
+            rval = entry->vword.toJsval();
+            goto do_push_rval;
         }
-    } else {
-        LOAD_ATOM(0);
+
+        if (entry->vword.isSlot()) {
+            slot = entry->vword.toSlot();
+            JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
+            rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
+            goto do_push_rval;
+        }
+
+        JS_ASSERT(entry->vword.isSprop());
+        sprop = entry->vword.toSprop();
+        goto do_native_get;
     }
 
     id = ATOM_TO_JSID(atom);
     if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
         goto error;
     if (!prop) {
         /* Kludge to allow (typeof foo == "undefined") tests. */
         endpc = script->code + script->length;
--- a/js/src/jspropertycache.cpp
+++ b/js/src/jspropertycache.cpp
@@ -329,17 +329,16 @@ PropertyCache::fullTest(JSContext *cx, j
     JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
     JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code)
               < cx->fp->script->length);
 
     JSOp op = js_GetOpcode(cx, cx->fp->script, pc);
     const JSCodeSpec &cs = js_CodeSpec[op];
 
     obj = *objp;
-    JS_ASSERT(OBJ_IS_NATIVE(obj));
     vcap = entry->vcap;
 
     if (entry->kpc != pc) {
         PCMETER(kpcmisses++);
 
         JSAtom *atom = GetAtomFromBytecode(cx, pc, op, cs);
 #ifdef DEBUG_notme
         fprintf(stderr,
@@ -355,17 +354,17 @@ PropertyCache::fullTest(JSContext *cx, j
                 js_Disassemble1(cx, cx->fp->script, pc,
                                 pc - cx->fp->script->code,
                                 JS_FALSE, stderr);
 #endif
 
         return atom;
     }
 
-    if (entry->kshape != OBJ_SHAPE(obj)) {
+    if (entry->kshape != obj->map->shape) {
         PCMETER(kshapemisses++);
         return GetAtomFromBytecode(cx, pc, op, cs);
     }
 
     /*
      * PropertyCache::test handles only the direct and immediate-prototype hit
      * cases. All others go here. We could embed the target object in the cache
      * entry but then entry size would be 5 words. Instead we traverse chains.
--- a/js/src/jspropertycacheinlines.h
+++ b/js/src/jspropertycacheinlines.h
@@ -68,23 +68,23 @@ PropertyCache::matchShape(JSContext *cx,
  * be deleting a property from its scope, or otherwise invalidating property
  * caches (on all threads) by re-generating scope->shape.
  */
 JS_ALWAYS_INLINE void
 PropertyCache::test(JSContext *cx, jsbytecode *pc, JSObject *&obj,
                     JSObject *&pobj, PropertyCacheEntry *&entry, JSAtom *&atom)
 {
     JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
-    JS_ASSERT(OBJ_IS_NATIVE(obj));
 
-    uint32 kshape = OBJ_SHAPE(obj);
+    uint32 kshape = obj->map->shape;
     entry = &table[hash(pc, kshape)];
     PCMETER(pctestentry = entry);
     PCMETER(tests++);
     JS_ASSERT(&obj != &pobj);
+    JS_ASSERT(entry->kshape < SHAPE_OVERFLOW_BIT);
     if (entry->kpc == pc && entry->kshape == kshape) {
         JSObject *tmp;
         pobj = obj;
         if (entry->vcapTag() == 1 &&
             (tmp = pobj->getProto()) != NULL) {
             pobj = tmp;
         }
 
@@ -99,22 +99,23 @@ PropertyCache::test(JSContext *cx, jsbyt
     if (atom)
         PCMETER(misses++);
 }
 
 JS_ALWAYS_INLINE bool
 PropertyCache::testForSet(JSContext *cx, jsbytecode *pc, JSObject *obj,
                           PropertyCacheEntry **entryp, JSObject **obj2p, JSAtom **atomp)
 {
-    uint32 shape = OBJ_SHAPE(obj);
+    uint32 shape = obj->map->shape;
     PropertyCacheEntry *entry = &table[hash(pc, shape)];
     *entryp = entry;
     PCMETER(pctestentry = entry);
     PCMETER(tests++);
     PCMETER(settests++);
+    JS_ASSERT(entry->kshape < SHAPE_OVERFLOW_BIT);
     if (entry->kpc == pc && entry->kshape == shape && matchShape(cx, obj, shape))
         return true;
 
 #ifdef DEBUG
     JSObject *orig = obj;
 #endif
     JSAtom *atom = fullTest(cx, pc, &obj, obj2p, entry);
     if (atom) {
@@ -134,16 +135,17 @@ PropertyCache::testForInit(JSRuntime *rt
     JS_ASSERT(scope->object == obj);
     JS_ASSERT(!scope->sealed());
     uint32 kshape = scope->shape;
     PropertyCacheEntry *entry = &table[hash(pc, kshape)];
     *entryp = entry;
     PCMETER(pctestentry = entry);
     PCMETER(tests++);
     PCMETER(initests++);
+    JS_ASSERT(entry->kshape < SHAPE_OVERFLOW_BIT);
 
     if (entry->kpc == pc &&
         entry->kshape == kshape &&
         entry->vshape() == rt->protoHazardShape) {
         PCMETER(pchits++);
         PCMETER(inipchits++);
         JS_ASSERT(entry->vcapTag() == 0);
         *spropp = entry->vword.toSprop();