mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
merge
This commit is contained in:
commit
36ee148277
@ -147,6 +147,7 @@ CPPSRCS = \
|
||||
jsopcode.cpp \
|
||||
jsparse.cpp \
|
||||
jsprf.cpp \
|
||||
jspropertycache.cpp \
|
||||
jspropertytree.cpp \
|
||||
jsregexp.cpp \
|
||||
jsscan.cpp \
|
||||
@ -202,6 +203,8 @@ INSTALLED_HEADERS = \
|
||||
jsotypes.h \
|
||||
jsparse.h \
|
||||
jsprf.h \
|
||||
jspropertycache.h \
|
||||
jspropertycacheinlines.h \
|
||||
jspropertytree.h \
|
||||
jsproto.tbl \
|
||||
jsprvtd.h \
|
||||
|
@ -136,7 +136,7 @@ JSThreadData::finish()
|
||||
js_DestroyDtoaState(dtoaState);
|
||||
|
||||
js_FinishGSNCache(&gsnCache);
|
||||
js_FinishPropertyCache(&propertyCache);
|
||||
propertyCache.~PropertyCache();
|
||||
#if defined JS_TRACER
|
||||
FinishJIT(&traceMonitor);
|
||||
#endif
|
||||
@ -160,7 +160,7 @@ JSThreadData::purge(JSContext *cx)
|
||||
js_PurgeGSNCache(&gsnCache);
|
||||
|
||||
/* FIXME: bug 506341. */
|
||||
js_PurgePropertyCache(cx, &propertyCache);
|
||||
propertyCache.purge(cx);
|
||||
|
||||
#ifdef JS_TRACER
|
||||
/*
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include "jshashtable.h"
|
||||
#include "jsinterp.h"
|
||||
#include "jsobj.h"
|
||||
#include "jspropertycache.h"
|
||||
#include "jspropertytree.h"
|
||||
#include "jsprvtd.h"
|
||||
#include "jspubtd.h"
|
||||
@ -539,7 +540,7 @@ struct JSThreadData {
|
||||
JSGSNCache gsnCache;
|
||||
|
||||
/* Property cache for faster call/get/set invocation. */
|
||||
JSPropertyCache propertyCache;
|
||||
js::PropertyCache propertyCache;
|
||||
|
||||
/* Optional stack of heap-allocated scoped local GC roots. */
|
||||
JSLocalRootStack *localRootStack;
|
||||
@ -2361,7 +2362,7 @@ js_GetTopStackFrame(JSContext *cx)
|
||||
static JS_INLINE JSBool
|
||||
js_IsPropertyCacheDisabled(JSContext *cx)
|
||||
{
|
||||
return cx->runtime->shapeGen >= SHAPE_OVERFLOW_BIT;
|
||||
return cx->runtime->shapeGen >= js::SHAPE_OVERFLOW_BIT;
|
||||
}
|
||||
|
||||
static JS_INLINE uint32
|
||||
@ -2376,7 +2377,7 @@ js_RegenerateShapeForGC(JSContext *cx)
|
||||
* the shape stays such.
|
||||
*/
|
||||
uint32 shape = cx->runtime->shapeGen;
|
||||
shape = (shape + 1) | (shape & SHAPE_OVERFLOW_BIT);
|
||||
shape = (shape + 1) | (shape & js::SHAPE_OVERFLOW_BIT);
|
||||
cx->runtime->shapeGen = shape;
|
||||
return shape;
|
||||
}
|
||||
|
@ -65,6 +65,7 @@
|
||||
#include "jsnum.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsopcode.h"
|
||||
#include "jspropertycache.h"
|
||||
#include "jsscan.h"
|
||||
#include "jsscope.h"
|
||||
#include "jsscript.h"
|
||||
@ -75,7 +76,7 @@
|
||||
#include "jsvector.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
#include "jsinterpinlines.h"
|
||||
#include "jspropertycacheinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsscopeinlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
@ -96,477 +97,6 @@ using namespace js;
|
||||
/* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */
|
||||
#if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___
|
||||
|
||||
JS_REQUIRES_STACK JSPropCacheEntry *
|
||||
js_FillPropertyCache(JSContext *cx, JSObject *obj,
|
||||
uintN scopeIndex, uintN protoIndex, JSObject *pobj,
|
||||
JSScopeProperty *sprop, JSBool adding)
|
||||
{
|
||||
JSPropertyCache *cache;
|
||||
jsbytecode *pc;
|
||||
JSScope *scope;
|
||||
jsuword kshape, vshape;
|
||||
JSOp op;
|
||||
const JSCodeSpec *cs;
|
||||
jsuword vword;
|
||||
JSPropCacheEntry *entry;
|
||||
|
||||
JS_ASSERT(!cx->runtime->gcRunning);
|
||||
cache = &JS_PROPERTY_CACHE(cx);
|
||||
|
||||
/* FIXME bug 489098: consider enabling the property cache for eval. */
|
||||
if (js_IsPropertyCacheDisabled(cx) || (cx->fp->flags & JSFRAME_EVAL)) {
|
||||
PCMETER(cache->disfills++);
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for fill from js_SetPropertyHelper where the setter removed sprop
|
||||
* from pobj's scope (via unwatch or delete, e.g.).
|
||||
*/
|
||||
scope = OBJ_SCOPE(pobj);
|
||||
if (!scope->hasProperty(sprop)) {
|
||||
PCMETER(cache->oddfills++);
|
||||
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 js_LookupPropertyWithFlags has returned the nominal protoIndex,
|
||||
* we have to validate protoIndex if it is non-zero. If it is zero, then
|
||||
* we know thanks to the scope->hasProperty test above, combined with the
|
||||
* fact that obj == pobj, that protoIndex is invariant.
|
||||
*
|
||||
* The scopeIndex can't be wrong. We require JS_SetParent calls to happen
|
||||
* before any running script might consult a parent-linked scope chain. If
|
||||
* this requirement is not satisfied, the fill in progress will never hit,
|
||||
* but vcap vs. scope shape tests ensure nothing malfunctions.
|
||||
*/
|
||||
JS_ASSERT_IF(scopeIndex == 0 && protoIndex == 0, obj == pobj);
|
||||
|
||||
if (protoIndex != 0) {
|
||||
JSObject *tmp = obj;
|
||||
|
||||
for (uintN i = 0; i != scopeIndex; i++)
|
||||
tmp = tmp->getParent();
|
||||
JS_ASSERT(tmp != pobj);
|
||||
|
||||
protoIndex = 1;
|
||||
for (;;) {
|
||||
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 || !OBJ_IS_NATIVE(tmp)) {
|
||||
PCMETER(cache->noprotos++);
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
if (tmp == pobj)
|
||||
break;
|
||||
++protoIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) {
|
||||
PCMETER(cache->longchains++);
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Optimize the cached vword based on our parameters and the current pc's
|
||||
* opcode format flags.
|
||||
*/
|
||||
pc = cx->fp->regs->pc;
|
||||
op = js_GetOpcode(cx, cx->fp->script, pc);
|
||||
cs = &js_CodeSpec[op];
|
||||
kshape = 0;
|
||||
|
||||
do {
|
||||
/*
|
||||
* Check for a prototype "plain old method" callee computation. What
|
||||
* is a plain old method? It's a function-valued property with stub
|
||||
* getter, so get of a function is idempotent.
|
||||
*/
|
||||
if (cs->format & JOF_CALLOP) {
|
||||
jsval v;
|
||||
|
||||
if (sprop->isMethod()) {
|
||||
/*
|
||||
* A compiler-created function object, AKA a method, already
|
||||
* memoized in the property tree.
|
||||
*/
|
||||
JS_ASSERT(scope->hasMethodBarrier());
|
||||
v = sprop->methodValue();
|
||||
JS_ASSERT(VALUE_IS_FUNCTION(cx, v));
|
||||
JS_ASSERT(v == LOCKED_OBJ_GET_SLOT(pobj, sprop->slot));
|
||||
vword = JSVAL_OBJECT_TO_PCVAL(v);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!scope->generic() &&
|
||||
sprop->hasDefaultGetter() &&
|
||||
SPROP_HAS_VALID_SLOT(sprop, scope)) {
|
||||
v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
|
||||
if (VALUE_IS_FUNCTION(cx, v)) {
|
||||
/*
|
||||
* Great, we have a function-valued prototype property
|
||||
* where the getter is JS_PropertyStub. The type id in
|
||||
* pobj's scope does not evolve with changes to property
|
||||
* values, however.
|
||||
*
|
||||
* So here, on first cache fill for this method, we brand
|
||||
* the scope with a new shape and set the JSScope::BRANDED
|
||||
* flag. Once this flag is set, any property assignment
|
||||
* that changes the value from or to a different function
|
||||
* object will result in shape being regenerated.
|
||||
*/
|
||||
if (!scope->branded()) {
|
||||
PCMETER(cache->brandfills++);
|
||||
#ifdef DEBUG_notme
|
||||
fprintf(stderr,
|
||||
"branding %p (%s) for funobj %p (%s), shape %lu\n",
|
||||
pobj, pobj->getClass()->name,
|
||||
JSVAL_TO_OBJECT(v),
|
||||
JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v))),
|
||||
OBJ_SHAPE(obj));
|
||||
#endif
|
||||
if (!scope->brand(cx, sprop->slot, v))
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
vword = JSVAL_OBJECT_TO_PCVAL(v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If getting a value via a stub getter, we can cache the slot. */
|
||||
if (!(cs->format & (JOF_SET | JOF_INCDEC | JOF_FOR)) &&
|
||||
sprop->hasDefaultGetter() &&
|
||||
SPROP_HAS_VALID_SLOT(sprop, scope)) {
|
||||
/* Great, let's cache sprop's slot and use it on cache hit. */
|
||||
vword = SLOT_TO_PCVAL(sprop->slot);
|
||||
} else {
|
||||
/* Best we can do is to cache sprop (still a nice speedup). */
|
||||
vword = SPROP_TO_PCVAL(sprop);
|
||||
if (adding &&
|
||||
sprop == scope->lastProperty() &&
|
||||
scope->shape == sprop->shape) {
|
||||
/*
|
||||
* Our caller added a new property. We also know that a setter
|
||||
* that js_NativeSet could have run has not mutated the scope,
|
||||
* so the added property is still the last one added, and the
|
||||
* scope is not branded.
|
||||
*
|
||||
* We want to cache under scope's shape before the property
|
||||
* addition to bias for the case when the mutator opcode
|
||||
* always adds the same property. This allows us to optimize
|
||||
* periodic execution of object initializers or other explicit
|
||||
* initialization sequences such as
|
||||
*
|
||||
* obj = {}; obj.x = 1; obj.y = 2;
|
||||
*
|
||||
* We assume that on average the win from this optimization is
|
||||
* greater than the cost of an extra mismatch per loop owing to
|
||||
* the bias for the following case:
|
||||
*
|
||||
* obj = {}; ... for (...) { ... obj.x = ... }
|
||||
*
|
||||
* On the first iteration of such a for loop, JSOP_SETPROP
|
||||
* fills the cache with the shape of the newly created object
|
||||
* obj, not the shape of obj after obj.x has been assigned.
|
||||
* That mismatches obj's shape on the second iteration. Note
|
||||
* that on the third and subsequent iterations the cache will
|
||||
* be hit because the shape is no longer updated.
|
||||
*/
|
||||
JS_ASSERT(!scope->isSharedEmpty());
|
||||
if (sprop->parent) {
|
||||
kshape = sprop->parent->shape;
|
||||
} else {
|
||||
/*
|
||||
* If obj had its own empty scope before, with a unique
|
||||
* shape, that is lost. Here we only attempt to find a
|
||||
* matching empty scope. In unusual cases involving
|
||||
* __proto__ assignment we may not find one.
|
||||
*/
|
||||
JSObject *proto = obj->getProto();
|
||||
if (!proto || !OBJ_IS_NATIVE(proto))
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
JSScope *protoscope = OBJ_SCOPE(proto);
|
||||
if (!protoscope->emptyScope ||
|
||||
protoscope->emptyScope->clasp != obj->getClass()) {
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
kshape = protoscope->emptyScope->shape;
|
||||
}
|
||||
|
||||
/*
|
||||
* When adding we predict no prototype object will later gain a
|
||||
* readonly property or setter.
|
||||
*/
|
||||
vshape = cx->runtime->protoHazardShape;
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (kshape == 0) {
|
||||
kshape = OBJ_SHAPE(obj);
|
||||
vshape = scope->shape;
|
||||
}
|
||||
JS_ASSERT(kshape < SHAPE_OVERFLOW_BIT);
|
||||
|
||||
if (obj == pobj) {
|
||||
JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
if (scopeIndex == 0) {
|
||||
JS_ASSERT(protoIndex != 0);
|
||||
JS_ASSERT((protoIndex == 1) == (obj->getProto() == pobj));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (scopeIndex != 0 || protoIndex != 1) {
|
||||
/*
|
||||
* Make sure that a later shadowing assignment will enter
|
||||
* PurgeProtoChain and invalidate this entry, bug 479198.
|
||||
*
|
||||
* This is thread-safe even though obj is not locked. Only the
|
||||
* DELEGATE bit of obj->classword can change at runtime, given that
|
||||
* obj is native; and the bit is only set, never cleared. And on
|
||||
* platforms where another CPU can fail to see this write, it's OK
|
||||
* because the property cache and JIT cache are thread-local.
|
||||
*/
|
||||
obj->setDelegate();
|
||||
}
|
||||
}
|
||||
JS_ASSERT(vshape < SHAPE_OVERFLOW_BIT);
|
||||
|
||||
entry = &cache->table[PROPERTY_CACHE_HASH(pc, kshape)];
|
||||
PCMETER(PCVAL_IS_NULL(entry->vword) || cache->recycles++);
|
||||
entry->kpc = pc;
|
||||
entry->kshape = kshape;
|
||||
entry->vcap = PCVCAP_MAKE(vshape, scopeIndex, protoIndex);
|
||||
entry->vword = vword;
|
||||
|
||||
cache->empty = JS_FALSE;
|
||||
PCMETER(cache->fills++);
|
||||
|
||||
/*
|
||||
* The modfills counter is not exact. It increases if a getter or setter
|
||||
* recurse into the interpreter.
|
||||
*/
|
||||
PCMETER(entry == cache->pctestentry || cache->modfills++);
|
||||
PCMETER(cache->pctestentry = NULL);
|
||||
return entry;
|
||||
}
|
||||
|
||||
static inline JSAtom *
|
||||
GetAtomFromBytecode(JSContext *cx, jsbytecode *pc, JSOp op, const JSCodeSpec &cs)
|
||||
{
|
||||
if (op == JSOP_LENGTH)
|
||||
return cx->runtime->atomState.lengthAtom;
|
||||
|
||||
ptrdiff_t pcoff = (JOF_TYPE(cs.format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
|
||||
JSAtom *atom;
|
||||
GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
|
||||
return atom;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK JSAtom *
|
||||
js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
|
||||
JSObject **objp, JSObject **pobjp,
|
||||
JSPropCacheEntry *entry)
|
||||
{
|
||||
JSObject *obj, *pobj, *tmp;
|
||||
uint32 vcap;
|
||||
|
||||
JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code)
|
||||
< cx->fp->script->length);
|
||||
|
||||
JSOp op = js_GetOpcode(cx, cx->fp->script, pc);
|
||||
const JSCodeSpec &cs = js_CodeSpec[op];
|
||||
|
||||
obj = *objp;
|
||||
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
||||
vcap = entry->vcap;
|
||||
|
||||
if (entry->kpc != pc) {
|
||||
PCMETER(JS_PROPERTY_CACHE(cx).kpcmisses++);
|
||||
|
||||
JSAtom *atom = GetAtomFromBytecode(cx, pc, op, cs);
|
||||
#ifdef DEBUG_notme
|
||||
fprintf(stderr,
|
||||
"id miss for %s from %s:%u"
|
||||
" (pc %u, kpc %u, kshape %u, shape %u)\n",
|
||||
js_AtomToPrintableString(cx, atom),
|
||||
cx->fp->script->filename,
|
||||
js_PCToLineNumber(cx, cx->fp->script, pc),
|
||||
pc - cx->fp->script->code,
|
||||
entry->kpc - cx->fp->script->code,
|
||||
entry->kshape,
|
||||
OBJ_SHAPE(obj));
|
||||
js_Disassemble1(cx, cx->fp->script, pc,
|
||||
pc - cx->fp->script->code,
|
||||
JS_FALSE, stderr);
|
||||
#endif
|
||||
|
||||
return atom;
|
||||
}
|
||||
|
||||
if (entry->kshape != OBJ_SHAPE(obj)) {
|
||||
PCMETER(JS_PROPERTY_CACHE(cx).kshapemisses++);
|
||||
return GetAtomFromBytecode(cx, pc, op, cs);
|
||||
}
|
||||
|
||||
/*
|
||||
* PROPERTY_CACHE_TEST handles only the direct and immediate-prototype hit
|
||||
* cases, all others go here. We could embed the target object in the cache
|
||||
* entry but then entry size would be 5 words. Instead we traverse chains.
|
||||
*/
|
||||
pobj = obj;
|
||||
|
||||
if (JOF_MODE(cs.format) == JOF_NAME) {
|
||||
while (vcap & (PCVCAP_SCOPEMASK << PCVCAP_PROTOBITS)) {
|
||||
tmp = pobj->getParent();
|
||||
if (!tmp || !OBJ_IS_NATIVE(tmp))
|
||||
break;
|
||||
pobj = tmp;
|
||||
vcap -= PCVCAP_PROTOSIZE;
|
||||
}
|
||||
|
||||
*objp = pobj;
|
||||
}
|
||||
|
||||
while (vcap & PCVCAP_PROTOMASK) {
|
||||
tmp = pobj->getProto();
|
||||
if (!tmp || !OBJ_IS_NATIVE(tmp))
|
||||
break;
|
||||
pobj = tmp;
|
||||
--vcap;
|
||||
}
|
||||
|
||||
if (JSPropCacheEntry::matchShape(cx, pobj, PCVCAP_SHAPE(vcap))) {
|
||||
#ifdef DEBUG
|
||||
JSAtom *atom = GetAtomFromBytecode(cx, pc, op, cs);
|
||||
jsid id = ATOM_TO_JSID(atom);
|
||||
|
||||
id = js_CheckForStringIndex(id);
|
||||
JS_ASSERT(OBJ_SCOPE(pobj)->lookup(id));
|
||||
JS_ASSERT_IF(OBJ_SCOPE(pobj)->object, OBJ_SCOPE(pobj)->object == pobj);
|
||||
#endif
|
||||
*pobjp = pobj;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PCMETER(JS_PROPERTY_CACHE(cx).vcapmisses++);
|
||||
return GetAtomFromBytecode(cx, pc, op, cs);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
#define ASSERT_CACHE_IS_EMPTY(cache) \
|
||||
JS_BEGIN_MACRO \
|
||||
JSPropertyCache *cache_ = (cache); \
|
||||
uintN i_; \
|
||||
JS_ASSERT(cache_->empty); \
|
||||
for (i_ = 0; i_ < PROPERTY_CACHE_SIZE; i_++) { \
|
||||
JS_ASSERT(!cache_->table[i_].kpc); \
|
||||
JS_ASSERT(!cache_->table[i_].kshape); \
|
||||
JS_ASSERT(!cache_->table[i_].vcap); \
|
||||
JS_ASSERT(!cache_->table[i_].vword); \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
#else
|
||||
#define ASSERT_CACHE_IS_EMPTY(cache) ((void)0)
|
||||
#endif
|
||||
|
||||
JS_STATIC_ASSERT(PCVAL_NULL == 0);
|
||||
|
||||
void
|
||||
js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache)
|
||||
{
|
||||
if (cache->empty) {
|
||||
ASSERT_CACHE_IS_EMPTY(cache);
|
||||
return;
|
||||
}
|
||||
|
||||
PodArrayZero(cache->table);
|
||||
cache->empty = JS_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);
|
||||
#ifdef JS_THREADSAFE
|
||||
fprintf(fp, "thread %lu, ", (unsigned long) cx->thread->id);
|
||||
#endif
|
||||
fprintf(fp, "GC %u\n", cx->runtime->gcNumber);
|
||||
|
||||
# define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)cache->mem)
|
||||
P(fills);
|
||||
P(nofills);
|
||||
P(rofills);
|
||||
P(disfills);
|
||||
P(oddfills);
|
||||
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. * cache->pchits) / cache->tests,
|
||||
(100. * cache->protopchits) / cache->tests,
|
||||
(100. * (cache->addpchits + cache->setpchits))
|
||||
/ cache->settests,
|
||||
(100. * cache->inipchits) / cache->initests,
|
||||
(100. * (cache->tests - cache->misses)) / cache->tests);
|
||||
fflush(fp);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
PCMETER(cache->flushes++);
|
||||
}
|
||||
|
||||
void
|
||||
js_PurgePropertyCacheForScript(JSContext *cx, JSScript *script)
|
||||
{
|
||||
JSPropertyCache *cache = &JS_PROPERTY_CACHE(cx);
|
||||
|
||||
for (JSPropCacheEntry *entry = cache->table;
|
||||
entry < cache->table + PROPERTY_CACHE_SIZE;
|
||||
entry++) {
|
||||
if (JS_UPTRDIFF(entry->kpc, script->code) < script->length) {
|
||||
entry->kpc = NULL;
|
||||
#ifdef DEBUG
|
||||
entry->kshape = entry->vcap = entry->vword = 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the current arena has enough space to fit nslots after sp and, if
|
||||
* so, reserve the necessary space.
|
||||
@ -2692,7 +2222,7 @@ JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL_CONSTEXPR(JSVAL_INT_MAX)));
|
||||
static bool
|
||||
AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
|
||||
ptrdiff_t pcoff, JSObject *start, JSObject *found,
|
||||
JSPropCacheEntry *entry)
|
||||
PropertyCacheEntry *entry)
|
||||
{
|
||||
uint32 sample = cx->runtime->gcNumber;
|
||||
|
||||
@ -2714,8 +2244,7 @@ AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
|
||||
}
|
||||
if (!ok)
|
||||
return false;
|
||||
if (cx->runtime->gcNumber != sample ||
|
||||
PCVCAP_SHAPE(entry->vcap) != OBJ_SHAPE(pobj)) {
|
||||
if (cx->runtime->gcNumber != sample || entry->vshape() != OBJ_SHAPE(pobj)) {
|
||||
pobj->dropProperty(cx, prop);
|
||||
return true;
|
||||
}
|
||||
|
@ -226,215 +226,6 @@ typedef struct JSInlineFrame {
|
||||
|
||||
#define JSFRAME_SPECIAL (JSFRAME_DEBUGGER | JSFRAME_EVAL)
|
||||
|
||||
/*
|
||||
* 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>.
|
||||
*/
|
||||
#define PROPERTY_CACHE_LOG2 12
|
||||
#define PROPERTY_CACHE_SIZE JS_BIT(PROPERTY_CACHE_LOG2)
|
||||
#define PROPERTY_CACHE_MASK JS_BITMASK(PROPERTY_CACHE_LOG2)
|
||||
|
||||
/*
|
||||
* Add kshape rather than xor it to avoid collisions between nearby bytecode
|
||||
* that are evolving an object by setting successive properties, incrementing
|
||||
* the object's scope->shape on each set.
|
||||
*/
|
||||
#define PROPERTY_CACHE_HASH(pc,kshape) \
|
||||
(((((jsuword)(pc) >> PROPERTY_CACHE_LOG2) ^ (jsuword)(pc)) + (kshape)) & \
|
||||
PROPERTY_CACHE_MASK)
|
||||
|
||||
/*
|
||||
* Property cache value capability macros.
|
||||
*/
|
||||
#define PCVCAP_PROTOBITS 4
|
||||
#define PCVCAP_PROTOSIZE JS_BIT(PCVCAP_PROTOBITS)
|
||||
#define PCVCAP_PROTOMASK JS_BITMASK(PCVCAP_PROTOBITS)
|
||||
|
||||
#define PCVCAP_SCOPEBITS 4
|
||||
#define PCVCAP_SCOPESIZE JS_BIT(PCVCAP_SCOPEBITS)
|
||||
#define PCVCAP_SCOPEMASK JS_BITMASK(PCVCAP_SCOPEBITS)
|
||||
|
||||
#define PCVCAP_TAGBITS (PCVCAP_PROTOBITS + PCVCAP_SCOPEBITS)
|
||||
#define PCVCAP_TAGMASK JS_BITMASK(PCVCAP_TAGBITS)
|
||||
#define PCVCAP_TAG(t) ((t) & PCVCAP_TAGMASK)
|
||||
|
||||
#define PCVCAP_MAKE(t,s,p) ((uint32(t) << PCVCAP_TAGBITS) | \
|
||||
((s) << PCVCAP_PROTOBITS) | \
|
||||
(p))
|
||||
#define PCVCAP_SHAPE(t) ((t) >> PCVCAP_TAGBITS)
|
||||
|
||||
#define SHAPE_OVERFLOW_BIT JS_BIT(32 - PCVCAP_TAGBITS)
|
||||
|
||||
struct JSPropCacheEntry {
|
||||
jsbytecode *kpc; /* pc of cache-testing bytecode */
|
||||
jsuword kshape; /* shape of direct (key) object */
|
||||
jsuword vcap; /* value capability, see above */
|
||||
jsuword vword; /* value word, see PCVAL_* below */
|
||||
|
||||
bool adding() const {
|
||||
return PCVCAP_TAG(vcap) == 0 && kshape != PCVCAP_SHAPE(vcap);
|
||||
}
|
||||
|
||||
bool directHit() const {
|
||||
return PCVCAP_TAG(vcap) == 0 && kshape == PCVCAP_SHAPE(vcap);
|
||||
}
|
||||
|
||||
static inline bool matchShape(JSContext *cx, JSObject *obj, uint32 shape);
|
||||
};
|
||||
|
||||
/*
|
||||
* Special value for functions returning JSPropCacheEntry * to distinguish
|
||||
* between failure and no no-cache-fill cases.
|
||||
*/
|
||||
#define JS_NO_PROP_CACHE_FILL ((JSPropCacheEntry *) NULL + 1)
|
||||
|
||||
#if defined DEBUG_brendan || defined DEBUG_brendaneich
|
||||
#define JS_PROPERTY_CACHE_METERING 1
|
||||
#endif
|
||||
|
||||
typedef struct JSPropertyCache {
|
||||
JSPropCacheEntry table[PROPERTY_CACHE_SIZE];
|
||||
JSBool empty;
|
||||
#ifdef JS_PROPERTY_CACHE_METERING
|
||||
JSPropCacheEntry *pctestentry; /* entry of the last PC-based test */
|
||||
uint32 fills; /* number of cache entry fills */
|
||||
uint32 nofills; /* couldn't fill (e.g. default get) */
|
||||
uint32 rofills; /* set on read-only prop can't fill */
|
||||
uint32 disfills; /* fill attempts on disabled cache */
|
||||
uint32 oddfills; /* fill attempt after setter deleted */
|
||||
uint32 modfills; /* fill that rehashed to a new entry */
|
||||
uint32 brandfills; /* scope brandings to type structural
|
||||
method fills */
|
||||
uint32 noprotos; /* resolve-returned non-proto pobj */
|
||||
uint32 longchains; /* overlong scope and/or proto chain */
|
||||
uint32 recycles; /* cache entries recycled by fills */
|
||||
uint32 tests; /* cache probes */
|
||||
uint32 pchits; /* fast-path polymorphic op hits */
|
||||
uint32 protopchits; /* pchits hitting immediate prototype */
|
||||
uint32 initests; /* cache probes from JSOP_INITPROP */
|
||||
uint32 inipchits; /* init'ing next property pchit case */
|
||||
uint32 inipcmisses; /* init'ing next property pc misses */
|
||||
uint32 settests; /* cache probes from JOF_SET opcodes */
|
||||
uint32 addpchits; /* adding next property pchit case */
|
||||
uint32 setpchits; /* setting existing property pchit */
|
||||
uint32 setpcmisses; /* setting/adding property pc misses */
|
||||
uint32 setmisses; /* JSOP_SET{NAME,PROP} total misses */
|
||||
uint32 kpcmisses; /* slow-path key id == atom misses */
|
||||
uint32 kshapemisses; /* slow-path key object misses */
|
||||
uint32 vcapmisses; /* value capability misses */
|
||||
uint32 misses; /* cache misses */
|
||||
uint32 flushes; /* cache flushes */
|
||||
uint32 pcpurges; /* shadowing purges on proto chain */
|
||||
# define PCMETER(x) x
|
||||
#else
|
||||
# define PCMETER(x) ((void)0)
|
||||
#endif
|
||||
} JSPropertyCache;
|
||||
|
||||
/*
|
||||
* Property cache value tagging/untagging macros.
|
||||
*/
|
||||
#define PCVAL_OBJECT 0
|
||||
#define PCVAL_SLOT 1
|
||||
#define PCVAL_SPROP 2
|
||||
|
||||
#define PCVAL_TAGBITS 2
|
||||
#define PCVAL_TAGMASK JS_BITMASK(PCVAL_TAGBITS)
|
||||
#define PCVAL_TAG(v) ((v) & PCVAL_TAGMASK)
|
||||
#define PCVAL_CLRTAG(v) ((v) & ~(jsuword)PCVAL_TAGMASK)
|
||||
#define PCVAL_SETTAG(v,t) ((jsuword)(v) | (t))
|
||||
|
||||
#define PCVAL_NULL 0
|
||||
#define PCVAL_IS_NULL(v) ((v) == PCVAL_NULL)
|
||||
|
||||
#define PCVAL_IS_OBJECT(v) (PCVAL_TAG(v) == PCVAL_OBJECT)
|
||||
#define PCVAL_TO_OBJECT(v) ((JSObject *) (v))
|
||||
#define OBJECT_TO_PCVAL(obj) ((jsuword) (obj))
|
||||
|
||||
#define PCVAL_OBJECT_TO_JSVAL(v) OBJECT_TO_JSVAL(PCVAL_TO_OBJECT(v))
|
||||
#define JSVAL_OBJECT_TO_PCVAL(v) OBJECT_TO_PCVAL(JSVAL_TO_OBJECT(v))
|
||||
|
||||
#define PCVAL_IS_SLOT(v) ((v) & PCVAL_SLOT)
|
||||
#define PCVAL_TO_SLOT(v) ((jsuint)(v) >> 1)
|
||||
#define SLOT_TO_PCVAL(i) (((jsuword)(i) << 1) | PCVAL_SLOT)
|
||||
|
||||
#define PCVAL_IS_SPROP(v) (PCVAL_TAG(v) == PCVAL_SPROP)
|
||||
#define PCVAL_TO_SPROP(v) ((JSScopeProperty *) PCVAL_CLRTAG(v))
|
||||
#define SPROP_TO_PCVAL(sprop) PCVAL_SETTAG(sprop, PCVAL_SPROP)
|
||||
|
||||
/*
|
||||
* Fill property cache entry for key cx->fp->pc, optimized value word computed
|
||||
* from obj and sprop, and entry capability forged from 24-bit OBJ_SHAPE(obj),
|
||||
* 4-bit scopeIndex, and 4-bit protoIndex.
|
||||
*
|
||||
* Return the filled cache entry or JS_NO_PROP_CACHE_FILL if caching was not
|
||||
* possible.
|
||||
*/
|
||||
extern JS_REQUIRES_STACK JSPropCacheEntry *
|
||||
js_FillPropertyCache(JSContext *cx, JSObject *obj,
|
||||
uintN scopeIndex, uintN protoIndex, JSObject *pobj,
|
||||
JSScopeProperty *sprop, JSBool adding = false);
|
||||
|
||||
/*
|
||||
* Property cache lookup macros. PROPERTY_CACHE_TEST 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 from PROPERTY_CACHE_TEST, if atom is null then obj points to the
|
||||
* scope chain element in which the property was found, pobj is locked, and
|
||||
* entry is valid. If atom is non-null then no object is locked but entry is
|
||||
* still set correctly for use, e.g., by js_FillPropertyCache and atom 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 scope->shape.
|
||||
*/
|
||||
#define PROPERTY_CACHE_TEST(cx, pc, obj, pobj, entry, atom) \
|
||||
do { \
|
||||
JSPropertyCache *cache_ = &JS_PROPERTY_CACHE(cx); \
|
||||
uint32 kshape_ = (JS_ASSERT(OBJ_IS_NATIVE(obj)), OBJ_SHAPE(obj)); \
|
||||
entry = &cache_->table[PROPERTY_CACHE_HASH(pc, kshape_)]; \
|
||||
PCMETER(cache_->pctestentry = entry); \
|
||||
PCMETER(cache_->tests++); \
|
||||
JS_ASSERT(&obj != &pobj); \
|
||||
if (entry->kpc == pc && entry->kshape == kshape_) { \
|
||||
JSObject *tmp_; \
|
||||
pobj = obj; \
|
||||
if (PCVCAP_TAG(entry->vcap) == 1 && \
|
||||
(tmp_ = pobj->getProto()) != NULL) { \
|
||||
pobj = tmp_; \
|
||||
} \
|
||||
\
|
||||
if (JSPropCacheEntry::matchShape(cx, pobj, \
|
||||
PCVCAP_SHAPE(entry->vcap))) { \
|
||||
PCMETER(cache_->pchits++); \
|
||||
PCMETER(!PCVCAP_TAG(entry->vcap) || cache_->protopchits++); \
|
||||
atom = NULL; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
atom = js_FullTestPropertyCache(cx, pc, &obj, &pobj, entry); \
|
||||
if (atom) \
|
||||
PCMETER(cache_->misses++); \
|
||||
} while (0)
|
||||
|
||||
extern JS_REQUIRES_STACK JSAtom *
|
||||
js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
|
||||
JSObject **objp, JSObject **pobjp,
|
||||
JSPropCacheEntry *entry);
|
||||
|
||||
/* The property cache does not need a destructor. */
|
||||
#define js_FinishPropertyCache(cache) ((void) 0)
|
||||
|
||||
extern void
|
||||
js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache);
|
||||
|
||||
extern void
|
||||
js_PurgePropertyCacheForScript(JSContext *cx, JSScript *script);
|
||||
|
||||
/*
|
||||
* Interpreter stack arena-pool alloc and free functions.
|
||||
*/
|
||||
|
@ -4520,8 +4520,8 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
||||
|
||||
if (defineHow & JSDNP_CACHE_RESULT) {
|
||||
JS_ASSERT_NOT_ON_TRACE(cx);
|
||||
JSPropCacheEntry *entry;
|
||||
entry = js_FillPropertyCache(cx, obj, 0, 0, obj, sprop, added);
|
||||
PropertyCacheEntry *entry =
|
||||
JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, sprop, added);
|
||||
TRACE_2(SetPropHit, entry, sprop);
|
||||
}
|
||||
if (propp)
|
||||
@ -4725,12 +4725,12 @@ out:
|
||||
return protoIndex;
|
||||
}
|
||||
|
||||
JSPropCacheEntry *
|
||||
PropertyCacheEntry *
|
||||
js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult,
|
||||
JSObject **objp, JSObject **pobjp, JSProperty **propp)
|
||||
{
|
||||
JSObject *scopeChain, *obj, *parent, *pobj;
|
||||
JSPropCacheEntry *entry;
|
||||
PropertyCacheEntry *entry;
|
||||
int scopeIndex, protoIndex;
|
||||
JSProperty *prop;
|
||||
|
||||
@ -4773,9 +4773,8 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult,
|
||||
}
|
||||
#endif
|
||||
if (cacheResult) {
|
||||
entry = js_FillPropertyCache(cx, scopeChain,
|
||||
scopeIndex, protoIndex, pobj,
|
||||
(JSScopeProperty *) prop);
|
||||
entry = JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, protoIndex, pobj,
|
||||
(JSScopeProperty *) prop);
|
||||
}
|
||||
SCOPE_DEPTH_ACCUM(&cx->runtime->scopeSearchDepthStats, scopeIndex);
|
||||
goto out;
|
||||
@ -4854,11 +4853,10 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id)
|
||||
JS_ASSERT(OBJ_IS_NATIVE(pobj));
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, pobj) == OBJ_GET_CLASS(cx, obj));
|
||||
#ifdef DEBUG
|
||||
JSPropCacheEntry *entry =
|
||||
PropertyCacheEntry *entry =
|
||||
#endif
|
||||
js_FillPropertyCache(cx, scopeChain,
|
||||
scopeIndex, protoIndex, pobj,
|
||||
(JSScopeProperty *) prop);
|
||||
JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, protoIndex, pobj,
|
||||
(JSScopeProperty *) prop);
|
||||
JS_ASSERT(entry);
|
||||
JS_UNLOCK_OBJ(cx, pobj);
|
||||
return obj;
|
||||
@ -5094,7 +5092,7 @@ js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN getHow,
|
||||
|
||||
if (getHow & JSGET_CACHE_RESULT) {
|
||||
JS_ASSERT_NOT_ON_TRACE(cx);
|
||||
js_FillPropertyCache(cx, aobj, 0, protoIndex, obj2, sprop);
|
||||
JS_PROPERTY_CACHE(cx).fill(cx, aobj, 0, protoIndex, obj2, sprop);
|
||||
}
|
||||
|
||||
if (!js_NativeGet(cx, obj, obj2, sprop, getHow, vp))
|
||||
@ -5283,8 +5281,8 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
|
||||
if (!sprop->hasSlot()) {
|
||||
if (defineHow & JSDNP_CACHE_RESULT) {
|
||||
JS_ASSERT_NOT_ON_TRACE(cx);
|
||||
JSPropCacheEntry *entry =
|
||||
js_FillPropertyCache(cx, obj, 0, protoIndex, pobj, sprop);
|
||||
PropertyCacheEntry *entry =
|
||||
JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, protoIndex, pobj, sprop);
|
||||
TRACE_2(SetPropHit, entry, sprop);
|
||||
}
|
||||
|
||||
@ -5384,8 +5382,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
|
||||
|
||||
if (defineHow & JSDNP_CACHE_RESULT) {
|
||||
JS_ASSERT_NOT_ON_TRACE(cx);
|
||||
JSPropCacheEntry *entry;
|
||||
entry = js_FillPropertyCache(cx, obj, 0, 0, obj, sprop, added);
|
||||
PropertyCacheEntry *entry = JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, sprop, added);
|
||||
TRACE_2(SetPropHit, entry, sprop);
|
||||
}
|
||||
|
||||
|
@ -900,7 +900,7 @@ js_IsCacheableNonGlobalScope(JSObject *obj)
|
||||
/*
|
||||
* If cacheResult is false, return JS_NO_PROP_CACHE_FILL on success.
|
||||
*/
|
||||
extern JSPropCacheEntry *
|
||||
extern js::PropertyCacheEntry *
|
||||
js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult,
|
||||
JSObject **objp, JSObject **pobjp, JSProperty **propp);
|
||||
|
||||
|
@ -687,7 +687,7 @@ END_CASE(JSOP_ENUMCONSTELEM)
|
||||
|
||||
BEGIN_CASE(JSOP_BINDNAME)
|
||||
do {
|
||||
JSPropCacheEntry *entry;
|
||||
PropertyCacheEntry *entry;
|
||||
|
||||
/*
|
||||
* We can skip the property lookup for the global object. If the
|
||||
@ -709,7 +709,7 @@ BEGIN_CASE(JSOP_BINDNAME)
|
||||
if (!obj->getParent())
|
||||
break;
|
||||
if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
|
||||
PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);
|
||||
JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
|
||||
if (!atom) {
|
||||
ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
|
||||
break;
|
||||
@ -1216,11 +1216,11 @@ BEGIN_CASE(JSOP_DECNAME)
|
||||
BEGIN_CASE(JSOP_NAMEINC)
|
||||
BEGIN_CASE(JSOP_NAMEDEC)
|
||||
{
|
||||
JSPropCacheEntry *entry;
|
||||
PropertyCacheEntry *entry;
|
||||
|
||||
obj = fp->scopeChain;
|
||||
if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
|
||||
PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);
|
||||
JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
|
||||
if (!atom) {
|
||||
ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
|
||||
if (obj == obj2 && PCVAL_IS_SLOT(entry->vword)) {
|
||||
@ -1475,7 +1475,7 @@ BEGIN_CASE(JSOP_GETXPROP)
|
||||
do_getprop_with_obj:
|
||||
do {
|
||||
JSObject *aobj;
|
||||
JSPropCacheEntry *entry;
|
||||
PropertyCacheEntry *entry;
|
||||
|
||||
/*
|
||||
* We do not impose the method read barrier if in an imacro,
|
||||
@ -1484,7 +1484,7 @@ BEGIN_CASE(JSOP_GETXPROP)
|
||||
*/
|
||||
aobj = js_GetProtoIfDenseArray(obj);
|
||||
if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {
|
||||
PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom);
|
||||
JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
|
||||
if (!atom) {
|
||||
ASSERT_VALID_PROPERTY_CACHE_HIT(i, aobj, obj2, entry);
|
||||
if (PCVAL_IS_OBJECT(entry->vword)) {
|
||||
@ -1556,7 +1556,7 @@ END_CASE(JSOP_LENGTH)
|
||||
BEGIN_CASE(JSOP_CALLPROP)
|
||||
{
|
||||
JSObject *aobj;
|
||||
JSPropCacheEntry *entry;
|
||||
PropertyCacheEntry *entry;
|
||||
|
||||
lval = FETCH_OPND(-1);
|
||||
if (!JSVAL_IS_PRIMITIVE(lval)) {
|
||||
@ -1580,7 +1580,7 @@ BEGIN_CASE(JSOP_CALLPROP)
|
||||
|
||||
aobj = js_GetProtoIfDenseArray(obj);
|
||||
if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {
|
||||
PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom);
|
||||
JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, atom);
|
||||
if (!atom) {
|
||||
ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry);
|
||||
if (PCVAL_IS_OBJECT(entry->vword)) {
|
||||
@ -1605,7 +1605,7 @@ BEGIN_CASE(JSOP_CALLPROP)
|
||||
|
||||
/*
|
||||
* Cache miss: use the immediate atom that was loaded for us under
|
||||
* PROPERTY_CACHE_TEST.
|
||||
* PropertyCache::test.
|
||||
*/
|
||||
id = ATOM_TO_JSID(atom);
|
||||
PUSH(JSVAL_NULL);
|
||||
@ -1671,16 +1671,16 @@ BEGIN_CASE(JSOP_SETMETHOD)
|
||||
VALUE_TO_OBJECT(cx, -2, lval, obj);
|
||||
|
||||
do {
|
||||
JSPropCacheEntry *entry;
|
||||
PropertyCacheEntry *entry;
|
||||
|
||||
entry = NULL;
|
||||
atom = NULL;
|
||||
if (JS_LIKELY(obj->map->ops->setProperty == js_SetProperty)) {
|
||||
JSPropertyCache *cache = &JS_PROPERTY_CACHE(cx);
|
||||
PropertyCache *cache = &JS_PROPERTY_CACHE(cx);
|
||||
uint32 kshape = OBJ_SHAPE(obj);
|
||||
|
||||
/*
|
||||
* Open-code PROPERTY_CACHE_TEST, specializing for two important
|
||||
* Open-code PropertyCache::test, specializing for two important
|
||||
* set-property cases. First:
|
||||
*
|
||||
* function f(a, b, c) {
|
||||
@ -1698,12 +1698,12 @@ BEGIN_CASE(JSOP_SETMETHOD)
|
||||
* (possibly after the first iteration) always exist in native
|
||||
* object o.
|
||||
*/
|
||||
entry = &cache->table[PROPERTY_CACHE_HASH(regs.pc, kshape)];
|
||||
entry = &cache->table[PropertyCache::hash(regs.pc, kshape)];
|
||||
PCMETER(cache->pctestentry = entry);
|
||||
PCMETER(cache->tests++);
|
||||
PCMETER(cache->settests++);
|
||||
if (entry->kpc == regs.pc && entry->kshape == kshape &&
|
||||
JSPropCacheEntry::matchShape(cx, obj, kshape)) {
|
||||
PropertyCache::matchShape(cx, obj, kshape)) {
|
||||
/*
|
||||
* Property cache hit: either predicting a new property to be
|
||||
* added directly to obj by this set, or on an existing "own"
|
||||
@ -1712,7 +1712,7 @@ BEGIN_CASE(JSOP_SETMETHOD)
|
||||
JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
|
||||
sprop = PCVAL_TO_SPROP(entry->vword);
|
||||
JS_ASSERT(sprop->writable());
|
||||
JS_ASSERT_IF(sprop->hasSlot(), PCVCAP_TAG(entry->vcap) == 0);
|
||||
JS_ASSERT_IF(sprop->hasSlot(), entry->vcapTag() == 0);
|
||||
|
||||
JSScope *scope = OBJ_SCOPE(obj);
|
||||
JS_ASSERT(!scope->sealed());
|
||||
@ -1725,10 +1725,10 @@ BEGIN_CASE(JSOP_SETMETHOD)
|
||||
*/
|
||||
bool checkForAdd;
|
||||
if (!sprop->hasSlot()) {
|
||||
if (PCVCAP_TAG(entry->vcap) == 0 ||
|
||||
if (entry->vcapTag() == 0 ||
|
||||
((obj2 = obj->getProto()) &&
|
||||
OBJ_IS_NATIVE(obj2) &&
|
||||
OBJ_SHAPE(obj2) == PCVCAP_SHAPE(entry->vcap))) {
|
||||
OBJ_SHAPE(obj2) == entry->vshape())) {
|
||||
goto fast_set_propcache_hit;
|
||||
}
|
||||
|
||||
@ -1758,7 +1758,7 @@ BEGIN_CASE(JSOP_SETMETHOD)
|
||||
}
|
||||
|
||||
if (checkForAdd &&
|
||||
PCVCAP_SHAPE(entry->vcap) == rt->protoHazardShape &&
|
||||
entry->vshape() == rt->protoHazardShape &&
|
||||
sprop->hasDefaultSetter() &&
|
||||
(slot = sprop->slot) == scope->freeslot) {
|
||||
/*
|
||||
@ -1835,7 +1835,7 @@ BEGIN_CASE(JSOP_SETMETHOD)
|
||||
PCMETER(cache->setpcmisses++);
|
||||
}
|
||||
|
||||
atom = js_FullTestPropertyCache(cx, regs.pc, &obj, &obj2, entry);
|
||||
atom = cache->fullTest(cx, regs.pc, &obj, &obj2, entry);
|
||||
if (atom) {
|
||||
PCMETER(cache->misses++);
|
||||
PCMETER(cache->setmisses++);
|
||||
@ -2288,11 +2288,11 @@ END_CASE(JSOP_SETCALL)
|
||||
BEGIN_CASE(JSOP_NAME)
|
||||
BEGIN_CASE(JSOP_CALLNAME)
|
||||
{
|
||||
JSPropCacheEntry *entry;
|
||||
PropertyCacheEntry *entry;
|
||||
|
||||
obj = fp->scopeChain;
|
||||
if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
|
||||
PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);
|
||||
JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, atom);
|
||||
if (!atom) {
|
||||
ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
|
||||
if (PCVAL_IS_OBJECT(entry->vword)) {
|
||||
@ -3372,8 +3372,8 @@ BEGIN_CASE(JSOP_INITMETHOD)
|
||||
do {
|
||||
JSScope *scope;
|
||||
uint32 kshape;
|
||||
JSPropertyCache *cache;
|
||||
JSPropCacheEntry *entry;
|
||||
PropertyCache *cache;
|
||||
PropertyCacheEntry *entry;
|
||||
|
||||
/*
|
||||
* We can not assume that the object created by JSOP_NEWINIT is still
|
||||
@ -3387,15 +3387,15 @@ BEGIN_CASE(JSOP_INITMETHOD)
|
||||
JS_ASSERT(!scope->sealed());
|
||||
kshape = scope->shape;
|
||||
cache = &JS_PROPERTY_CACHE(cx);
|
||||
entry = &cache->table[PROPERTY_CACHE_HASH(regs.pc, kshape)];
|
||||
entry = &cache->table[PropertyCache::hash(regs.pc, kshape)];
|
||||
PCMETER(cache->pctestentry = entry);
|
||||
PCMETER(cache->tests++);
|
||||
PCMETER(cache->initests++);
|
||||
|
||||
if (entry->kpc == regs.pc &&
|
||||
entry->kshape == kshape &&
|
||||
PCVCAP_SHAPE(entry->vcap) == rt->protoHazardShape) {
|
||||
JS_ASSERT(PCVCAP_TAG(entry->vcap) == 0);
|
||||
entry->vshape() == rt->protoHazardShape) {
|
||||
JS_ASSERT(entry->vcapTag() == 0);
|
||||
|
||||
PCMETER(cache->pchits++);
|
||||
PCMETER(cache->inipchits++);
|
||||
|
@ -863,7 +863,7 @@ struct JSFunctionBox : public JSObjectBox
|
||||
*
|
||||
* We despecialize from caching function objects, caching slots or sprops
|
||||
* instead, because an unbranded object may still have joined methods (for
|
||||
* which sprop->isMethod), since js_FillPropertyCache gives precedence to
|
||||
* which sprop->isMethod), since PropertyCache::fill gives precedence to
|
||||
* joined methods over branded methods.
|
||||
*/
|
||||
bool shouldUnbrand(uintN methods, uintN slowMethods) const;
|
||||
|
504
js/src/jspropertycache.cpp
Normal file
504
js/src/jspropertycache.cpp
Normal file
@ -0,0 +1,504 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=98:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code, released
|
||||
* March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1998
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "jspropertycache.h"
|
||||
#include "jspropertycacheinlines.h"
|
||||
#include "jscntxt.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
JS_REQUIRES_STACK PropertyCacheEntry *
|
||||
PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN protoIndex,
|
||||
JSObject *pobj, JSScopeProperty *sprop, JSBool adding)
|
||||
{
|
||||
jsbytecode *pc;
|
||||
JSScope *scope;
|
||||
jsuword kshape, vshape;
|
||||
JSOp op;
|
||||
const JSCodeSpec *cs;
|
||||
jsuword vword;
|
||||
PropertyCacheEntry *entry;
|
||||
|
||||
JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
|
||||
JS_ASSERT(!cx->runtime->gcRunning);
|
||||
|
||||
/* FIXME bug 489098: consider enabling the property cache for eval. */
|
||||
if (js_IsPropertyCacheDisabled(cx) || (cx->fp->flags & JSFRAME_EVAL)) {
|
||||
PCMETER(disfills++);
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for fill from js_SetPropertyHelper where the setter removed sprop
|
||||
* from pobj's scope (via unwatch or delete, e.g.).
|
||||
*/
|
||||
scope = OBJ_SCOPE(pobj);
|
||||
if (!scope->hasProperty(sprop)) {
|
||||
PCMETER(oddfills++);
|
||||
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 js_LookupPropertyWithFlags has returned the nominal protoIndex,
|
||||
* we have to validate protoIndex if it is non-zero. If it is zero, then
|
||||
* we know thanks to the scope->hasProperty test above, combined with the
|
||||
* fact that obj == pobj, that protoIndex is invariant.
|
||||
*
|
||||
* The scopeIndex can't be wrong. We require JS_SetParent calls to happen
|
||||
* before any running script might consult a parent-linked scope chain. If
|
||||
* this requirement is not satisfied, the fill in progress will never hit,
|
||||
* but vcap vs. scope shape tests ensure nothing malfunctions.
|
||||
*/
|
||||
JS_ASSERT_IF(scopeIndex == 0 && protoIndex == 0, obj == pobj);
|
||||
|
||||
if (protoIndex != 0) {
|
||||
JSObject *tmp = obj;
|
||||
|
||||
for (uintN i = 0; i != scopeIndex; i++)
|
||||
tmp = tmp->getParent();
|
||||
JS_ASSERT(tmp != pobj);
|
||||
|
||||
protoIndex = 1;
|
||||
for (;;) {
|
||||
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 || !OBJ_IS_NATIVE(tmp)) {
|
||||
PCMETER(noprotos++);
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
if (tmp == pobj)
|
||||
break;
|
||||
++protoIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) {
|
||||
PCMETER(longchains++);
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Optimize the cached vword based on our parameters and the current pc's
|
||||
* opcode format flags.
|
||||
*/
|
||||
pc = cx->fp->regs->pc;
|
||||
op = js_GetOpcode(cx, cx->fp->script, pc);
|
||||
cs = &js_CodeSpec[op];
|
||||
kshape = 0;
|
||||
|
||||
do {
|
||||
/*
|
||||
* Check for a prototype "plain old method" callee computation. What
|
||||
* is a plain old method? It's a function-valued property with stub
|
||||
* getter, so get of a function is idempotent.
|
||||
*/
|
||||
if (cs->format & JOF_CALLOP) {
|
||||
jsval v;
|
||||
|
||||
if (sprop->isMethod()) {
|
||||
/*
|
||||
* A compiler-created function object, AKA a method, already
|
||||
* memoized in the property tree.
|
||||
*/
|
||||
JS_ASSERT(scope->hasMethodBarrier());
|
||||
v = sprop->methodValue();
|
||||
JS_ASSERT(VALUE_IS_FUNCTION(cx, v));
|
||||
JS_ASSERT(v == LOCKED_OBJ_GET_SLOT(pobj, sprop->slot));
|
||||
vword = JSVAL_OBJECT_TO_PCVAL(v);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!scope->generic() &&
|
||||
sprop->hasDefaultGetter() &&
|
||||
SPROP_HAS_VALID_SLOT(sprop, scope)) {
|
||||
v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
|
||||
if (VALUE_IS_FUNCTION(cx, v)) {
|
||||
/*
|
||||
* Great, we have a function-valued prototype property
|
||||
* where the getter is JS_PropertyStub. The type id in
|
||||
* pobj's scope does not evolve with changes to property
|
||||
* values, however.
|
||||
*
|
||||
* So here, on first cache fill for this method, we brand
|
||||
* the scope with a new shape and set the JSScope::BRANDED
|
||||
* flag. Once this flag is set, any property assignment
|
||||
* that changes the value from or to a different function
|
||||
* object will result in shape being regenerated.
|
||||
*/
|
||||
if (!scope->branded()) {
|
||||
PCMETER(brandfills++);
|
||||
#ifdef DEBUG_notme
|
||||
fprintf(stderr,
|
||||
"branding %p (%s) for funobj %p (%s), shape %lu\n",
|
||||
pobj, pobj->getClass()->name,
|
||||
JSVAL_TO_OBJECT(v),
|
||||
JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v))),
|
||||
OBJ_SHAPE(obj));
|
||||
#endif
|
||||
if (!scope->brand(cx, sprop->slot, v))
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
vword = JSVAL_OBJECT_TO_PCVAL(v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If getting a value via a stub getter, we can cache the slot. */
|
||||
if (!(cs->format & (JOF_SET | JOF_INCDEC | JOF_FOR)) &&
|
||||
sprop->hasDefaultGetter() &&
|
||||
SPROP_HAS_VALID_SLOT(sprop, scope)) {
|
||||
/* Great, let's cache sprop's slot and use it on cache hit. */
|
||||
vword = SLOT_TO_PCVAL(sprop->slot);
|
||||
} else {
|
||||
/* Best we can do is to cache sprop (still a nice speedup). */
|
||||
vword = SPROP_TO_PCVAL(sprop);
|
||||
if (adding &&
|
||||
sprop == scope->lastProperty() &&
|
||||
scope->shape == sprop->shape) {
|
||||
/*
|
||||
* Our caller added a new property. We also know that a setter
|
||||
* that js_NativeSet could have run has not mutated the scope,
|
||||
* so the added property is still the last one added, and the
|
||||
* scope is not branded.
|
||||
*
|
||||
* We want to cache under scope's shape before the property
|
||||
* addition to bias for the case when the mutator opcode
|
||||
* always adds the same property. This allows us to optimize
|
||||
* periodic execution of object initializers or other explicit
|
||||
* initialization sequences such as
|
||||
*
|
||||
* obj = {}; obj.x = 1; obj.y = 2;
|
||||
*
|
||||
* We assume that on average the win from this optimization is
|
||||
* greater than the cost of an extra mismatch per loop owing to
|
||||
* the bias for the following case:
|
||||
*
|
||||
* obj = {}; ... for (...) { ... obj.x = ... }
|
||||
*
|
||||
* On the first iteration of such a for loop, JSOP_SETPROP
|
||||
* fills the cache with the shape of the newly created object
|
||||
* obj, not the shape of obj after obj.x has been assigned.
|
||||
* That mismatches obj's shape on the second iteration. Note
|
||||
* that on the third and subsequent iterations the cache will
|
||||
* be hit because the shape is no longer updated.
|
||||
*/
|
||||
JS_ASSERT(!scope->isSharedEmpty());
|
||||
if (sprop->parent) {
|
||||
kshape = sprop->parent->shape;
|
||||
} else {
|
||||
/*
|
||||
* If obj had its own empty scope before, with a unique
|
||||
* shape, that is lost. Here we only attempt to find a
|
||||
* matching empty scope. In unusual cases involving
|
||||
* __proto__ assignment we may not find one.
|
||||
*/
|
||||
JSObject *proto = obj->getProto();
|
||||
if (!proto || !OBJ_IS_NATIVE(proto))
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
JSScope *protoscope = OBJ_SCOPE(proto);
|
||||
if (!protoscope->emptyScope ||
|
||||
protoscope->emptyScope->clasp != obj->getClass()) {
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
kshape = protoscope->emptyScope->shape;
|
||||
}
|
||||
|
||||
/*
|
||||
* When adding we predict no prototype object will later gain a
|
||||
* readonly property or setter.
|
||||
*/
|
||||
vshape = cx->runtime->protoHazardShape;
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (kshape == 0) {
|
||||
kshape = OBJ_SHAPE(obj);
|
||||
vshape = scope->shape;
|
||||
}
|
||||
JS_ASSERT(kshape < SHAPE_OVERFLOW_BIT);
|
||||
|
||||
if (obj == pobj) {
|
||||
JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
if (scopeIndex == 0) {
|
||||
JS_ASSERT(protoIndex != 0);
|
||||
JS_ASSERT((protoIndex == 1) == (obj->getProto() == pobj));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (scopeIndex != 0 || protoIndex != 1) {
|
||||
/*
|
||||
* Make sure that a later shadowing assignment will enter
|
||||
* PurgeProtoChain and invalidate this entry, bug 479198.
|
||||
*
|
||||
* This is thread-safe even though obj is not locked. Only the
|
||||
* DELEGATE bit of obj->classword can change at runtime, given that
|
||||
* obj is native; and the bit is only set, never cleared. And on
|
||||
* platforms where another CPU can fail to see this write, it's OK
|
||||
* because the property cache and JIT cache are thread-local.
|
||||
*/
|
||||
obj->setDelegate();
|
||||
}
|
||||
}
|
||||
JS_ASSERT(vshape < SHAPE_OVERFLOW_BIT);
|
||||
|
||||
entry = &table[hash(pc, kshape)];
|
||||
PCMETER(PCVAL_IS_NULL(entry->vword) || recycles++);
|
||||
entry->assign(pc, kshape, vshape, scopeIndex, protoIndex, vword);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static inline JSAtom *
|
||||
GetAtomFromBytecode(JSContext *cx, jsbytecode *pc, JSOp op, const JSCodeSpec &cs)
|
||||
{
|
||||
if (op == JSOP_LENGTH)
|
||||
return cx->runtime->atomState.lengthAtom;
|
||||
|
||||
ptrdiff_t pcoff = (JOF_TYPE(cs.format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
|
||||
JSAtom *atom;
|
||||
GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
|
||||
return atom;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK JSAtom *
|
||||
PropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject **pobjp,
|
||||
PropertyCacheEntry *entry)
|
||||
{
|
||||
JSObject *obj, *pobj, *tmp;
|
||||
uint32 vcap;
|
||||
|
||||
JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
|
||||
JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code)
|
||||
< cx->fp->script->length);
|
||||
|
||||
JSOp op = js_GetOpcode(cx, cx->fp->script, pc);
|
||||
const JSCodeSpec &cs = js_CodeSpec[op];
|
||||
|
||||
obj = *objp;
|
||||
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
||||
vcap = entry->vcap;
|
||||
|
||||
if (entry->kpc != pc) {
|
||||
PCMETER(kpcmisses++);
|
||||
|
||||
JSAtom *atom = GetAtomFromBytecode(cx, pc, op, cs);
|
||||
#ifdef DEBUG_notme
|
||||
fprintf(stderr,
|
||||
"id miss for %s from %s:%u"
|
||||
" (pc %u, kpc %u, kshape %u, shape %u)\n",
|
||||
js_AtomToPrintableString(cx, atom),
|
||||
cx->fp->script->filename,
|
||||
js_PCToLineNumber(cx, cx->fp->script, pc),
|
||||
pc - cx->fp->script->code,
|
||||
entry->kpc - cx->fp->script->code,
|
||||
entry->kshape,
|
||||
OBJ_SHAPE(obj));
|
||||
js_Disassemble1(cx, cx->fp->script, pc,
|
||||
pc - cx->fp->script->code,
|
||||
JS_FALSE, stderr);
|
||||
#endif
|
||||
|
||||
return atom;
|
||||
}
|
||||
|
||||
if (entry->kshape != OBJ_SHAPE(obj)) {
|
||||
PCMETER(kshapemisses++);
|
||||
return GetAtomFromBytecode(cx, pc, op, cs);
|
||||
}
|
||||
|
||||
/*
|
||||
* PropertyCache::test handles only the direct and immediate-prototype hit
|
||||
* cases. All others go here. We could embed the target object in the cache
|
||||
* entry but then entry size would be 5 words. Instead we traverse chains.
|
||||
*/
|
||||
pobj = obj;
|
||||
|
||||
if (JOF_MODE(cs.format) == JOF_NAME) {
|
||||
while (vcap & (PCVCAP_SCOPEMASK << PCVCAP_PROTOBITS)) {
|
||||
tmp = pobj->getParent();
|
||||
if (!tmp || !OBJ_IS_NATIVE(tmp))
|
||||
break;
|
||||
pobj = tmp;
|
||||
vcap -= PCVCAP_PROTOSIZE;
|
||||
}
|
||||
|
||||
*objp = pobj;
|
||||
}
|
||||
|
||||
while (vcap & PCVCAP_PROTOMASK) {
|
||||
tmp = pobj->getProto();
|
||||
if (!tmp || !OBJ_IS_NATIVE(tmp))
|
||||
break;
|
||||
pobj = tmp;
|
||||
--vcap;
|
||||
}
|
||||
|
||||
if (matchShape(cx, pobj, vcap >> PCVCAP_TAGBITS)) {
|
||||
#ifdef DEBUG
|
||||
JSAtom *atom = GetAtomFromBytecode(cx, pc, op, cs);
|
||||
jsid id = ATOM_TO_JSID(atom);
|
||||
|
||||
id = js_CheckForStringIndex(id);
|
||||
JS_ASSERT(OBJ_SCOPE(pobj)->lookup(id));
|
||||
JS_ASSERT_IF(OBJ_SCOPE(pobj)->object, OBJ_SCOPE(pobj)->object == pobj);
|
||||
#endif
|
||||
*pobjp = pobj;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PCMETER(vcapmisses++);
|
||||
return GetAtomFromBytecode(cx, pc, op, cs);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
PropertyCache::assertEmpty()
|
||||
{
|
||||
JS_ASSERT(empty);
|
||||
for (uintN i = 0; i < SIZE; i++) {
|
||||
JS_ASSERT(!table[i].kpc);
|
||||
JS_ASSERT(!table[i].kshape);
|
||||
JS_ASSERT(!table[i].vcap);
|
||||
JS_ASSERT(!table[i].vword);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
JS_STATIC_ASSERT(PCVAL_NULL == 0);
|
||||
|
||||
void
|
||||
PropertyCache::purge(JSContext *cx)
|
||||
{
|
||||
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);
|
||||
#ifdef JS_THREADSAFE
|
||||
fprintf(fp, "thread %lu, ", (unsigned long) cx->thread->id);
|
||||
#endif
|
||||
fprintf(fp, "GC %u\n", cx->runtime->gcNumber);
|
||||
|
||||
# define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)mem)
|
||||
P(fills);
|
||||
P(nofills);
|
||||
P(rofills);
|
||||
P(disfills);
|
||||
P(oddfills);
|
||||
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::purgeForScript(JSScript *script)
|
||||
{
|
||||
for (PropertyCacheEntry *entry = table; entry < table + SIZE; entry++) {
|
||||
if (JS_UPTRDIFF(entry->kpc, script->code) < script->length) {
|
||||
entry->kpc = NULL;
|
||||
#ifdef DEBUG
|
||||
entry->kshape = entry->vcap = entry->vword = 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
235
js/src/jspropertycache.h
Normal file
235
js/src/jspropertycache.h
Normal file
@ -0,0 +1,235 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=98:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Communicator client code, released
|
||||
* March 31, 1998.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1998
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef jspropertycache_h___
|
||||
#define jspropertycache_h___
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "jsprvtd.h"
|
||||
#include "jstypes.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>.
|
||||
*/
|
||||
|
||||
/* Property cache value capabilities. */
|
||||
enum {
|
||||
PCVCAP_PROTOBITS = 4,
|
||||
PCVCAP_PROTOSIZE = JS_BIT(PCVCAP_PROTOBITS),
|
||||
PCVCAP_PROTOMASK = JS_BITMASK(PCVCAP_PROTOBITS),
|
||||
|
||||
PCVCAP_SCOPEBITS = 4,
|
||||
PCVCAP_SCOPESIZE = JS_BIT(PCVCAP_SCOPEBITS),
|
||||
PCVCAP_SCOPEMASK = JS_BITMASK(PCVCAP_SCOPEBITS),
|
||||
|
||||
PCVCAP_TAGBITS = PCVCAP_PROTOBITS + PCVCAP_SCOPEBITS,
|
||||
PCVCAP_TAGMASK = JS_BITMASK(PCVCAP_TAGBITS)
|
||||
};
|
||||
|
||||
const uint32 SHAPE_OVERFLOW_BIT = JS_BIT(32 - PCVCAP_TAGBITS);
|
||||
|
||||
struct PropertyCacheEntry
|
||||
{
|
||||
jsbytecode *kpc; /* pc of cache-testing bytecode */
|
||||
jsuword kshape; /* shape of direct (key) object */
|
||||
jsuword vcap; /* value capability, see above */
|
||||
jsuword vword; /* value word, see PCVAL_* below */
|
||||
|
||||
bool adding() const { return vcapTag() == 0 && kshape != vshape(); }
|
||||
bool directHit() const { return vcapTag() == 0 && kshape == vshape(); }
|
||||
|
||||
jsuword vcapTag() const { return vcap & PCVCAP_TAGMASK; }
|
||||
uint32 vshape() const { return uint32(vcap >> PCVCAP_TAGBITS); }
|
||||
jsuword scopeIndex() const { return (vcap >> PCVCAP_PROTOBITS) & PCVCAP_SCOPEMASK; }
|
||||
jsuword protoIndex() const { return vcap & PCVCAP_PROTOMASK; }
|
||||
|
||||
void assign(jsbytecode *kpc, jsuword kshape, jsuword vshape,
|
||||
uintN scopeIndex, uintN protoIndex, jsuword vword) {
|
||||
JS_ASSERT(kshape < SHAPE_OVERFLOW_BIT);
|
||||
JS_ASSERT(vshape < SHAPE_OVERFLOW_BIT);
|
||||
JS_ASSERT(scopeIndex <= PCVCAP_SCOPEMASK);
|
||||
JS_ASSERT(protoIndex <= PCVCAP_PROTOMASK);
|
||||
|
||||
this->kpc = kpc;
|
||||
this->kshape = kshape;
|
||||
this->vcap = (vshape << PCVCAP_TAGBITS) | (scopeIndex << PCVCAP_PROTOBITS) | protoIndex;
|
||||
this->vword = vword;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
struct PropertyCache
|
||||
{
|
||||
enum {
|
||||
SIZE_LOG2 = 12,
|
||||
SIZE = JS_BIT(SIZE_LOG2),
|
||||
MASK = JS_BITMASK(SIZE_LOG2)
|
||||
};
|
||||
|
||||
PropertyCacheEntry table[SIZE];
|
||||
JSBool empty;
|
||||
#ifdef JS_PROPERTY_CACHE_METERING
|
||||
PropertyCacheEntry *pctestentry; /* entry of the last PC-based test */
|
||||
uint32 fills; /* number of cache entry fills */
|
||||
uint32 nofills; /* couldn't fill (e.g. default get) */
|
||||
uint32 rofills; /* set on read-only prop can't fill */
|
||||
uint32 disfills; /* fill attempts on disabled cache */
|
||||
uint32 oddfills; /* fill attempt after setter deleted */
|
||||
uint32 modfills; /* fill that rehashed to a new entry */
|
||||
uint32 brandfills; /* scope brandings to type structural
|
||||
method fills */
|
||||
uint32 noprotos; /* resolve-returned non-proto pobj */
|
||||
uint32 longchains; /* overlong scope and/or proto chain */
|
||||
uint32 recycles; /* cache entries recycled by fills */
|
||||
uint32 tests; /* cache probes */
|
||||
uint32 pchits; /* fast-path polymorphic op hits */
|
||||
uint32 protopchits; /* pchits hitting immediate prototype */
|
||||
uint32 initests; /* cache probes from JSOP_INITPROP */
|
||||
uint32 inipchits; /* init'ing next property pchit case */
|
||||
uint32 inipcmisses; /* init'ing next property pc misses */
|
||||
uint32 settests; /* cache probes from JOF_SET opcodes */
|
||||
uint32 addpchits; /* adding next property pchit case */
|
||||
uint32 setpchits; /* setting existing property pchit */
|
||||
uint32 setpcmisses; /* setting/adding property pc misses */
|
||||
uint32 setmisses; /* JSOP_SET{NAME,PROP} total misses */
|
||||
uint32 kpcmisses; /* slow-path key id == atom misses */
|
||||
uint32 kshapemisses; /* slow-path key object misses */
|
||||
uint32 vcapmisses; /* value capability misses */
|
||||
uint32 misses; /* cache misses */
|
||||
uint32 flushes; /* cache flushes */
|
||||
uint32 pcpurges; /* shadowing purges on proto chain */
|
||||
# define PCMETER(x) x
|
||||
#else
|
||||
# define PCMETER(x) ((void)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Add kshape rather than xor it to avoid collisions between nearby bytecode
|
||||
* that are evolving an object by setting successive properties, incrementing
|
||||
* the object's scope->shape on each set.
|
||||
*/
|
||||
static inline jsuword
|
||||
hash(jsbytecode *pc, jsuword kshape)
|
||||
{
|
||||
return ((((jsuword(pc) >> SIZE_LOG2) ^ jsuword(pc)) + kshape) & MASK);
|
||||
}
|
||||
|
||||
static inline bool matchShape(JSContext *cx, JSObject *obj, uint32 shape);
|
||||
|
||||
JS_ALWAYS_INLINE JS_REQUIRES_STACK void test(JSContext *cx, jsbytecode *pc,
|
||||
JSObject *&obj, JSObject *&pobj,
|
||||
PropertyCacheEntry *&entry, JSAtom *&atom);
|
||||
|
||||
JS_REQUIRES_STACK JSAtom *fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp,
|
||||
JSObject **pobjp, PropertyCacheEntry *entry);
|
||||
|
||||
/*
|
||||
* Fill property cache entry for key cx->fp->pc, optimized value word
|
||||
* computed from obj and sprop, and entry capability forged from 24-bit
|
||||
* OBJ_SHAPE(obj), 4-bit scopeIndex, and 4-bit protoIndex.
|
||||
*
|
||||
* Return the filled cache entry or JS_NO_PROP_CACHE_FILL if caching was
|
||||
* not possible.
|
||||
*/
|
||||
JS_REQUIRES_STACK PropertyCacheEntry *fill(JSContext *cx, JSObject *obj, uintN scopeIndex,
|
||||
uintN protoIndex, JSObject *pobj,
|
||||
JSScopeProperty *sprop, JSBool adding = false);
|
||||
|
||||
void purge(JSContext *cx);
|
||||
void purgeForScript(JSScript *script);
|
||||
|
||||
#ifdef DEBUG
|
||||
void assertEmpty();
|
||||
#else
|
||||
inline void assertEmpty() {}
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Property cache value tagging/untagging macros.
|
||||
*/
|
||||
enum {
|
||||
PCVAL_OBJECT = 0,
|
||||
PCVAL_SLOT = 1,
|
||||
PCVAL_SPROP = 2,
|
||||
|
||||
PCVAL_TAGBITS = 2,
|
||||
PCVAL_TAGMASK = JS_BITMASK(PCVAL_TAGBITS),
|
||||
|
||||
PCVAL_NULL = 0
|
||||
};
|
||||
|
||||
inline jsuword PCVAL_TAG(jsuword v) { return v & PCVAL_TAGMASK; }
|
||||
inline jsuword PCVAL_CLRTAG(jsuword v) { return v & ~jsuword(PCVAL_TAGMASK); }
|
||||
inline jsuword PCVAL_SETTAG(jsuword v, jsuword t) { return v | t; }
|
||||
|
||||
inline bool PCVAL_IS_NULL(jsuword v) { return v == PCVAL_NULL; }
|
||||
|
||||
inline bool PCVAL_IS_OBJECT(jsuword v) { return PCVAL_TAG(v) == PCVAL_OBJECT; }
|
||||
inline JSObject *PCVAL_TO_OBJECT(jsuword v) { JS_ASSERT(PCVAL_IS_OBJECT(v)); return (JSObject *) v; }
|
||||
inline jsuword OBJECT_TO_PCVAL(JSObject *obj) { return jsuword(obj); }
|
||||
|
||||
inline jsval PCVAL_OBJECT_TO_JSVAL(jsuword v) { return OBJECT_TO_JSVAL(PCVAL_TO_OBJECT(v)); }
|
||||
inline jsuword JSVAL_OBJECT_TO_PCVAL(jsval v) { return OBJECT_TO_PCVAL(JSVAL_TO_OBJECT(v)); }
|
||||
|
||||
inline bool PCVAL_IS_SLOT(jsuword v) { return v & PCVAL_SLOT; }
|
||||
inline jsuint PCVAL_TO_SLOT(jsuword v) { JS_ASSERT(PCVAL_IS_SLOT(v)); return jsuint(v) >> 1; }
|
||||
inline jsuword SLOT_TO_PCVAL(jsuint i) { return (jsuword(i) << 1) | PCVAL_SLOT; }
|
||||
|
||||
inline bool PCVAL_IS_SPROP(jsuword v) { return PCVAL_TAG(v) == PCVAL_SPROP; }
|
||||
inline JSScopeProperty *PCVAL_TO_SPROP(jsuword v) { JS_ASSERT(PCVAL_IS_SPROP(v)); return (JSScopeProperty *) PCVAL_CLRTAG(v); }
|
||||
inline jsuword SPROP_TO_PCVAL(JSScopeProperty *sprop) { return PCVAL_SETTAG(jsuword(sprop), PCVAL_SPROP); }
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* jspropertycache_h___ */
|
@ -39,17 +39,65 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef jsinterpinlines_h___
|
||||
#define jsinterpinlines_h___
|
||||
#ifndef jspropertycacheinlines_h___
|
||||
#define jspropertycacheinlines_h___
|
||||
|
||||
#include "jsinterp.h"
|
||||
#include "jslock.h"
|
||||
#include "jspropertycache.h"
|
||||
#include "jsscope.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
/* static */ inline bool
|
||||
JSPropCacheEntry::matchShape(JSContext *cx, JSObject *obj, uint32 shape)
|
||||
PropertyCache::matchShape(JSContext *cx, JSObject *obj, uint32 shape)
|
||||
{
|
||||
return CX_OWNS_OBJECT_TITLE(cx, obj) && OBJ_SHAPE(obj) == shape;
|
||||
}
|
||||
|
||||
#endif /* jsinterpinlines_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 atom is null then obj points to the scope chain element in
|
||||
* which the property was found, pobj is locked, and entry is valid. If atom is
|
||||
* non-null then no object is locked but entry is still set correctly for use,
|
||||
* e.g., by PropertyCache::fill and atom 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 scope->shape.
|
||||
*/
|
||||
JS_ALWAYS_INLINE void
|
||||
PropertyCache::test(JSContext *cx, jsbytecode *pc, JSObject *&obj,
|
||||
JSObject *&pobj, PropertyCacheEntry *&entry, JSAtom *&atom)
|
||||
{
|
||||
JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
|
||||
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
||||
|
||||
uint32 kshape = OBJ_SHAPE(obj);
|
||||
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->vcapTag() == 1 &&
|
||||
(tmp = pobj->getProto()) != NULL) {
|
||||
pobj = tmp;
|
||||
}
|
||||
|
||||
if (matchShape(cx, pobj, entry->vshape())) {
|
||||
PCMETER(pchits++);
|
||||
PCMETER(!entry->vcapTag() || protopchits++);
|
||||
atom = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
atom = fullTest(cx, pc, &obj, &pobj, entry);
|
||||
if (atom)
|
||||
PCMETER(misses++);
|
||||
}
|
||||
|
||||
#endif /* jspropertycacheinlines_h___ */
|
@ -98,7 +98,6 @@ typedef struct JSCompiler JSCompiler;
|
||||
typedef struct JSFunctionBox JSFunctionBox;
|
||||
typedef struct JSObjectBox JSObjectBox;
|
||||
typedef struct JSParseNode JSParseNode;
|
||||
typedef struct JSPropCacheEntry JSPropCacheEntry;
|
||||
typedef struct JSProperty JSProperty;
|
||||
typedef struct JSSharpObjectMap JSSharpObjectMap;
|
||||
typedef struct JSEmptyScope JSEmptyScope;
|
||||
@ -175,6 +174,8 @@ class HashSet;
|
||||
|
||||
class DeflatedStringCache;
|
||||
|
||||
struct PropertyCache;
|
||||
struct PropertyCacheEntry;
|
||||
} /* namespace js */
|
||||
|
||||
/* Common instantiations. */
|
||||
|
@ -48,10 +48,13 @@
|
||||
#endif
|
||||
|
||||
#include "jstypes.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jslock.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsprvtd.h"
|
||||
#include "jspubtd.h"
|
||||
#include "jspropertycache.h"
|
||||
#include "jspropertytree.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
|
@ -1136,10 +1136,10 @@ js_DestroyScript(JSContext *cx, JSScript *script)
|
||||
* regenerating shapes, so we don't have to purge fragments if the GC is
|
||||
* currently running.
|
||||
*
|
||||
* JS_THREADSAFE note: js_PurgePropertyCacheForScript purges only the
|
||||
* current thread's property cache, so a script not owned by a function
|
||||
* or object, which hands off lifetime management for that script to the
|
||||
* GC, must be used by only one thread over its lifetime.
|
||||
* JS_THREADSAFE note: The code below purges only the current thread's
|
||||
* property cache, so a script not owned by a function or object, which
|
||||
* hands off lifetime management for that script to the GC, must be used by
|
||||
* only one thread over its lifetime.
|
||||
*
|
||||
* This should be an API-compatible change, since a script is never safe
|
||||
* against premature GC if shared among threads without a rooted object
|
||||
@ -1155,7 +1155,7 @@ js_DestroyScript(JSContext *cx, JSScript *script)
|
||||
JSStackFrame *fp = js_GetTopStackFrame(cx);
|
||||
|
||||
if (!(fp && (fp->flags & JSFRAME_EVAL))) {
|
||||
js_PurgePropertyCacheForScript(cx, script);
|
||||
JS_PROPERTY_CACHE(cx).purgeForScript(script);
|
||||
|
||||
#ifdef CHECK_SCRIPT_OWNER
|
||||
JS_ASSERT(script->owner == cx->thread);
|
||||
|
@ -78,7 +78,7 @@
|
||||
#include "jstypedarray.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
#include "jsinterpinlines.h"
|
||||
#include "jspropertycacheinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsscopeinlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
@ -9113,7 +9113,7 @@ JS_REQUIRES_STACK RecordingStatus
|
||||
TraceRecorder::guardNativePropertyOp(JSObject* aobj, LIns* map_ins)
|
||||
{
|
||||
/*
|
||||
* Interpreter calls to PROPERTY_CACHE_TEST guard on native object ops
|
||||
* Interpreter calls to PropertyCache::test guard on native object ops
|
||||
* which is required to use native objects (those whose maps are scopes),
|
||||
* or even more narrow conditions required because the cache miss case
|
||||
* will call a particular object-op (js_GetProperty, js_SetProperty).
|
||||
@ -9166,8 +9166,8 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
|
||||
CHECK_STATUS_A(guardNativePropertyOp(aobj, map_ins));
|
||||
|
||||
JSAtom* atom;
|
||||
JSPropCacheEntry* entry;
|
||||
PROPERTY_CACHE_TEST(cx, pc, aobj, obj2, entry, atom);
|
||||
PropertyCacheEntry* entry;
|
||||
JS_PROPERTY_CACHE(cx).test(cx, pc, aobj, obj2, entry, atom);
|
||||
if (atom) {
|
||||
// Miss: pre-fill the cache for the interpreter, as well as for our needs.
|
||||
jsid id = ATOM_TO_JSID(atom);
|
||||
@ -9208,8 +9208,8 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
|
||||
obj2->dropProperty(cx, prop);
|
||||
RETURN_STOP_A("property found on non-native object");
|
||||
}
|
||||
entry = js_FillPropertyCache(cx, aobj, 0, protoIndex, obj2,
|
||||
(JSScopeProperty*) prop);
|
||||
entry = JS_PROPERTY_CACHE(cx).fill(cx, aobj, 0, protoIndex, obj2,
|
||||
(JSScopeProperty*) prop);
|
||||
JS_ASSERT(entry);
|
||||
if (entry == JS_NO_PROP_CACHE_FILL)
|
||||
entry = NULL;
|
||||
@ -9249,12 +9249,12 @@ TraceRecorder::guardPropertyCacheHit(LIns* obj_ins,
|
||||
LIns* map_ins,
|
||||
JSObject* aobj,
|
||||
JSObject* obj2,
|
||||
JSPropCacheEntry* entry,
|
||||
PropertyCacheEntry* entry,
|
||||
jsuword& pcval)
|
||||
{
|
||||
VMSideExit* exit = snapshot(BRANCH_EXIT);
|
||||
|
||||
uint32 vshape = PCVCAP_SHAPE(entry->vcap);
|
||||
uint32 vshape = entry->vshape();
|
||||
|
||||
// Special case for the global object, which may be aliased to get a property value.
|
||||
// To catch cross-global property accesses we must check against globalObj identity.
|
||||
@ -9290,14 +9290,14 @@ TraceRecorder::guardPropertyCacheHit(LIns* obj_ins,
|
||||
|
||||
// For any hit that goes up the scope and/or proto chains, we will need to
|
||||
// guard on the shape of the object containing the property.
|
||||
if (PCVCAP_TAG(entry->vcap) >= 1) {
|
||||
if (entry->vcapTag() >= 1) {
|
||||
JS_ASSERT(OBJ_SHAPE(obj2) == vshape);
|
||||
if (obj2 == globalObj)
|
||||
RETURN_STOP("hitting the global object via a prototype chain");
|
||||
|
||||
LIns* obj2_ins;
|
||||
if (PCVCAP_TAG(entry->vcap) == 1) {
|
||||
// Duplicate the special case in PROPERTY_CACHE_TEST.
|
||||
if (entry->vcapTag() == 1) {
|
||||
// Duplicate the special case in PropertyCache::test.
|
||||
obj2_ins = addName(stobj_get_proto(obj_ins), "proto");
|
||||
guard(false, lir->ins_peq0(obj2_ins), exit);
|
||||
} else {
|
||||
@ -11251,12 +11251,12 @@ JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, MethodWriteBarrier, CONTEXT, OBJECT, SCO
|
||||
0, ACC_STORE_ANY)
|
||||
|
||||
JS_REQUIRES_STACK RecordingStatus
|
||||
TraceRecorder::setProp(jsval &l, JSPropCacheEntry* entry, JSScopeProperty* sprop,
|
||||
TraceRecorder::setProp(jsval &l, PropertyCacheEntry* entry, JSScopeProperty* sprop,
|
||||
jsval &v, LIns*& v_ins)
|
||||
{
|
||||
if (entry == JS_NO_PROP_CACHE_FILL)
|
||||
RETURN_STOP("can't trace uncacheable property set");
|
||||
JS_ASSERT_IF(PCVCAP_TAG(entry->vcap) >= 1, !sprop->hasSlot());
|
||||
JS_ASSERT_IF(entry->vcapTag() >= 1, !sprop->hasSlot());
|
||||
if (!sprop->hasDefaultSetter() && sprop->slot != SPROP_INVALID_SLOT)
|
||||
RETURN_STOP("can't trace set of property with setter and slot");
|
||||
if (sprop->hasSetterValue())
|
||||
@ -11273,7 +11273,7 @@ TraceRecorder::setProp(jsval &l, JSPropCacheEntry* entry, JSScopeProperty* sprop
|
||||
LIns* obj_ins = get(&l);
|
||||
JSScope* scope = OBJ_SCOPE(obj);
|
||||
|
||||
JS_ASSERT_IF(entry->vcap == PCVCAP_MAKE(entry->kshape, 0, 0), scope->hasProperty(sprop));
|
||||
JS_ASSERT_IF(entry->directHit(), scope->hasProperty(sprop));
|
||||
|
||||
// Fast path for CallClass. This is about 20% faster than the general case.
|
||||
v_ins = get(&v);
|
||||
@ -11282,9 +11282,9 @@ TraceRecorder::setProp(jsval &l, JSPropCacheEntry* entry, JSScopeProperty* sprop
|
||||
|
||||
// Find obj2. If entry->adding(), the TAG bits are all 0.
|
||||
JSObject* obj2 = obj;
|
||||
for (jsuword i = PCVCAP_TAG(entry->vcap) >> PCVCAP_PROTOBITS; i; i--)
|
||||
for (jsuword i = entry->scopeIndex(); i; i--)
|
||||
obj2 = obj2->getParent();
|
||||
for (jsuword j = PCVCAP_TAG(entry->vcap) & PCVCAP_PROTOMASK; j; j--)
|
||||
for (jsuword j = entry->protoIndex(); j; j--)
|
||||
obj2 = obj2->getProto();
|
||||
scope = OBJ_SCOPE(obj2);
|
||||
JS_ASSERT_IF(entry->adding(), obj2 == obj);
|
||||
@ -11457,7 +11457,7 @@ TraceRecorder::setCallProp(JSObject *callobj, LIns *callobj_ins, JSScopeProperty
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_SetPropHit(JSPropCacheEntry* entry, JSScopeProperty* sprop)
|
||||
TraceRecorder::record_SetPropHit(PropertyCacheEntry* entry, JSScopeProperty* sprop)
|
||||
{
|
||||
jsval& r = stackval(-1);
|
||||
jsval& l = stackval(-2);
|
||||
|
@ -1212,11 +1212,11 @@ class TraceRecorder
|
||||
JS_REQUIRES_STACK RecordingStatus guardNativePropertyOp(JSObject* aobj,
|
||||
nanojit::LIns* map_ins);
|
||||
JS_REQUIRES_STACK RecordingStatus guardPropertyCacheHit(nanojit::LIns* obj_ins,
|
||||
nanojit::LIns* map_ins,
|
||||
JSObject* aobj,
|
||||
JSObject* obj2,
|
||||
JSPropCacheEntry* entry,
|
||||
jsuword& pcval);
|
||||
nanojit::LIns* map_ins,
|
||||
JSObject* aobj,
|
||||
JSObject* obj2,
|
||||
PropertyCacheEntry* entry,
|
||||
jsuword& pcval);
|
||||
|
||||
void stobj_set_fslot(nanojit::LIns *obj_ins, unsigned slot,
|
||||
nanojit::LIns* v_ins);
|
||||
@ -1280,7 +1280,7 @@ class TraceRecorder
|
||||
JS_REQUIRES_STACK RecordingStatus nativeSet(JSObject* obj, nanojit::LIns* obj_ins,
|
||||
JSScopeProperty* sprop,
|
||||
jsval v, nanojit::LIns* v_ins);
|
||||
JS_REQUIRES_STACK RecordingStatus setProp(jsval &l, JSPropCacheEntry* entry,
|
||||
JS_REQUIRES_STACK RecordingStatus setProp(jsval &l, PropertyCacheEntry* entry,
|
||||
JSScopeProperty* sprop,
|
||||
jsval &v, nanojit::LIns*& v_ins);
|
||||
JS_REQUIRES_STACK RecordingStatus setCallProp(JSObject *callobj, nanojit::LIns *callobj_ins,
|
||||
@ -1431,8 +1431,8 @@ public:
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus monitorRecording(JSOp op);
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus record_EnterFrame(uintN& inlineCallCount);
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus record_LeaveFrame();
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus record_SetPropHit(JSPropCacheEntry* entry,
|
||||
JSScopeProperty* sprop);
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus record_SetPropHit(PropertyCacheEntry* entry,
|
||||
JSScopeProperty* sprop);
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus record_DefLocalFunSetSlot(uint32 slot, JSObject* obj);
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus record_NativeCallComplete();
|
||||
void forgetGuardedShapesForObject(JSObject* obj);
|
||||
|
@ -7,7 +7,7 @@ script regress-452498-193.js
|
||||
script regress-452498-196.js
|
||||
script regress-452498-224.js
|
||||
script regress-466905-04.js
|
||||
script regress-466905-05.js
|
||||
skip script regress-466905-05.js # no-op in browser, fails in shell - see bug 554793
|
||||
script regress-477158.js
|
||||
script regress-477187.js
|
||||
script regress-520572.js
|
||||
|
Loading…
Reference in New Issue
Block a user