Bug 704356 - Remove the JS property cache. r=njn, sr=luke
authorJan de Mooij <jdemooij@mozilla.com>
Tue, 18 Jun 2013 09:16:57 +0200
changeset 147762 d009ce8e199efa47cc420caa1afbf6ec31540a70
parent 147761 a0f434c4d17c2f91f9cbd029e1b09cb3145fd81f
child 147763 c86d40e96c89a85988911736c2407e2968668f60
push idunknown
push userunknown
push dateunknown
reviewersnjn, luke
bugs704356
milestone24.0a1
Bug 704356 - Remove the JS property cache. r=njn, sr=luke
js/src/jscntxt.h
js/src/jsgc.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsopcode.h
js/src/jsopcodeinlines.h
js/src/jspropertycache.cpp
js/src/jspropertycache.h
js/src/jspropertycacheinlines.h
js/src/moz.build
js/src/vm/Interpreter-inl.h
js/src/vm/Interpreter.cpp
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -17,17 +17,16 @@
 #include <string.h>
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "jsprvtd.h"
 #include "jsatom.h"
 #include "jsclist.h"
 #include "jsgc.h"
-#include "jspropertycache.h"
 #include "jspropertytree.h"
 #include "jsprototypes.h"
 #include "jsutil.h"
 #include "prmjtime.h"
 
 #include "ds/LifoAlloc.h"
 #include "frontend/ParseMaps.h"
 #include "gc/Nursery.h"
@@ -1257,17 +1256,16 @@ struct JSRuntime : public JS::shadow::Ru
     js::MathCache *mathCache_;
     js::MathCache *createMathCache(JSContext *cx);
   public:
     js::MathCache *getMathCache(JSContext *cx) {
         return mathCache_ ? mathCache_ : createMathCache(cx);
     }
 
     js::GSNCache        gsnCache;
-    js::PropertyCache   propertyCache;
     js::NewObjectCache  newObjectCache;
     js::NativeIterCache nativeIterCache;
     js::SourceDataCache sourceDataCache;
     js::EvalCache       evalCache;
 
     /* State used by jsdtoa.cpp. */
     DtoaState           *dtoaState;
 
@@ -1719,18 +1717,16 @@ struct JSContext : js::ContextFriendFiel
     bool hasWErrorOption() const { return hasOption(JSOPTION_WERROR); }
 
     js::LifoAlloc &tempLifoAlloc() { return runtime()->tempLifoAlloc; }
     inline js::LifoAlloc &analysisLifoAlloc();
     inline js::LifoAlloc &typeLifoAlloc();
 
     inline js::PropertyTree &propertyTree();
 
-    js::PropertyCache &propertyCache() { return runtime()->propertyCache; }
-
 #ifdef JS_THREADSAFE
     unsigned            outstandingRequests;/* number of JS_BeginRequest calls
                                                without the corresponding
                                                JS_EndRequest. */
 #endif
 
     /* Stored here to avoid passing it around as a parameter. */
     unsigned               resolveFlags;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2566,17 +2566,16 @@ static void
 PurgeRuntime(JSRuntime *rt)
 {
     for (GCCompartmentsIter comp(rt); !comp.done(); comp.next())
         comp->purge();
 
     rt->freeLifoAlloc.transferUnusedFrom(&rt->tempLifoAlloc);
 
     rt->gsnCache.purge();
-    rt->propertyCache.purge(rt);
     rt->newObjectCache.purge();
     rt->nativeIterCache.purge();
     rt->sourceDataCache.purge();
     rt->evalCache.clear();
 
     if (!rt->activeCompilations)
         rt->parseMapPool.purgeAll();
 }
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3403,18 +3403,17 @@ DefinePropertyOrElement(JSContext *cx, H
     return true;
 }
 
 bool
 js::DefineNativeProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
                          PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
                          unsigned flags, int shortid, unsigned defineHow /* = 0 */)
 {
-    JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_DONT_PURGE |
-                             DNP_SKIP_TYPE)) == 0);
+    JS_ASSERT((defineHow & ~(DNP_DONT_PURGE | DNP_SKIP_TYPE)) == 0);
     JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
 
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     /*
      * If defining a getter or setter, we must check for its counterpart and
      * update the attributes and property ops.  A getter or setter is really
      * only half of a property.
@@ -3998,19 +3997,16 @@ GetPropertyHelperInline(JSContext *cx,
                : JSObject::getGeneric(cx, obj2Handle, obj2Handle, idHandle, vpHandle);
     }
 
     if (IsImplicitDenseElement(shape)) {
         vp.set(obj2->getDenseElement(JSID_TO_INT(id)));
         return true;
     }
 
-    if (getHow & JSGET_CACHE_RESULT)
-        cx->propertyCache().fill(cx, obj, obj2, shape);
-
     /* This call site is hot -- use the always-inlined variant of js_NativeGet(). */
     if (!NativeGetInline<allowGC>(cx, obj, receiver, obj2, shape, getHow, vp))
         return JS_FALSE;
 
     return JS_TRUE;
 }
 
 bool
@@ -4267,17 +4263,17 @@ JSObject::callMethod(JSContext *cx, Hand
         return false;
     return Invoke(cx, ObjectValue(*obj), fval, argc, argv, vp.address());
 }
 
 JSBool
 baseops::SetPropertyHelper(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id,
                            unsigned defineHow, MutableHandleValue vp, JSBool strict)
 {
-    JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_UNQUALIFIED)) == 0);
+    JS_ASSERT((defineHow & ~DNP_UNQUALIFIED) == 0);
 
     if (JS_UNLIKELY(obj->watched())) {
         /* Fire watchpoints, if any. */
         WatchpointMap *wpmap = cx->compartment()->watchpointMap;
         if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
             return false;
     }
 
