mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge backout of bug 500431.
This commit is contained in:
commit
a452c93626
@ -147,7 +147,6 @@ CPPSRCS = \
|
|||||||
jsopcode.cpp \
|
jsopcode.cpp \
|
||||||
jsparse.cpp \
|
jsparse.cpp \
|
||||||
jsprf.cpp \
|
jsprf.cpp \
|
||||||
jspropcache.cpp \
|
|
||||||
jsregexp.cpp \
|
jsregexp.cpp \
|
||||||
jsscan.cpp \
|
jsscan.cpp \
|
||||||
jsscope.cpp \
|
jsscope.cpp \
|
||||||
|
@ -91,6 +91,492 @@
|
|||||||
/* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */
|
/* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */
|
||||||
#if !JS_LONE_INTERPRET ^ defined 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, khash;
|
||||||
|
JSOp op;
|
||||||
|
const JSCodeSpec *cs;
|
||||||
|
jsuword vword;
|
||||||
|
ptrdiff_t pcoff;
|
||||||
|
JSAtom *atom;
|
||||||
|
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->has(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->has 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 = OBJ_GET_PARENT(cx, tmp);
|
||||||
|
JS_ASSERT(tmp != pobj);
|
||||||
|
|
||||||
|
protoIndex = 1;
|
||||||
|
for (;;) {
|
||||||
|
tmp = OBJ_GET_PROTO(cx, tmp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 (SPROP_HAS_STUB_GETTER(sprop) &&
|
||||||
|
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
|
||||||
|
scope->brandingShapeChange(cx, sprop->slot, v);
|
||||||
|
if (js_IsPropertyCacheDisabled(cx)) /* check for rt->shapeGen overflow */
|
||||||
|
return JS_NO_PROP_CACHE_FILL;
|
||||||
|
scope->setBranded();
|
||||||
|
}
|
||||||
|
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_HAS_STUB_GETTER(sprop) &&
|
||||||
|
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->lastProp &&
|
||||||
|
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->owned());
|
||||||
|
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 = STOBJ_GET_PROTO(obj);
|
||||||
|
if (!proto || !OBJ_IS_NATIVE(proto))
|
||||||
|
return JS_NO_PROP_CACHE_FILL;
|
||||||
|
JSScope *protoscope = OBJ_SCOPE(proto);
|
||||||
|
if (!protoscope->emptyScope ||
|
||||||
|
!js_ObjectIsSimilarToProto(cx, obj, obj->map->ops, OBJ_GET_CLASS(cx, obj),
|
||||||
|
proto)) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
khash = PROPERTY_CACHE_HASH_PC(pc, kshape);
|
||||||
|
if (obj == pobj) {
|
||||||
|
JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
|
||||||
|
} else {
|
||||||
|
if (op == JSOP_LENGTH) {
|
||||||
|
atom = cx->runtime->atomState.lengthAtom;
|
||||||
|
} else {
|
||||||
|
pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
|
||||||
|
GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (scopeIndex == 0) {
|
||||||
|
JS_ASSERT(protoIndex != 0);
|
||||||
|
JS_ASSERT((protoIndex == 1) == (OBJ_GET_PROTO(cx, obj) == pobj));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (scopeIndex != 0 || protoIndex != 1) {
|
||||||
|
khash = PROPERTY_CACHE_HASH_ATOM(atom, obj);
|
||||||
|
PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1)
|
||||||
|
cache->pcrecycles++);
|
||||||
|
pc = (jsbytecode *) atom;
|
||||||
|
kshape = (jsuword) obj;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = &cache->table[khash];
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_REQUIRES_STACK JSAtom *
|
||||||
|
js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
|
||||||
|
JSObject **objp, JSObject **pobjp,
|
||||||
|
JSPropCacheEntry **entryp)
|
||||||
|
{
|
||||||
|
JSOp op;
|
||||||
|
const JSCodeSpec *cs;
|
||||||
|
ptrdiff_t pcoff;
|
||||||
|
JSAtom *atom;
|
||||||
|
JSObject *obj, *pobj, *tmp;
|
||||||
|
JSPropCacheEntry *entry;
|
||||||
|
uint32 vcap;
|
||||||
|
|
||||||
|
JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code)
|
||||||
|
< cx->fp->script->length);
|
||||||
|
|
||||||
|
op = js_GetOpcode(cx, cx->fp->script, pc);
|
||||||
|
cs = &js_CodeSpec[op];
|
||||||
|
if (op == JSOP_LENGTH) {
|
||||||
|
atom = cx->runtime->atomState.lengthAtom;
|
||||||
|
} else {
|
||||||
|
pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
|
||||||
|
GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = *objp;
|
||||||
|
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
||||||
|
entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_ATOM(atom, obj)];
|
||||||
|
*entryp = entry;
|
||||||
|
vcap = entry->vcap;
|
||||||
|
|
||||||
|
if (entry->kpc != (jsbytecode *) atom) {
|
||||||
|
PCMETER(JS_PROPERTY_CACHE(cx).idmisses++);
|
||||||
|
|
||||||
|
#ifdef DEBUG_notme
|
||||||
|
entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_PC(pc, OBJ_SHAPE(obj))];
|
||||||
|
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 != (jsuword) obj) {
|
||||||
|
PCMETER(JS_PROPERTY_CACHE(cx).komisses++);
|
||||||
|
return atom;
|
||||||
|
}
|
||||||
|
|
||||||
|
pobj = obj;
|
||||||
|
|
||||||
|
if (JOF_MODE(cs->format) == JOF_NAME) {
|
||||||
|
while (vcap & (PCVCAP_SCOPEMASK << PCVCAP_PROTOBITS)) {
|
||||||
|
tmp = OBJ_GET_PARENT(cx, pobj);
|
||||||
|
if (!tmp || !OBJ_IS_NATIVE(tmp))
|
||||||
|
break;
|
||||||
|
pobj = tmp;
|
||||||
|
vcap -= PCVCAP_PROTOSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*objp = pobj;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (vcap & PCVCAP_PROTOMASK) {
|
||||||
|
tmp = OBJ_GET_PROTO(cx, pobj);
|
||||||
|
if (!tmp || !OBJ_IS_NATIVE(tmp))
|
||||||
|
break;
|
||||||
|
pobj = tmp;
|
||||||
|
--vcap;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JS_LOCK_OBJ_IF_SHAPE(cx, pobj, PCVCAP_SHAPE(vcap))) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
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).vcmisses++);
|
||||||
|
return atom;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(cache->table, 0, sizeof 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(pcrecycles);
|
||||||
|
P(tests);
|
||||||
|
P(pchits);
|
||||||
|
P(protopchits);
|
||||||
|
P(initests);
|
||||||
|
P(inipchits);
|
||||||
|
P(inipcmisses);
|
||||||
|
P(settests);
|
||||||
|
P(addpchits);
|
||||||
|
P(setpchits);
|
||||||
|
P(setpcmisses);
|
||||||
|
P(slotchanges);
|
||||||
|
P(setmisses);
|
||||||
|
P(idmisses);
|
||||||
|
P(komisses);
|
||||||
|
P(vcmisses);
|
||||||
|
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;
|
||||||
|
JSPropCacheEntry *entry;
|
||||||
|
|
||||||
|
cache = &JS_PROPERTY_CACHE(cx);
|
||||||
|
for (entry = cache->table; entry < cache->table + PROPERTY_CACHE_SIZE;
|
||||||
|
entry++) {
|
||||||
|
if (JS_UPTRDIFF(entry->kpc, script->code) < script->length) {
|
||||||
|
entry->kpc = NULL;
|
||||||
|
entry->kshape = 0;
|
||||||
|
#ifdef DEBUG
|
||||||
|
entry->vcap = entry->vword = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the current arena has enough space to fit nslots after sp and, if
|
* Check if the current arena has enough space to fit nslots after sp and, if
|
||||||
* so, reserve the necessary space.
|
* so, reserve the necessary space.
|
||||||
|
@ -47,7 +47,6 @@
|
|||||||
#include "jspubtd.h"
|
#include "jspubtd.h"
|
||||||
#include "jsfun.h"
|
#include "jsfun.h"
|
||||||
#include "jsopcode.h"
|
#include "jsopcode.h"
|
||||||
#include "jspropcache.h"
|
|
||||||
#include "jsscript.h"
|
#include "jsscript.h"
|
||||||
|
|
||||||
JS_BEGIN_EXTERN_C
|
JS_BEGIN_EXTERN_C
|
||||||
@ -205,6 +204,223 @@ typedef struct JSInlineFrame {
|
|||||||
|
|
||||||
#define JSFRAME_SPECIAL (JSFRAME_DEBUGGER | JSFRAME_EVAL)
|
#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)
|
||||||
|
|
||||||
|
#define PROPERTY_CACHE_HASH_PC(pc,kshape) \
|
||||||
|
PROPERTY_CACHE_HASH(pc, kshape)
|
||||||
|
|
||||||
|
#define PROPERTY_CACHE_HASH_ATOM(atom,obj) \
|
||||||
|
PROPERTY_CACHE_HASH((jsuword)(atom) >> 2, OBJ_SHAPE(obj))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 if vcap tag is <= 1, else atom */
|
||||||
|
jsuword kshape; /* key shape if pc, else obj for atom */
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 pcrecycles; /* pc-keyed entries recycled by atom-
|
||||||
|
keyed 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 slotchanges; /* clasp->reserveSlots result variance-
|
||||||
|
induced slot changes */
|
||||||
|
uint32 setmisses; /* JSOP_SET{NAME,PROP} total misses */
|
||||||
|
uint32 idmisses; /* slow-path key id == atom misses */
|
||||||
|
uint32 komisses; /* slow-path key object misses */
|
||||||
|
uint32 vcmisses; /* 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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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(pc, kshape_)]; \
|
||||||
|
PCMETER(cache_->pctestentry = entry); \
|
||||||
|
PCMETER(cache_->tests++); \
|
||||||
|
JS_ASSERT(&obj != &pobj); \
|
||||||
|
if (entry->kpc == pc && entry->kshape == kshape_) { \
|
||||||
|
JSObject *tmp_; \
|
||||||
|
pobj = obj; \
|
||||||
|
JS_ASSERT(PCVCAP_TAG(entry->vcap) <= 1); \
|
||||||
|
if (PCVCAP_TAG(entry->vcap) == 1 && \
|
||||||
|
(tmp_ = OBJ_GET_PROTO(cx, pobj)) != NULL) { \
|
||||||
|
pobj = tmp_; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
if (JS_LOCK_OBJ_IF_SHAPE(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 **entryp);
|
||||||
|
|
||||||
|
/* 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.
|
* Interpreter stack arena-pool alloc and free functions.
|
||||||
*/
|
*/
|
||||||
|
@ -1,532 +0,0 @@
|
|||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
||||||
* vim: set ts=8 sw=4 et tw=79:
|
|
||||||
*
|
|
||||||
* ***** 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 of 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 <string.h>
|
|
||||||
#include "jsapi.h"
|
|
||||||
#include "jscntxt.h"
|
|
||||||
#include "jsinterp.h"
|
|
||||||
#include "jsobj.h"
|
|
||||||
#include "jsscope.h"
|
|
||||||
|
|
||||||
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, khash;
|
|
||||||
JSOp op;
|
|
||||||
const JSCodeSpec *cs;
|
|
||||||
jsuword vword;
|
|
||||||
ptrdiff_t pcoff;
|
|
||||||
JSAtom *atom;
|
|
||||||
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->has(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->has 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 = OBJ_GET_PARENT(cx, tmp);
|
|
||||||
JS_ASSERT(tmp != pobj);
|
|
||||||
|
|
||||||
protoIndex = 1;
|
|
||||||
for (;;) {
|
|
||||||
tmp = OBJ_GET_PROTO(cx, tmp);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 (SPROP_HAS_STUB_GETTER(sprop) &&
|
|
||||||
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
|
|
||||||
scope->brandingShapeChange(cx, sprop->slot, v);
|
|
||||||
if (js_IsPropertyCacheDisabled(cx)) /* check for rt->shapeGen overflow */
|
|
||||||
return JS_NO_PROP_CACHE_FILL;
|
|
||||||
scope->setBranded();
|
|
||||||
}
|
|
||||||
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_HAS_STUB_GETTER(sprop) &&
|
|
||||||
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->lastProp &&
|
|
||||||
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->owned());
|
|
||||||
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 = STOBJ_GET_PROTO(obj);
|
|
||||||
if (!proto || !OBJ_IS_NATIVE(proto))
|
|
||||||
return JS_NO_PROP_CACHE_FILL;
|
|
||||||
JSScope *protoscope = OBJ_SCOPE(proto);
|
|
||||||
if (!protoscope->emptyScope ||
|
|
||||||
!js_ObjectIsSimilarToProto(cx, obj, obj->map->ops, OBJ_GET_CLASS(cx, obj),
|
|
||||||
proto)) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
khash = PROPERTY_CACHE_HASH_PC(pc, kshape);
|
|
||||||
if (obj == pobj) {
|
|
||||||
JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
|
|
||||||
} else {
|
|
||||||
if (op == JSOP_LENGTH) {
|
|
||||||
atom = cx->runtime->atomState.lengthAtom;
|
|
||||||
} else {
|
|
||||||
pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
|
|
||||||
GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
if (scopeIndex == 0) {
|
|
||||||
JS_ASSERT(protoIndex != 0);
|
|
||||||
JS_ASSERT((protoIndex == 1) == (OBJ_GET_PROTO(cx, obj) == pobj));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (scopeIndex != 0 || protoIndex != 1) {
|
|
||||||
khash = PROPERTY_CACHE_HASH_ATOM(atom, obj);
|
|
||||||
PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1)
|
|
||||||
cache->pcrecycles++);
|
|
||||||
pc = (jsbytecode *) atom;
|
|
||||||
kshape = (jsuword) obj;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
entry = &cache->table[khash];
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
JS_REQUIRES_STACK JSAtom *
|
|
||||||
js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
|
|
||||||
JSObject **objp, JSObject **pobjp,
|
|
||||||
JSPropCacheEntry **entryp)
|
|
||||||
{
|
|
||||||
JSOp op;
|
|
||||||
const JSCodeSpec *cs;
|
|
||||||
ptrdiff_t pcoff;
|
|
||||||
JSAtom *atom;
|
|
||||||
JSObject *obj, *pobj, *tmp;
|
|
||||||
JSPropCacheEntry *entry;
|
|
||||||
uint32 vcap;
|
|
||||||
|
|
||||||
JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code)
|
|
||||||
< cx->fp->script->length);
|
|
||||||
|
|
||||||
op = js_GetOpcode(cx, cx->fp->script, pc);
|
|
||||||
cs = &js_CodeSpec[op];
|
|
||||||
if (op == JSOP_LENGTH) {
|
|
||||||
atom = cx->runtime->atomState.lengthAtom;
|
|
||||||
} else {
|
|
||||||
pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
|
|
||||||
GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
|
|
||||||
}
|
|
||||||
|
|
||||||
obj = *objp;
|
|
||||||
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
|
||||||
entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_ATOM(atom, obj)];
|
|
||||||
*entryp = entry;
|
|
||||||
vcap = entry->vcap;
|
|
||||||
|
|
||||||
if (entry->kpc != (jsbytecode *) atom) {
|
|
||||||
PCMETER(JS_PROPERTY_CACHE(cx).idmisses++);
|
|
||||||
|
|
||||||
#ifdef DEBUG_notme
|
|
||||||
entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_PC(pc, OBJ_SHAPE(obj))];
|
|
||||||
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 != (jsuword) obj) {
|
|
||||||
PCMETER(JS_PROPERTY_CACHE(cx).komisses++);
|
|
||||||
return atom;
|
|
||||||
}
|
|
||||||
|
|
||||||
pobj = obj;
|
|
||||||
|
|
||||||
if (JOF_MODE(cs->format) == JOF_NAME) {
|
|
||||||
while (vcap & (PCVCAP_SCOPEMASK << PCVCAP_PROTOBITS)) {
|
|
||||||
tmp = OBJ_GET_PARENT(cx, pobj);
|
|
||||||
if (!tmp || !OBJ_IS_NATIVE(tmp))
|
|
||||||
break;
|
|
||||||
pobj = tmp;
|
|
||||||
vcap -= PCVCAP_PROTOSIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
*objp = pobj;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (vcap & PCVCAP_PROTOMASK) {
|
|
||||||
tmp = OBJ_GET_PROTO(cx, pobj);
|
|
||||||
if (!tmp || !OBJ_IS_NATIVE(tmp))
|
|
||||||
break;
|
|
||||||
pobj = tmp;
|
|
||||||
--vcap;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JS_LOCK_OBJ_IF_SHAPE(cx, pobj, PCVCAP_SHAPE(vcap))) {
|
|
||||||
#ifdef DEBUG
|
|
||||||
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).vcmisses++);
|
|
||||||
return atom;
|
|
||||||
}
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(cache->table, 0, sizeof 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(pcrecycles);
|
|
||||||
P(tests);
|
|
||||||
P(pchits);
|
|
||||||
P(protopchits);
|
|
||||||
P(initests);
|
|
||||||
P(inipchits);
|
|
||||||
P(inipcmisses);
|
|
||||||
P(settests);
|
|
||||||
P(addpchits);
|
|
||||||
P(setpchits);
|
|
||||||
P(setpcmisses);
|
|
||||||
P(slotchanges);
|
|
||||||
P(setmisses);
|
|
||||||
P(idmisses);
|
|
||||||
P(komisses);
|
|
||||||
P(vcmisses);
|
|
||||||
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;
|
|
||||||
JSPropCacheEntry *entry;
|
|
||||||
|
|
||||||
cache = &JS_PROPERTY_CACHE(cx);
|
|
||||||
for (entry = cache->table; entry < cache->table + PROPERTY_CACHE_SIZE;
|
|
||||||
entry++) {
|
|
||||||
if (JS_UPTRDIFF(entry->kpc, script->code) < script->length) {
|
|
||||||
entry->kpc = NULL;
|
|
||||||
entry->kshape = 0;
|
|
||||||
#ifdef DEBUG
|
|
||||||
entry->vcap = entry->vword = 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,261 +0,0 @@
|
|||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
||||||
* vim: set ts=8 sw=4 et tw=79:
|
|
||||||
*
|
|
||||||
* ***** 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 of 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 jspropcache_h___
|
|
||||||
#define jspropcache_h___
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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)
|
|
||||||
|
|
||||||
#define PROPERTY_CACHE_HASH_PC(pc,kshape) \
|
|
||||||
PROPERTY_CACHE_HASH(pc, kshape)
|
|
||||||
|
|
||||||
#define PROPERTY_CACHE_HASH_ATOM(atom,obj) \
|
|
||||||
PROPERTY_CACHE_HASH((jsuword)(atom) >> 2, OBJ_SHAPE(obj))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 if vcap tag is <= 1, else atom */
|
|
||||||
jsuword kshape; /* key shape if pc, else obj for atom */
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 pcrecycles; /* pc-keyed entries recycled by atom-
|
|
||||||
keyed 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 slotchanges; /* clasp->reserveSlots result variance-
|
|
||||||
induced slot changes */
|
|
||||||
uint32 setmisses; /* JSOP_SET{NAME,PROP} total misses */
|
|
||||||
uint32 idmisses; /* slow-path key id == atom misses */
|
|
||||||
uint32 komisses; /* slow-path key object misses */
|
|
||||||
uint32 vcmisses; /* 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);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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(pc, kshape_)]; \
|
|
||||||
PCMETER(cache_->pctestentry = entry); \
|
|
||||||
PCMETER(cache_->tests++); \
|
|
||||||
JS_ASSERT(&obj != &pobj); \
|
|
||||||
if (entry->kpc == pc && entry->kshape == kshape_) { \
|
|
||||||
JSObject *tmp_; \
|
|
||||||
pobj = obj; \
|
|
||||||
JS_ASSERT(PCVCAP_TAG(entry->vcap) <= 1); \
|
|
||||||
if (PCVCAP_TAG(entry->vcap) == 1 && \
|
|
||||||
(tmp_ = OBJ_GET_PROTO(cx, pobj)) != NULL) { \
|
|
||||||
pobj = tmp_; \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
if (JS_LOCK_OBJ_IF_SHAPE(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 **entryp);
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
#endif /* jspropcache_h___ */
|
|
Loading…
Reference in New Issue
Block a user