Bug 704356 - Remove the JS property cache. r=njn, sr=luke

--HG--
extra : rebase_source : 67079f154c13fd401388792ec1410adb04157092
This commit is contained in:
Jan de Mooij 2013-06-18 09:16:57 +02:00
parent fb4602b57a
commit 22b86d4fe0
12 changed files with 77 additions and 770 deletions

View File

@ -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

View File

@ -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();

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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;
}

View File

@ -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___ */

View File

@ -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___ */

View File

@ -167,7 +167,6 @@ CPP_SOURCES += [
'jsopcode.cpp',
'jsperf.cpp',
'jsprf.cpp',
'jspropertycache.cpp',
'jspropertytree.cpp',
'jsproxy.cpp',
'jsreflect.cpp',

View File

@ -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)

View File

@ -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(&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];