@@ -4355,19 +4351,16 @@ baseops::SetPropertyHelper(JSContext *cx
         }
 
         attrs = shape->attributes();
         if (pobj != obj) {
             /*
              * We found id in a prototype object: prepare to share or shadow.
              */
             if (!shape->shadowable()) {
-                if (defineHow & DNP_CACHE_RESULT)
-                    cx->propertyCache().fill(cx, obj, pobj, shape);
-
                 if (shape->hasDefaultSetter() && !shape->hasGetterValue())
                     return JS_TRUE;
 
                 return shape->set(cx, obj, receiver, strict, vp);
             }
 
             /*
              * Preserve attrs except JSPROP_SHARED, getter, and setter when
@@ -4436,19 +4429,16 @@ baseops::SetPropertyHelper(JSContext *cx
 
         if (getter == JS_PropertyStub)
             AddTypePropertyId(cx, obj, id, vp);
 
         return DefinePropertyOrElement(cx, obj, id, getter, setter,
                                        attrs, flags, shortid, vp, true, strict);
     }
 
-    if (defineHow & DNP_CACHE_RESULT)
-        cx->propertyCache().fill(cx, obj, obj, shape);
-
     return js_NativeSet(cx, obj, receiver, shape, strict, vp);
 }
 
 JSBool
 baseops::SetElementHelper(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index,
                           unsigned defineHow, MutableHandleValue vp, JSBool strict)
 {
     RootedId id(cx);
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1195,22 +1195,21 @@ extern JSObject *
 CreateThis(JSContext *cx, js::Class *clasp, js::HandleObject callee);
 
 extern JSObject *
 CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto, HandleObject parent);
 
 /*
  * Flags for the defineHow parameter of js_DefineNativeProperty.
  */
-const unsigned DNP_CACHE_RESULT = 1;   /* an interpreter call from JSOP_INITPROP */
-const unsigned DNP_DONT_PURGE   = 2;   /* suppress js_PurgeScopeChain */
-const unsigned DNP_UNQUALIFIED  = 4;   /* Unqualified property set.  Only used in
+const unsigned DNP_DONT_PURGE   = 1;   /* suppress js_PurgeScopeChain */
+const unsigned DNP_UNQUALIFIED  = 2;   /* Unqualified property set.  Only used in
                                        the defineHow argument of
                                        js_SetPropertyHelper. */
-const unsigned DNP_SKIP_TYPE    = 8;   /* Don't update type information */
+const unsigned DNP_SKIP_TYPE    = 4;   /* Don't update type information */
 
 /*
  * Return successfully added or changed shape or NULL on error.
  */
 extern bool
 DefineNativeProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
                      PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
                      unsigned flags, int shortid, unsigned defineHow = 0);
@@ -1287,19 +1286,16 @@ extern bool
 LookupNameWithGlobalDefault(JSContext *cx, HandlePropertyName name, HandleObject scopeChain,
                             MutableHandleObject objp);
 
 }
 
 extern JSObject *
 js_FindVariableScope(JSContext *cx, JSFunction **funp);
 
-/* JSGET_CACHE_RESULT is the analogue of DNP_CACHE_RESULT. */
-const unsigned JSGET_CACHE_RESULT = 1; // from a caching interpreter opcode
-
 /*
  * NB: js_NativeGet and js_NativeSet are called with the scope containing shape
  * (pobj's scope for Get, obj's for Set) locked, and on successful return, that
  * scope is again locked.  But on failure, both functions return false with the
  * scope containing shape unlocked.
  */
 extern JSBool
 js_NativeGet(JSContext *cx, js::Handle<JSObject*> obj, js::Handle<JSObject*> pobj,
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -228,30 +228,16 @@ extern const char       js_EscapeMap[];
 /*
  * Return a GC'ed string containing the chars in str, with any non-printing
  * chars or quotes (' or " as specified by the quote argument) escaped, and
  * with the quote character at the beginning and end of the result string.
  */
 extern JSString *
 js_QuoteString(JSContext *cx, JSString *str, jschar quote);
 
-#define GET_ATOM_FROM_BYTECODE(script, pc, pcoff, atom)                       \
-    JS_BEGIN_MACRO                                                            \
-        JS_ASSERT(js_CodeSpec[*(pc)].format & JOF_ATOM);                      \
-        (atom) = (script)->getAtom(GET_UINT32_INDEX((pc) + (pcoff)));         \
-    JS_END_MACRO
-
-#define GET_NAME_FROM_BYTECODE(script, pc, pcoff, name)                       \
-    JS_BEGIN_MACRO                                                            \
-        JSAtom *atom_;                                                        \
-        GET_ATOM_FROM_BYTECODE(script, pc, pcoff, atom_);                     \
-        JS_ASSERT(js_CodeSpec[*(pc)].format & (JOF_NAME | JOF_PROP));         \
-        (name) = atom_->asPropertyName();                                     \
-    JS_END_MACRO
-
 namespace js {
 
 extern unsigned
 StackUses(JSScript *script, jsbytecode *pc);
 
 extern unsigned
 StackDefs(JSScript *script, jsbytecode *pc);
 
--- a/js/src/jsopcodeinlines.h
+++ b/js/src/jsopcodeinlines.h
@@ -80,32 +80,16 @@ BytecodeFallsThrough(JSOp op)
       case JSOP_GOSUB:
         /* These fall through indirectly, after executing a 'finally'. */
         return true;
       default:
         return true;
     }
 }
 
