зеркало из https://github.com/mozilla/gecko-dev.git
Bug 704356 - Remove the JS property cache. r=njn, sr=luke
--HG-- extra : rebase_source : 67079f154c13fd401388792ec1410adb04157092
This commit is contained in:
Родитель
d8a5222e3e
Коммит
fb8cbdd709
|
@ -22,7 +22,6 @@
|
|||
#include "jsatom.h"
|
||||
#include "jsclist.h"
|
||||
#include "jsgc.h"
|
||||
#include "jspropertycache.h"
|
||||
#include "jspropertytree.h"
|
||||
#include "jsprototypes.h"
|
||||
#include "jsutil.h"
|
||||
|
@ -1262,7 +1261,6 @@ struct JSRuntime : public JS::shadow::Runtime,
|
|||
}
|
||||
|
||||
js::GSNCache gsnCache;
|
||||
js::PropertyCache propertyCache;
|
||||
js::NewObjectCache newObjectCache;
|
||||
js::NativeIterCache nativeIterCache;
|
||||
js::SourceDataCache sourceDataCache;
|
||||
|
@ -1724,8 +1722,6 @@ struct JSContext : js::ContextFriendFields,
|
|||
|
||||
inline js::PropertyTree &propertyTree();
|
||||
|
||||
js::PropertyCache &propertyCache() { return runtime()->propertyCache; }
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
unsigned outstandingRequests;/* number of JS_BeginRequest calls
|
||||
without the corresponding
|
||||
|
|
|
@ -2571,7 +2571,6 @@ PurgeRuntime(JSRuntime *rt)
|
|||
rt->freeLifoAlloc.transferUnusedFrom(&rt->tempLifoAlloc);
|
||||
|
||||
rt->gsnCache.purge();
|
||||
rt->propertyCache.purge(rt);
|
||||
rt->newObjectCache.purge();
|
||||
rt->nativeIterCache.purge();
|
||||
rt->sourceDataCache.purge();
|
||||
|
|
|
@ -3408,8 +3408,7 @@ js::DefineNativeProperty(JSContext *cx, HandleObject obj, HandleId id, HandleVal
|
|||
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);
|
||||
|
@ -4003,9 +4002,6 @@ GetPropertyHelperInline(JSContext *cx,
|
|||
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;
|
||||
|
@ -4272,7 +4268,7 @@ 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. */
|
||||
|
@ -4360,9 +4356,6 @@ baseops::SetPropertyHelper(JSContext *cx, HandleObject obj, HandleObject receive
|
|||
* 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;
|
||||
|
||||
|
@ -4441,9 +4434,6 @@ baseops::SetPropertyHelper(JSContext *cx, HandleObject obj, HandleObject receive
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -1200,12 +1200,11 @@ CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto, Hand
|
|||
/*
|
||||
* 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.
|
||||
|
@ -1292,9 +1291,6 @@ LookupNameWithGlobalDefault(JSContext *cx, HandlePropertyName name, HandleObject
|
|||
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
|
||||
|
|
|
@ -233,20 +233,6 @@ extern const char js_EscapeMap[];
|
|||
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
|
||||
|
|
|
@ -85,22 +85,6 @@ BytecodeFallsThrough(JSOp op)
|
|||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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___ */
|
|
@ -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___ */
|
|
@ -167,7 +167,6 @@ CPP_SOURCES += [
|
|||
'jsopcode.cpp',
|
||||
'jsperf.cpp',
|
||||
'jsprf.cpp',
|
||||
'jspropertycache.cpp',
|
||||
'jspropertytree.cpp',
|
||||
'jsproxy.cpp',
|
||||
'jsreflect.cpp',
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#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"
|
||||
|
@ -200,17 +199,6 @@ NativeGet(JSContext *cx, JSObject *objArg, JSObject *pobjArg, Shape *shapeArg,
|
|||
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)
|
||||
{
|
||||
|
@ -246,129 +234,6 @@ GetLengthProperty(const Value &lval, MutableHandleValue vp)
|
|||
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)
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#include "jsobj.h"
|
||||
#include "jsopcode.h"
|
||||
#include "jsprf.h"
|
||||
#include "jspropertycache.h"
|
||||
#include "jsscript.h"
|
||||
#include "jsstr.h"
|
||||
#include "builtin/Eval.h"
|
||||
|
@ -254,6 +253,76 @@ NoSuchMethod(JSContext *cx, unsigned argc, Value *vp)
|
|||
|
||||
#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)
|
||||
{
|
||||
|
@ -909,31 +978,8 @@ 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);
|
||||
|
@ -2081,7 +2127,7 @@ BEGIN_CASE(JSOP_SETPROP)
|
|||
HandleValue lval = HandleValue::fromMarkedLocation(®s.sp[-2]);
|
||||
HandleValue rval = HandleValue::fromMarkedLocation(®s.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];
|
||||
|
|
Загрузка…
Ссылка в новой задаче