-static inline PropertyName *
-GetNameFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op)
-{
-    if (op == JSOP_LENGTH)
-        return cx->names().length;
-
-    // The method JIT's implementation of instanceof contains an internal lookup
-    // of the prototype property.
-    if (op == JSOP_INSTANCEOF)
-        return cx->names().classPrototype;
-
-    PropertyName *name;
-    GET_NAME_FROM_BYTECODE(script, pc, 0, name);
-    return name;
-}
-
 class BytecodeRange {
   public:
     BytecodeRange(JSContext *cx, JSScript *script)
       : script(cx, script), pc(script->code), end(pc + script->length) {}
     bool empty() const { return pc == end; }
     jsbytecode *frontPC() const { return pc; }
     JSOp frontOpcode() const { return JSOp(*pc); }
     size_t frontOffset() const { return pc - script->code; }
deleted file mode 100644
--- a/js/src/jspropertycache.cpp
+++ /dev/null
@@ -1,277 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "jspropertycache.h"
-
-#include "mozilla/PodOperations.h"
-
-#include "jscntxt.h"
-
-#include "jsobjinlines.h"
-#include "jsopcodeinlines.h"
-
-using namespace js;
-
-using mozilla::PodArrayZero;
-
-PropertyCacheEntry *
-PropertyCache::fill(JSContext *cx, JSObject *obj, JSObject *pobj, Shape *shape)
-{
-    JS_ASSERT(this == &cx->propertyCache());
-    JS_ASSERT(!cx->runtime()->isHeapBusy());
-
-    /*
-     * Don't cache entries on indexed properties. Indexes can be added or
-     * deleted from the dense elements of objects along the prototype chain
-     * wihout any shape changes.
-     */
-    if (JSID_IS_INT(shape->propid()))
-        return JS_NO_PROP_CACHE_FILL;
-
-    /*
-     * Check for overdeep scope and prototype chain. Because resolve, getter,
-     * and setter hooks can change the prototype chain using JS_SetPrototype
-     * after LookupPropertyWithFlags has returned, we calculate the protoIndex
-     * here and not in LookupPropertyWithFlags.
-     */
-
-    JSObject *tmp = obj;
-    unsigned protoIndex = 0;
-    while (tmp != pobj) {
-        /*
-         * Don't cache entries across prototype lookups which can mutate in
-         * arbitrary ways without a shape change.
-         */
-        if (tmp->hasUncacheableProto()) {
-            PCMETER(noprotos++);
-            return JS_NO_PROP_CACHE_FILL;
-        }
-
-        tmp = tmp->getProto();
-
-        /*
-         * We cannot cache properties coming from native objects behind
-         * non-native ones on the prototype chain. The non-natives can
-         * mutate in arbitrary way without changing any shapes.
-         */
-        if (!tmp || !tmp->isNative()) {
-            PCMETER(noprotos++);
-            return JS_NO_PROP_CACHE_FILL;
-        }
-        ++protoIndex;
-    }
-
-    typedef PropertyCacheEntry Entry;
-    if (protoIndex > Entry::MaxProtoIndex) {
-        PCMETER(longchains++);
-        return JS_NO_PROP_CACHE_FILL;
-    }
-
-    /*
-     * Optimize the cached vword based on our parameters and the current pc's
-     * opcode format flags.
-     */
-    jsbytecode *pc;
-    (void) cx->stack.currentScript(&pc);
-    JSOp op = JSOp(*pc);
-    const JSCodeSpec *cs = &js_CodeSpec[op];
-
-    if ((cs->format & JOF_SET) && obj->watched())
-        return JS_NO_PROP_CACHE_FILL;
-
-    if (obj == pobj) {
-        JS_ASSERT(protoIndex == 0);
-    } else {
-        JS_ASSERT(protoIndex != 0);
-        JS_ASSERT((protoIndex == 1) == (obj->getProto() == pobj));
-
-        if (protoIndex != 1) {
-            /*
-             * Make sure that a later shadowing assignment will enter
-             * PurgeProtoChain and invalidate this entry, bug 479198.
-             */
-            if (!obj->isDelegate())
-                return JS_NO_PROP_CACHE_FILL;
-        }
-    }
-
-    PropertyCacheEntry *entry = &table[hash(pc, obj->lastProperty())];
-    PCMETER(entry->vword.isNull() || recycles++);
-    entry->assign(pc, obj->lastProperty(), pobj->lastProperty(), shape, protoIndex);
-
-    empty = false;
-    PCMETER(fills++);
-
-    /*
-     * The modfills counter is not exact. It increases if a getter or setter
-     * recurse into the interpreter.
-     */
-    PCMETER(entry == pctestentry || modfills++);
-    PCMETER(pctestentry = NULL);
-    return entry;
-}
-
-PropertyName *
-PropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject **pobjp,
-                        PropertyCacheEntry *entry)
-{
-    JSObject *obj, *pobj;
-    RootedScript script(cx, cx->stack.currentScript());
-
-    JS_ASSERT(this == &cx->propertyCache());
-    JS_ASSERT(uint32_t(pc - script->code) < script->length);
-
-    JSOp op = JSOp(*pc);
-
-    obj = *objp;
-
-    if (entry->kpc != pc) {
-        PCMETER(kpcmisses++);
-
-        PropertyName *name = GetNameFromBytecode(cx, script, pc, op);
-#ifdef DEBUG_notme
-        JSAutoByteString printable;
-        fprintf(stderr,
-                "id miss for %s from %s:%u"
-                " (pc %u, kpc %u, kshape %p, shape %p)\n",
-                js_AtomToPrintableString(cx, name, &printable),
-                script->filename,
-                js_PCToLineNumber(cx, script, pc),
-                pc - script->code,
-                entry->kpc - script->code,
-                entry->kshape,
-                obj->lastProperty());
-                js_Disassemble1(cx, script, pc,
-                                pc - script->code,
-                                JS_FALSE, stderr);
-#endif
-
-        return name;
-    }
-
-    if (entry->kshape != obj->lastProperty()) {
-        PCMETER(kshapemisses++);
-        return GetNameFromBytecode(cx, script, pc, op);
-    }
-
-    /*
-     * PropertyCache::test handles only the direct and immediate-prototype hit
-     * cases. All others go here.
-     */
-    pobj = obj;
-
-    uint8_t protoIndex = entry->protoIndex;
-    while (protoIndex > 0) {
-        JSObject *tmp = pobj->getProto();
-        if (!tmp || !tmp->isNative())
-            break;
-        pobj = tmp;
-        protoIndex--;
-    }
-
-    if (pobj->lastProperty() == entry->pshape) {
-#ifdef DEBUG
-        Rooted<PropertyName*> name(cx, GetNameFromBytecode(cx, script, pc, op));
-        JS_ASSERT(pobj->nativeContains(cx, name));
-#endif
-        *pobjp = pobj;
-        return NULL;
-    }
-
-    PCMETER(vcapmisses++);
-    return GetNameFromBytecode(cx, script, pc, op);
-}
-
-#ifdef DEBUG
-void
-PropertyCache::assertEmpty()
-{
-    JS_ASSERT(empty);
-    for (unsigned i = 0; i < SIZE; i++) {
-        JS_ASSERT(!table[i].kpc);
-        JS_ASSERT(!table[i].kshape);
-        JS_ASSERT(!table[i].pshape);
-        JS_ASSERT(!table[i].prop);
-        JS_ASSERT(!table[i].protoIndex);
-    }
-}
-#endif
-
-void
-PropertyCache::purge(JSRuntime *rt)
-{
-    if (empty) {
-        assertEmpty();
-        return;
-    }
-
-    PodArrayZero(table);
-    empty = true;
-
-#ifdef JS_PROPERTY_CACHE_METERING
-  { static FILE *fp;
-    if (!fp)
-        fp = fopen("/tmp/propcache.stats", "w");
-    if (fp) {
-        fputs("Property cache stats for ", fp);
-        fprintf(fp, "GC %lu\n", (unsigned long)rt->gcNumber);
-
-# define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)mem)
-        P(fills);
-        P(nofills);
-        P(rofills);
-        P(disfills);
-        P(oddfills);
-        P(add2dictfills);
-        P(modfills);
-        P(brandfills);
-        P(noprotos);
-        P(longchains);
-        P(recycles);
-        P(tests);
-        P(pchits);
-        P(protopchits);
-        P(initests);
-        P(inipchits);
-        P(inipcmisses);
-        P(settests);
-        P(addpchits);
-        P(setpchits);
-        P(setpcmisses);
-        P(setmisses);
-        P(kpcmisses);
-        P(kshapemisses);
-        P(vcapmisses);
-        P(misses);
-        P(flushes);
-        P(pcpurges);
-# undef P
-
-        fprintf(fp, "hit rates: pc %g%% (proto %g%%), set %g%%, ini %g%%, full %g%%\n",
-                (100. * pchits) / tests,
-                (100. * protopchits) / tests,
-                (100. * (addpchits + setpchits))
-                / settests,
-                (100. * inipchits) / initests,
-                (100. * (tests - misses)) / tests);
-        fflush(fp);
-    }
-  }
-#endif
-
-    PCMETER(flushes++);
-}
-
-void
-PropertyCache::restore(PropertyCacheEntry *entry)
-{
-    PropertyCacheEntry *entry2;
-
-    empty = false;
-
-    entry2 = &table[hash(entry->kpc, entry->kshape)];
-    *entry2 = *entry;
-}
deleted file mode 100644
--- a/js/src/jspropertycache.h
+++ /dev/null
@@ -1,191 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef jspropertycache_h___
-#define jspropertycache_h___
-
-#include "mozilla/PodOperations.h"
-
-#include "jsapi.h"
-#include "jsprvtd.h"
-#include "jstypes.h"
-
-#include "vm/String.h"
-
-namespace js {
-
-/*
- * Property cache with structurally typed capabilities for invalidation, for
- * polymorphic callsite method/get/set speedups.  For details, see
- * <https://developer.mozilla.org/en/SpiderMonkey/Internals/Property_cache>.
- */
-class PropertyCache;
-
-struct PropertyCacheEntry
-{
-    jsbytecode    *kpc;           /* pc of cache-testing bytecode */
-    Shape         *kshape;        /* shape of direct (key) object */
-    Shape         *pshape;        /* shape of owning object */
-    Shape         *prop;          /* shape of accessed property */
-
-    friend class PropertyCache;
-
-  private:
-    /* Index into the prototype chain from the object for this entry. */
-    uint8_t       protoIndex;
-
-  public:
-    static const size_t MaxProtoIndex = 15;
-
-    /*
-     * True iff the property lookup will find an own property on the object if
-     * the entry matches.
-     *
-     * This test is applicable only to property lookups, not to identifier
-     * lookups.  It is meaningless to ask this question of an entry for an
-     * identifier lookup.
-     */
-    bool isOwnPropertyHit() const { return protoIndex == 0; }
-
-    /*
-     * True iff the property lookup will find the property on the prototype of
-     * the object if the entry matches.
-     *
-     * This test is applicable only to property lookups, not to identifier
-     * lookups.  It is meaningless to ask this question of an entry for an
-     * identifier lookup.
-     */
-    bool isPrototypePropertyHit() const { return protoIndex == 1; }
-
-    void assign(jsbytecode *kpc, Shape *kshape, Shape *pshape, Shape *prop, unsigned protoIndex) {
-        JS_ASSERT(protoIndex <= MaxProtoIndex);
-
-        this->kpc = kpc;
-        this->kshape = kshape;
-        this->pshape = pshape;
-        this->prop = prop;
-        this->protoIndex = uint8_t(protoIndex);
-    }
-};
-
-/*
- * Special value for functions returning PropertyCacheEntry * to distinguish
- * between failure and no no-cache-fill cases.
- */
-#define JS_NO_PROP_CACHE_FILL ((js::PropertyCacheEntry *) NULL + 1)
-
-#if defined DEBUG_brendan || defined DEBUG_brendaneich
-#define JS_PROPERTY_CACHE_METERING 1
-#endif
-
-class PropertyCache
-{
-  private:
-    enum {
-        SIZE_LOG2 = 8,
-        SIZE = JS_BIT(SIZE_LOG2),
-        MASK = JS_BITMASK(SIZE_LOG2)
-    };
-
-    PropertyCacheEntry  table[SIZE];
-    JSBool              empty;
-
-  public:
-#ifdef JS_PROPERTY_CACHE_METERING
-    PropertyCacheEntry  *pctestentry;   /* entry of the last PC-based test */
-    uint32_t            fills;          /* number of cache entry fills */
-    uint32_t            nofills;        /* couldn't fill (e.g. default get) */
-    uint32_t            rofills;        /* set on read-only prop can't fill */
-    uint32_t            disfills;       /* fill attempts on disabled cache */
-    uint32_t            oddfills;       /* fill attempt after setter deleted */
-    uint32_t            add2dictfills;  /* fill attempt on dictionary object */
-    uint32_t            modfills;       /* fill that rehashed to a new entry */
-    uint32_t            brandfills;     /* scope brandings to type structural
-                                           method fills */
-    uint32_t            noprotos;       /* resolve-returned non-proto pobj */
-    uint32_t            longchains;     /* overlong scope and/or proto chain */
-    uint32_t            recycles;       /* cache entries recycled by fills */
-    uint32_t            tests;          /* cache probes */
-    uint32_t            pchits;         /* fast-path polymorphic op hits */
-    uint32_t            protopchits;    /* pchits hitting immediate prototype */
-    uint32_t            initests;       /* cache probes from JSOP_INITPROP */
-    uint32_t            inipchits;      /* init'ing next property pchit case */
-    uint32_t            inipcmisses;    /* init'ing next property pc misses */
-    uint32_t            settests;       /* cache probes from JOF_SET opcodes */
-    uint32_t            addpchits;      /* adding next property pchit case */
-    uint32_t            setpchits;      /* setting existing property pchit */
-    uint32_t            setpcmisses;    /* setting/adding property pc misses */
-    uint32_t            setmisses;      /* JSOP_SET{NAME,PROP} total misses */
-    uint32_t            kpcmisses;      /* slow-path key id == atom misses */
-    uint32_t            kshapemisses;   /* slow-path key object misses */
-    uint32_t            vcapmisses;     /* value capability misses */
-    uint32_t            misses;         /* cache misses */
-    uint32_t            flushes;        /* cache flushes */
-    uint32_t            pcpurges;       /* shadowing purges on proto chain */
-
-# define PCMETER(x)     x
-#else
-# define PCMETER(x)     ((void)0)
-#endif
-
-    PropertyCache() {
-        mozilla::PodZero(this);
-    }
-
-  private:
-    static inline uintptr_t
-    hash(jsbytecode *pc, const Shape *kshape)
-    {
-        return (((uintptr_t(pc) >> SIZE_LOG2) ^ uintptr_t(pc) ^ ((uintptr_t)kshape >> 3)) & MASK);
-    }
-
-    static inline bool matchShape(JSContext *cx, JSObject *obj, uint32_t shape);
-
-    PropertyName *
-    fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp,
-             JSObject **pobjp, PropertyCacheEntry *entry);
-
-#ifdef DEBUG
-    void assertEmpty();
-#else
-    inline void assertEmpty() {}
-#endif
-
-  public:
-    JS_ALWAYS_INLINE void test(JSContext *cx, jsbytecode *pc,
-                               JSObject **obj, JSObject **pobj,
-                               PropertyCacheEntry **entry, PropertyName **name);
-
-    /*
-     * Test for cached information about a property set on *objp at pc.
-     *
-     * On a hit, set *entryp to the entry and return true.
-     *
-     * On a miss, set *namep to the name of the property being set and return false.
-     */
-    JS_ALWAYS_INLINE bool testForSet(JSContext *cx, jsbytecode *pc, JSObject *obj,
-                                     PropertyCacheEntry **entryp, JSObject **obj2p,
-                                     PropertyName **namep);
-
-    /*
-     * Fill property cache entry for key cx->fp->pc, optimized value word
-     * computed from obj and shape, and entry capability forged from
-     * obj->shape() and an 8-bit protoIndex.
-     *
-     * Return the filled cache entry or JS_NO_PROP_CACHE_FILL if caching was
-     * not possible.
-     */
-    PropertyCacheEntry *fill(JSContext *cx, JSObject *obj, JSObject *pobj, js::Shape *shape);
-
-    void purge(JSRuntime *rt);
-
-    /* Restore an entry that may have been purged during a GC. */
-    void restore(PropertyCacheEntry *entry);
-};
-
-} /* namespace js */
-
-#endif /* jspropertycache_h___ */
deleted file mode 100644
--- a/js/src/jspropertycacheinlines.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef jspropertycacheinlines_h___
-#define jspropertycacheinlines_h___
-
-#include "jslock.h"
-#include "jspropertycache.h"
-
-#include "vm/Shape.h"
-
-/*
- * This method is designed to inline the fast path in js_Interpret, so it makes
- * "just-so" restrictions on parameters, e.g. pobj and obj should not be the
- * same variable, since for JOF_PROP-mode opcodes, obj must not be changed
- * because of a cache miss.
- *
- * On return, if name is null then obj points to the scope chain element in
- * which the property was found, pobj is locked, and entry is valid. If name is
- * non-null then no object is locked but entry is still set correctly for use,
- * e.g., by PropertyCache::fill and name should be used as the id to find.
- *
- * We must lock pobj on a hit in order to close races with threads that might
- * be deleting a property from its scope, or otherwise invalidating property
- * caches (on all threads) by re-generating JSObject::shape().
- */
-JS_ALWAYS_INLINE void
-js::PropertyCache::test(JSContext *cx, jsbytecode *pc, JSObject **obj,
-                        JSObject **pobj, PropertyCacheEntry **entry, PropertyName **name)
-{
-    JS_ASSERT(this == &cx->propertyCache());
-
-    Shape *kshape = (*obj)->lastProperty();
-    *entry = &table[hash(pc, kshape)];
-    PCMETER(pctestentry = *entry);
-    PCMETER(tests++);
-    JS_ASSERT(obj != pobj);
-    if ((*entry)->kpc == pc && (*entry)->kshape == kshape) {
-        JSObject *tmp;
-        *pobj = *obj;
-        if ((*entry)->isPrototypePropertyHit() &&
-            (tmp = (*pobj)->getProto()) != NULL) {
-            *pobj = tmp;
-        }
-
-        if ((*pobj)->lastProperty() == (*entry)->pshape) {
-            PCMETER(pchits++);
-            PCMETER((*entry)->isOwnPropertyHit() || protopchits++);
-            *name = NULL;
-            return;
-        }
-    }
-    *name = fullTest(cx, pc, obj, pobj, *entry);
-    if (!*name)
-        PCMETER(misses++);
-}
-
-JS_ALWAYS_INLINE bool
-js::PropertyCache::testForSet(JSContext *cx, jsbytecode *pc, JSObject *obj,
-                              PropertyCacheEntry **entryp, JSObject **obj2p, PropertyName **namep)
-{
-    JS_ASSERT(this == &cx->propertyCache());
-
-    Shape *kshape = obj->lastProperty();
-    PropertyCacheEntry *entry = &table[hash(pc, kshape)];
-    *entryp = entry;
-    PCMETER(pctestentry = entry);
-    PCMETER(tests++);
-    PCMETER(settests++);
-    if (entry->kpc == pc && entry->kshape == kshape)
-        return true;
-
-    PropertyName *name = fullTest(cx, pc, &obj, obj2p, entry);
-    JS_ASSERT(name);
-
-    PCMETER(misses++);
-    PCMETER(setmisses++);
-
-    *namep = name;
-    return false;
-}
-
-#endif /* jspropertycacheinlines_h___ */
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -162,17 +162,16 @@ CPP_SOURCES += [
     'jsnativestack.cpp',
     'jsnum.cpp',
     'jsobj.cpp',
     'json.cpp',
     'jsonparser.cpp',
     'jsopcode.cpp',
     'jsperf.cpp',
     'jsprf.cpp',
-    'jspropertycache.cpp',
     'jspropertytree.cpp',
     'jsproxy.cpp',
     'jsreflect.cpp',
     'jsscript.cpp',
     'jsstr.cpp',
     'jstypedarray.cpp',
     'jsutil.cpp',
     'jswatchpoint.cpp',
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -18,17 +18,16 @@
 #include "ion/IonCompartment.h"
 #include "vm/ForkJoin.h"
 #include "vm/Interpreter.h"
 
 #include "jsatominlines.h"
 #include "jsfuninlines.h"
 #include "jsinferinlines.h"
 #include "jsopcodeinlines.h"
-#include "jspropertycacheinlines.h"
 #include "jstypedarrayinlines.h"
 #include "vm/GlobalObject-inl.h"
 #include "vm/Stack-inl.h"
 
 namespace js {
 
 /*
  * Compute the implicit |this| parameter for a call expression where the callee
@@ -195,27 +194,16 @@ NativeGet(JSContext *cx, JSObject *objAr
         RootedObject pobj(cx, pobjArg);
         RootedShape shape(cx, shapeArg);
         if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp))
             return false;
     }
     return true;
 }
 
-#if defined(DEBUG) && !defined(JS_THREADSAFE) && !defined(JSGC_ROOT_ANALYSIS)
-extern void
-AssertValidPropertyCacheHit(JSContext *cx, JSObject *start, JSObject *found,
-                            PropertyCacheEntry *entry);
-#else
-inline void
-AssertValidPropertyCacheHit(JSContext *cx, JSObject *start, JSObject *found,
-                            PropertyCacheEntry *entry)
-{}
-#endif
-
 inline bool
 GetLengthProperty(const Value &lval, MutableHandleValue vp)
 {
     /* Optimize length accesses on strings, arrays, and arguments. */
     if (lval.isString()) {
         vp.setInt32(lval.toString()->length());
         return true;
     }
@@ -241,139 +229,16 @@ GetLengthProperty(const Value &lval, Mut
             vp.setInt32(TypedArray::length(obj));
             return true;
         }
     }
 
     return false;
 }
 
-inline bool
-GetPropertyOperation(JSContext *cx, JSScript *script, jsbytecode *pc, MutableHandleValue lval,
-                     MutableHandleValue vp)
-{
-    JSOp op = JSOp(*pc);
-
-    if (op == JSOP_LENGTH) {
-        if (IsOptimizedArguments(cx->fp(), lval.address())) {
-            vp.setInt32(cx->fp()->numActualArgs());
-            return true;
-        }
-
-        if (GetLengthProperty(lval, vp))
-            return true;
-    }
-
-    JSObject *obj = ToObjectFromStack(cx, lval);
-    if (!obj)
-        return false;
-
-    PropertyCacheEntry *entry;
-    JSObject *pobj;
-    PropertyName *name;
-    cx->propertyCache().test(cx, pc, &obj, &pobj, &entry, &name);
-    if (!name) {
-        AssertValidPropertyCacheHit(cx, obj, pobj, entry);
-        return NativeGet(cx, obj, pobj, entry->prop, JSGET_CACHE_RESULT, vp);
-    }
-
-    bool wasObject = lval.isObject();
-
-    RootedId id(cx, NameToId(name));
-    RootedObject nobj(cx, obj);
-
-    if (obj->getOps()->getProperty) {
-        if (!JSObject::getGeneric(cx, nobj, nobj, id, vp))
-            return false;
-    } else {
-        if (!GetPropertyHelper(cx, nobj, id, JSGET_CACHE_RESULT, vp))
-            return false;
-    }
-
-#if JS_HAS_NO_SUCH_METHOD
-    if (op == JSOP_CALLPROP &&
-        JS_UNLIKELY(vp.isPrimitive()) &&
-        wasObject)
-    {
-        if (!OnUnknownMethod(cx, nobj, IdToValue(id), vp))
-            return false;
-    }
-#endif
-
-    return true;
-}
-
-inline bool
-SetPropertyOperation(JSContext *cx, jsbytecode *pc, HandleValue lval, HandleValue rval)
-{
-    JS_ASSERT(*pc == JSOP_SETPROP);
-
-    RootedObject obj(cx, ToObjectFromStack(cx, lval));
-    if (!obj)
-        return false;
-
-    PropertyCacheEntry *entry;
-    JSObject *obj2;
-    PropertyName *name;
-    if (cx->propertyCache().testForSet(cx, pc, obj, &entry, &obj2, &name)) {
-        /*
-         * 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 a set either an existing "own" property, or
-         * on a prototype property that has a setter.
-         */
-        RootedShape shape(cx, entry->prop);
-        JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
-        JS_ASSERT_IF(shape->hasSlot(), entry->isOwnPropertyHit());
-
-        if (entry->isOwnPropertyHit() ||
-            ((obj2 = obj->getProto()) && obj2->lastProperty() == entry->pshape)) {
-#ifdef DEBUG
-            if (entry->isOwnPropertyHit()) {
-                JS_ASSERT(obj->nativeLookup(cx, shape->propid()) == shape);
-            } else {
-                JS_ASSERT(obj2->nativeLookup(cx, shape->propid()) == shape);
-                JS_ASSERT(entry->isPrototypePropertyHit());
-                JS_ASSERT(entry->kshape != entry->pshape);
-                JS_ASSERT(!shape->hasSlot());
-            }
-#endif
-
-            if (shape->hasDefaultSetter() && shape->hasSlot()) {
-                /* Fast path for, e.g., plain Object instance properties. */
-                JSObject::nativeSetSlotWithType(cx, obj, shape, rval);
-            } else {
-                RootedValue rref(cx, rval);
-                bool strict = cx->stack.currentScript()->strict;
-                if (!js_NativeSet(cx, obj, obj, shape, strict, &rref))
-                    return false;
-            }
-            return true;
-        }
-
-        GET_NAME_FROM_BYTECODE(cx->stack.currentScript(), pc, 0, name);
-    }
-
-    bool strict = cx->stack.currentScript()->strict;
-    RootedValue rref(cx, rval);
-
-    RootedId id(cx, NameToId(name));
-    if (JS_LIKELY(!obj->getOps()->setProperty)) {
-        if (!baseops::SetPropertyHelper(cx, obj, obj, id, DNP_CACHE_RESULT, &rref, strict))
-            return false;
-    } else {
-        if (!JSObject::setGeneric(cx, obj, obj, id, &rref, strict))
-            return false;
-    }
-
-    return true;
-}
-
 template <bool TypeOf> inline bool
 FetchName(JSContext *cx, HandleObject obj, HandleObject obj2, HandlePropertyName name,
           HandleShape shape, MutableHandleValue vp)
 {
     if (!shape) {
         if (TypeOf) {
             vp.setUndefined();
             return true;
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -23,17 +23,16 @@
 #include "jsdbgapi.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsiter.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsprf.h"
-#include "jspropertycache.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "builtin/Eval.h"
 #include "ion/BaselineJIT.h"
 #include "ion/Ion.h"
 #include "vm/Debugger.h"
 #include "vm/Shape.h"
 
@@ -249,16 +248,86 @@ NoSuchMethod(JSContext *cx, unsigned arg
     args[1].setObject(*argsobj);
     JSBool ok = Invoke(cx, args);
     vp[0] = args.rval();
     return ok;
 }
 
 #endif /* JS_HAS_NO_SUCH_METHOD */
 
+inline bool
+GetPropertyOperation(JSContext *cx, HandleScript script, jsbytecode *pc, MutableHandleValue lval,
+                     MutableHandleValue vp)
+{
+    JSOp op = JSOp(*pc);
+
+    if (op == JSOP_LENGTH) {
+        if (IsOptimizedArguments(cx->fp(), lval.address())) {
+            vp.setInt32(cx->fp()->numActualArgs());
+            return true;
+        }
+
+        if (GetLengthProperty(lval, vp))
+            return true;
+    }
+
+    JSObject *obj = ToObjectFromStack(cx, lval);
+    if (!obj)
+        return false;
+
+    bool wasObject = lval.isObject();
+
+    RootedId id(cx, NameToId(script->getName(pc)));
+    RootedObject nobj(cx, obj);
+
+    if (obj->getOps()->getProperty) {
+        if (!JSObject::getGeneric(cx, nobj, nobj, id, vp))
+            return false;
+    } else {
+        if (!GetPropertyHelper(cx, nobj, id, 0, vp))
+            return false;
+    }
+
+#if JS_HAS_NO_SUCH_METHOD
+    if (op == JSOP_CALLPROP &&
+        JS_UNLIKELY(vp.isPrimitive()) &&
+        wasObject)
+    {
+        if (!OnUnknownMethod(cx, nobj, IdToValue(id), vp))
+            return false;
+    }
+#endif
+
+    return true;
+}
+
+inline bool
+SetPropertyOperation(JSContext *cx, HandleScript script, jsbytecode *pc, HandleValue lval,
+                     HandleValue rval)
+{
+    JS_ASSERT(*pc == JSOP_SETPROP);
+
+    RootedObject obj(cx, ToObjectFromStack(cx, lval));
+    if (!obj)
+        return false;
+
+    RootedValue rref(cx, rval);
+
+    RootedId id(cx, NameToId(script->getName(pc)));
+    if (JS_LIKELY(!obj->getOps()->setProperty)) {
+        if (!baseops::SetPropertyHelper(cx, obj, obj, id, 0, &rref, script->strict))
+            return false;
+    } else {
+        if (!JSObject::setGeneric(cx, obj, obj, id, &rref, script->strict))
+            return false;
+    }
+
+    return true;
+}
+
 bool
 js::ReportIsNotFunction(JSContext *cx, const Value &v, int numToSkip, MaybeConstruct construct)
 {
     unsigned error = construct ? JSMSG_NOT_CONSTRUCTOR : JSMSG_NOT_FUNCTION;
     int spIndex = numToSkip >= 0 ? -(numToSkip + 1) : JSDVG_SEARCH_STACK;
 
     RootedValue val(cx, v);
     js_ReportValueError3(cx, error, spIndex, val, NullPtr(), NULL, NULL);
@@ -904,41 +973,18 @@ inline InterpreterFrames::InterpreterFra
     cx->runtime()->interpreterFrames = this;
 }
 
 inline InterpreterFrames::~InterpreterFrames()
 {
     context->runtime()->interpreterFrames = older;
 }
 
-#if defined(DEBUG) && !defined(JS_THREADSAFE) && !defined(JSGC_ROOT_ANALYSIS)
-void
-js::AssertValidPropertyCacheHit(JSContext *cx, JSObject *start,
-                                JSObject *found, PropertyCacheEntry *entry)
-{
-    jsbytecode *pc;
-    JSScript *script = cx->stack.currentScript(&pc);
-
-    uint64_t sample = cx->runtime()->gcNumber;
-
-    PropertyName *name = GetNameFromBytecode(cx, script, pc, JSOp(*pc));
-    JSObject *pobj;
-    Shape *prop;
-    if (baseops::LookupProperty<NoGC>(cx, start, NameToId(name), &pobj, &prop)) {
-        JS_ASSERT(prop);
-        JS_ASSERT(pobj == found);
-        JS_ASSERT(entry->prop == prop);
-    }
-
-    JS_ASSERT(cx->runtime()->gcNumber == sample);
-}
-#endif /* DEBUG && !JS_THREADSAFE */
-
 /*
- * Ensure that the intrepreter switch can close call-bytecode cases in the
+ * Ensure that the interpreter switch can close call-bytecode cases in the
  * same way as non-call bytecodes.
  */
 JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
 JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
 JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
 
 /*
  * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
@@ -2076,17 +2122,17 @@ BEGIN_CASE(JSOP_SETNAME)
 }
 END_CASE(JSOP_SETNAME)
 
 BEGIN_CASE(JSOP_SETPROP)
 {
     HandleValue lval = HandleValue::fromMarkedLocation(&regs.sp[-2]);
     HandleValue rval = HandleValue::fromMarkedLocation(&regs.sp[-1]);
 
-    if (!SetPropertyOperation(cx, regs.pc, lval, rval))
+    if (!SetPropertyOperation(cx, script, regs.pc, lval, rval))
         goto error;
 
     regs.sp[-2] = regs.sp[-1];
     regs.sp--;
 }
 END_CASE(JSOP_SETPROP)
 
 BEGIN_CASE(JSOP_GETELEM)