mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 500431 - Encapsulate the property cache using C++ best practices - Part 2, refactoring. r=Waldo.
This commit is contained in:
parent
666b4c3e13
commit
b040e5c724
@ -201,6 +201,8 @@ INSTALLED_HEADERS = \
|
|||||||
jsotypes.h \
|
jsotypes.h \
|
||||||
jsparse.h \
|
jsparse.h \
|
||||||
jsprf.h \
|
jsprf.h \
|
||||||
|
jspropcache.h \
|
||||||
|
jspropcacheinlines.h \
|
||||||
jsproto.tbl \
|
jsproto.tbl \
|
||||||
jsprvtd.h \
|
jsprvtd.h \
|
||||||
jspubtd.h \
|
jspubtd.h \
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
#include "jsvector.h"
|
#include "jsvector.h"
|
||||||
|
|
||||||
#include "jsatominlines.h"
|
#include "jsatominlines.h"
|
||||||
|
#include "jspropcacheinlines.h"
|
||||||
|
|
||||||
using namespace avmplus;
|
using namespace avmplus;
|
||||||
using namespace nanojit;
|
using namespace nanojit;
|
||||||
|
@ -98,7 +98,6 @@ FinishThreadData(JSThreadData *data)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
js_FinishGSNCache(&data->gsnCache);
|
js_FinishGSNCache(&data->gsnCache);
|
||||||
js_FinishPropertyCache(&data->propertyCache);
|
|
||||||
#if defined JS_TRACER
|
#if defined JS_TRACER
|
||||||
js_FinishJIT(&data->traceMonitor);
|
js_FinishJIT(&data->traceMonitor);
|
||||||
#endif
|
#endif
|
||||||
@ -110,7 +109,7 @@ PurgeThreadData(JSContext *cx, JSThreadData *data)
|
|||||||
js_PurgeGSNCache(&data->gsnCache);
|
js_PurgeGSNCache(&data->gsnCache);
|
||||||
|
|
||||||
/* FIXME: bug 506341. */
|
/* FIXME: bug 506341. */
|
||||||
js_PurgePropertyCache(cx, &data->propertyCache);
|
data->propertyCache.purge(cx);
|
||||||
|
|
||||||
# ifdef JS_TRACER
|
# ifdef JS_TRACER
|
||||||
JSTraceMonitor *tm = &data->traceMonitor;
|
JSTraceMonitor *tm = &data->traceMonitor;
|
||||||
|
@ -1702,12 +1702,6 @@ js_GetTopStackFrame(JSContext *cx)
|
|||||||
return cx->fp;
|
return cx->fp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JS_INLINE JSBool
|
|
||||||
js_IsPropertyCacheDisabled(JSContext *cx)
|
|
||||||
{
|
|
||||||
return cx->runtime->shapeGen >= SHAPE_OVERFLOW_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static JS_INLINE uint32
|
static JS_INLINE uint32
|
||||||
js_RegenerateShapeForGC(JSContext *cx)
|
js_RegenerateShapeForGC(JSContext *cx)
|
||||||
{
|
{
|
||||||
|
@ -84,6 +84,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "jsatominlines.h"
|
#include "jsatominlines.h"
|
||||||
|
#include "jspropcacheinlines.h"
|
||||||
#include "jsscriptinlines.h"
|
#include "jsscriptinlines.h"
|
||||||
|
|
||||||
#include "jsautooplen.h"
|
#include "jsautooplen.h"
|
||||||
|
@ -3838,7 +3838,7 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
|||||||
if (defineHow & JSDNP_CACHE_RESULT) {
|
if (defineHow & JSDNP_CACHE_RESULT) {
|
||||||
JS_ASSERT_NOT_ON_TRACE(cx);
|
JS_ASSERT_NOT_ON_TRACE(cx);
|
||||||
JSPropCacheEntry *entry;
|
JSPropCacheEntry *entry;
|
||||||
entry = js_FillPropertyCache(cx, obj, 0, 0, obj, sprop, added);
|
entry = JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, sprop, added);
|
||||||
TRACE_2(SetPropHit, entry, sprop);
|
TRACE_2(SetPropHit, entry, sprop);
|
||||||
}
|
}
|
||||||
if (propp)
|
if (propp)
|
||||||
@ -4090,8 +4090,7 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (cacheResult) {
|
if (cacheResult) {
|
||||||
entry = js_FillPropertyCache(cx, scopeChain,
|
entry = JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, protoIndex, pobj,
|
||||||
scopeIndex, protoIndex, pobj,
|
|
||||||
(JSScopeProperty *) prop, false);
|
(JSScopeProperty *) prop, false);
|
||||||
}
|
}
|
||||||
SCOPE_DEPTH_ACCUM(&rt->scopeSearchDepthStats, scopeIndex);
|
SCOPE_DEPTH_ACCUM(&rt->scopeSearchDepthStats, scopeIndex);
|
||||||
@ -4173,8 +4172,7 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id)
|
|||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
JSPropCacheEntry *entry =
|
JSPropCacheEntry *entry =
|
||||||
#endif
|
#endif
|
||||||
js_FillPropertyCache(cx, scopeChain,
|
JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, protoIndex, pobj,
|
||||||
scopeIndex, protoIndex, pobj,
|
|
||||||
(JSScopeProperty *) prop, false);
|
(JSScopeProperty *) prop, false);
|
||||||
JS_ASSERT(entry);
|
JS_ASSERT(entry);
|
||||||
JS_UNLOCK_OBJ(cx, pobj);
|
JS_UNLOCK_OBJ(cx, pobj);
|
||||||
@ -4421,7 +4419,7 @@ js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN getHow,
|
|||||||
|
|
||||||
if (getHow & JSGET_CACHE_RESULT) {
|
if (getHow & JSGET_CACHE_RESULT) {
|
||||||
JS_ASSERT_NOT_ON_TRACE(cx);
|
JS_ASSERT_NOT_ON_TRACE(cx);
|
||||||
js_FillPropertyCache(cx, aobj, 0, protoIndex, obj2, sprop, false);
|
JS_PROPERTY_CACHE(cx).fill(cx, aobj, 0, protoIndex, obj2, sprop, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!js_NativeGet(cx, obj, obj2, sprop, getHow, vp))
|
if (!js_NativeGet(cx, obj, obj2, sprop, getHow, vp))
|
||||||
@ -4599,7 +4597,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
|
|||||||
if (attrs & JSPROP_SHARED) {
|
if (attrs & JSPROP_SHARED) {
|
||||||
if (defineHow & JSDNP_CACHE_RESULT) {
|
if (defineHow & JSDNP_CACHE_RESULT) {
|
||||||
JSPropCacheEntry *entry;
|
JSPropCacheEntry *entry;
|
||||||
entry = js_FillPropertyCache(cx, obj, 0, protoIndex, pobj, sprop, false);
|
entry = JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, protoIndex, pobj, sprop, false);
|
||||||
TRACE_2(SetPropHit, entry, sprop);
|
TRACE_2(SetPropHit, entry, sprop);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4701,7 +4699,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
|
|||||||
|
|
||||||
if (defineHow & JSDNP_CACHE_RESULT) {
|
if (defineHow & JSDNP_CACHE_RESULT) {
|
||||||
JSPropCacheEntry *entry;
|
JSPropCacheEntry *entry;
|
||||||
entry = js_FillPropertyCache(cx, obj, 0, 0, obj, sprop, added);
|
entry = JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, sprop, added);
|
||||||
TRACE_2(SetPropHit, entry, sprop);
|
TRACE_2(SetPropHit, entry, sprop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
106
js/src/jsops.cpp
106
js/src/jsops.cpp
@ -1682,11 +1682,8 @@
|
|||||||
entry = NULL;
|
entry = NULL;
|
||||||
atom = NULL;
|
atom = NULL;
|
||||||
if (JS_LIKELY(obj->map->ops->setProperty == js_SetProperty)) {
|
if (JS_LIKELY(obj->map->ops->setProperty == js_SetProperty)) {
|
||||||
JSPropertyCache *cache = &JS_PROPERTY_CACHE(cx);
|
|
||||||
uint32 kshape = OBJ_SHAPE(obj);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open-code PROPERTY_CACHE_TEST, specializing for two
|
* Open-code JSPropertyCache::test, specializing for two
|
||||||
* important set-property cases. First:
|
* important set-property cases. First:
|
||||||
*
|
*
|
||||||
* function f(a, b, c) {
|
* function f(a, b, c) {
|
||||||
@ -1704,13 +1701,8 @@
|
|||||||
* will (possibly after the first iteration) always exist
|
* will (possibly after the first iteration) always exist
|
||||||
* in native object o.
|
* in native object o.
|
||||||
*/
|
*/
|
||||||
entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)];
|
if (JS_PROPERTY_CACHE(cx).testForSet(cx, regs.pc, &obj,
|
||||||
PCMETER(cache->pctestentry = entry);
|
&obj2, &entry, &atom)) {
|
||||||
PCMETER(cache->tests++);
|
|
||||||
PCMETER(cache->settests++);
|
|
||||||
if (entry->kpc == regs.pc && entry->kshape == kshape) {
|
|
||||||
JS_ASSERT(PCVCAP_TAG(entry->vcap) <= 1);
|
|
||||||
if (JS_LOCK_OBJ_IF_SHAPE(cx, obj, kshape)) {
|
|
||||||
JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
|
JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
|
||||||
sprop = PCVAL_TO_SPROP(entry->vword);
|
sprop = PCVAL_TO_SPROP(entry->vword);
|
||||||
JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
|
JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
|
||||||
@ -1722,8 +1714,8 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Fastest path: check whether the cached sprop is
|
* Fastest path: check whether the cached sprop is
|
||||||
* already in scope and call NATIVE_SET and break
|
* already in scope and call NATIVE_SET and break to
|
||||||
* to get out of the do-while(0). But we can call
|
* get out of the do-while(0). But we can call
|
||||||
* NATIVE_SET only if obj owns scope or sprop is
|
* NATIVE_SET only if obj owns scope or sprop is
|
||||||
* shared.
|
* shared.
|
||||||
*/
|
*/
|
||||||
@ -1764,15 +1756,15 @@
|
|||||||
SPROP_HAS_STUB_SETTER(sprop) &&
|
SPROP_HAS_STUB_SETTER(sprop) &&
|
||||||
(slot = sprop->slot) == scope->freeslot) {
|
(slot = sprop->slot) == scope->freeslot) {
|
||||||
/*
|
/*
|
||||||
* Fast path: adding a plain old property that
|
* Fast path: adding a plain old property that was
|
||||||
* was once at the frontier of the property
|
* once at the frontier of the property tree, whose
|
||||||
* tree, whose slot is next to claim among the
|
* slot is next to claim among the allocated slots
|
||||||
* allocated slots in obj, where scope->table
|
* in obj, where scope->table has not been created
|
||||||
* has not been created yet.
|
* yet.
|
||||||
*
|
*
|
||||||
* We may want to remove hazard conditions
|
* We may want to remove hazard conditions above
|
||||||
* above and inline compensation code here,
|
* and inline compensation code here, depending on
|
||||||
* depending on real-world workloads.
|
* real-world workloads.
|
||||||
*/
|
*/
|
||||||
JS_ASSERT(!(obj->getClass()->flags &
|
JS_ASSERT(!(obj->getClass()->flags &
|
||||||
JSCLASS_SHARE_ALL_PROPERTIES));
|
JSCLASS_SHARE_ALL_PROPERTIES));
|
||||||
@ -1781,9 +1773,9 @@
|
|||||||
PCMETER(cache->addpchits++);
|
PCMETER(cache->addpchits++);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Beware classes such as Function that use
|
* Beware classes such as Function that use the
|
||||||
* the reserveSlots hook to allocate a number
|
* reserveSlots hook to allocate a number of
|
||||||
* of reserved slots that may vary with obj.
|
* reserved slots that may vary with obj.
|
||||||
*/
|
*/
|
||||||
if (slot < STOBJ_NSLOTS(obj) &&
|
if (slot < STOBJ_NSLOTS(obj) &&
|
||||||
!OBJ_GET_CLASS(cx, obj)->reserveSlots) {
|
!OBJ_GET_CLASS(cx, obj)->reserveSlots) {
|
||||||
@ -1796,15 +1788,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this obj's number of reserved slots
|
* If this obj's number of reserved slots differed,
|
||||||
* differed, or if something created a hash
|
* or if something created a hash table for scope,
|
||||||
* table for scope, we must pay the price of
|
* we must pay the price of JSScope::add.
|
||||||
* JSScope::add.
|
|
||||||
*
|
*
|
||||||
* If slot does not match the cached sprop's
|
* If slot does not match the cached sprop's slot,
|
||||||
* slot, update the cache entry in the hope
|
* update the cache entry in the hope that obj and
|
||||||
* that obj and other instances with the same
|
* other instances with the same number of reserved
|
||||||
* number of reserved slots are now "hot".
|
* slots are now "hot".
|
||||||
*/
|
*/
|
||||||
if (slot != sprop->slot || scope->table) {
|
if (slot != sprop->slot || scope->table) {
|
||||||
JSScopeProperty *sprop2 =
|
JSScopeProperty *sprop2 =
|
||||||
@ -1830,20 +1821,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* No method change check here because here we
|
* No method change check here because here we are
|
||||||
* are adding a new property, not updating an
|
* adding a new property, not updating an existing
|
||||||
* existing slot's value that might contain a
|
* slot's value that might contain a method of a
|
||||||
* method of a branded scope.
|
* branded scope.
|
||||||
*/
|
*/
|
||||||
TRACE_2(SetPropHit, entry, sprop);
|
TRACE_2(SetPropHit, entry, sprop);
|
||||||
LOCKED_OBJ_SET_SLOT(obj, slot, rval);
|
LOCKED_OBJ_SET_SLOT(obj, slot, rval);
|
||||||
JS_UNLOCK_SCOPE(cx, scope);
|
JS_UNLOCK_SCOPE(cx, scope);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Purge the property cache of the id we may
|
* Purge the property cache of the id we may have
|
||||||
* have just shadowed in obj's scope and proto
|
* just shadowed in obj's scope and proto
|
||||||
* chains. We do this after unlocking obj's
|
* chains. We do this after unlocking obj's scope
|
||||||
* scope to avoid lock nesting.
|
* to avoid lock nesting.
|
||||||
*/
|
*/
|
||||||
js_PurgeScopeChain(cx, obj, sprop->id);
|
js_PurgeScopeChain(cx, obj, sprop->id);
|
||||||
break;
|
break;
|
||||||
@ -1851,14 +1842,8 @@
|
|||||||
JS_UNLOCK_SCOPE(cx, scope);
|
JS_UNLOCK_SCOPE(cx, scope);
|
||||||
PCMETER(cache->setpcmisses++);
|
PCMETER(cache->setpcmisses++);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
atom = js_FullTestPropertyCache(cx, regs.pc, &obj, &obj2,
|
if (!atom) {
|
||||||
&entry);
|
|
||||||
if (atom) {
|
|
||||||
PCMETER(cache->misses++);
|
|
||||||
PCMETER(cache->setmisses++);
|
|
||||||
} else {
|
|
||||||
ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
|
ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
|
||||||
sprop = NULL;
|
sprop = NULL;
|
||||||
if (obj == obj2) {
|
if (obj == obj2) {
|
||||||
@ -3463,37 +3448,16 @@
|
|||||||
JS_ASSERT(!(obj->getClass()->flags & JSCLASS_SHARE_ALL_PROPERTIES));
|
JS_ASSERT(!(obj->getClass()->flags & JSCLASS_SHARE_ALL_PROPERTIES));
|
||||||
|
|
||||||
do {
|
do {
|
||||||
JSScope *scope;
|
|
||||||
uint32 kshape;
|
|
||||||
JSPropertyCache *cache;
|
|
||||||
JSPropCacheEntry *entry;
|
|
||||||
|
|
||||||
JS_LOCK_OBJ(cx, obj);
|
JS_LOCK_OBJ(cx, obj);
|
||||||
scope = OBJ_SCOPE(obj);
|
JSScope *scope = OBJ_SCOPE(obj);
|
||||||
// FIXME: bug 513291 -- uncomment this assertion and remove the
|
// FIXME: bug 513291 -- uncomment this assertion and remove the
|
||||||
// (!scope->owned()) => js_GetMutableScope code further
|
// (!scope->owned()) => js_GetMutableScope code further
|
||||||
// below.
|
// below.
|
||||||
// JS_ASSERT(scope->object == obj);
|
// JS_ASSERT(scope->object == obj);
|
||||||
JS_ASSERT(!scope->sealed());
|
JS_ASSERT(!scope->sealed());
|
||||||
kshape = scope->shape;
|
|
||||||
cache = &JS_PROPERTY_CACHE(cx);
|
|
||||||
entry = &cache->table[PROPERTY_CACHE_HASH_PC(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);
|
|
||||||
|
|
||||||
PCMETER(cache->pchits++);
|
|
||||||
PCMETER(cache->inipchits++);
|
|
||||||
|
|
||||||
JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
|
|
||||||
sprop = PCVAL_TO_SPROP(entry->vword);
|
|
||||||
JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
|
|
||||||
|
|
||||||
|
JSPropCacheEntry *entry;
|
||||||
|
if (JS_PROPERTY_CACHE(cx).testForInit(cx, regs.pc, obj, &entry, &sprop)) {
|
||||||
/*
|
/*
|
||||||
* If this property has a non-stub setter, it must be
|
* If this property has a non-stub setter, it must be
|
||||||
* __proto__, __parent__, or another "shared prototype"
|
* __proto__, __parent__, or another "shared prototype"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
* vim: set ts=8 sw=4 et tw=79:
|
* vim: set ts=8 sw=4 et tw=99:
|
||||||
*
|
*
|
||||||
* ***** BEGIN LICENSE BLOCK *****
|
* ***** BEGIN LICENSE BLOCK *****
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
@ -42,31 +42,28 @@
|
|||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
#include "jscntxt.h"
|
#include "jscntxt.h"
|
||||||
#include "jsinterp.h"
|
#include "jsinterp.h"
|
||||||
#include "jsobj.h"
|
|
||||||
#include "jsscope.h"
|
#include "jsscope.h"
|
||||||
|
#include "jspropcacheinlines.h"
|
||||||
|
|
||||||
JS_REQUIRES_STACK JSPropCacheEntry *
|
JS_REQUIRES_STACK JSPropCacheEntry *
|
||||||
js_FillPropertyCache(JSContext *cx, JSObject *obj,
|
JSPropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN protoIndex,
|
||||||
uintN scopeIndex, uintN protoIndex, JSObject *pobj,
|
JSObject *pobj, JSScopeProperty *sprop, JSBool adding)
|
||||||
JSScopeProperty *sprop, JSBool adding)
|
|
||||||
{
|
{
|
||||||
JSPropertyCache *cache;
|
|
||||||
jsbytecode *pc;
|
jsbytecode *pc;
|
||||||
JSScope *scope;
|
JSScope *scope;
|
||||||
jsuword kshape, vshape, khash;
|
jsuword kshape, vshape;
|
||||||
JSOp op;
|
JSOp op;
|
||||||
const JSCodeSpec *cs;
|
const JSCodeSpec *cs;
|
||||||
jsuword vword;
|
jsuword vword;
|
||||||
ptrdiff_t pcoff;
|
ptrdiff_t pcoff;
|
||||||
JSAtom *atom;
|
JSAtom *atom;
|
||||||
JSPropCacheEntry *entry;
|
|
||||||
|
|
||||||
JS_ASSERT(!cx->runtime->gcRunning);
|
JS_ASSERT(!cx->runtime->gcRunning);
|
||||||
cache = &JS_PROPERTY_CACHE(cx);
|
JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
|
||||||
|
|
||||||
/* FIXME bug 489098: consider enabling the property cache for eval. */
|
/* FIXME bug 489098: consider enabling the property cache for eval. */
|
||||||
if (js_IsPropertyCacheDisabled(cx) || (cx->fp->flags & JSFRAME_EVAL)) {
|
if (js_IsPropertyCacheDisabled(cx) || (cx->fp->flags & JSFRAME_EVAL)) {
|
||||||
PCMETER(cache->disfills++);
|
PCMETER(disfills++);
|
||||||
return JS_NO_PROP_CACHE_FILL;
|
return JS_NO_PROP_CACHE_FILL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +73,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
|
|||||||
*/
|
*/
|
||||||
scope = OBJ_SCOPE(pobj);
|
scope = OBJ_SCOPE(pobj);
|
||||||
if (!scope->has(sprop)) {
|
if (!scope->has(sprop)) {
|
||||||
PCMETER(cache->oddfills++);
|
PCMETER(oddfills++);
|
||||||
return JS_NO_PROP_CACHE_FILL;
|
return JS_NO_PROP_CACHE_FILL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +109,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
|
|||||||
* mutate in arbitrary way without changing any shapes.
|
* mutate in arbitrary way without changing any shapes.
|
||||||
*/
|
*/
|
||||||
if (!tmp || !OBJ_IS_NATIVE(tmp)) {
|
if (!tmp || !OBJ_IS_NATIVE(tmp)) {
|
||||||
PCMETER(cache->noprotos++);
|
PCMETER(noprotos++);
|
||||||
return JS_NO_PROP_CACHE_FILL;
|
return JS_NO_PROP_CACHE_FILL;
|
||||||
}
|
}
|
||||||
if (tmp == pobj)
|
if (tmp == pobj)
|
||||||
@ -122,7 +119,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) {
|
if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) {
|
||||||
PCMETER(cache->longchains++);
|
PCMETER(longchains++);
|
||||||
return JS_NO_PROP_CACHE_FILL;
|
return JS_NO_PROP_CACHE_FILL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +171,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
|
|||||||
* object will result in shape being regenerated.
|
* object will result in shape being regenerated.
|
||||||
*/
|
*/
|
||||||
if (!scope->branded()) {
|
if (!scope->branded()) {
|
||||||
PCMETER(cache->brandfills++);
|
PCMETER(brandfills++);
|
||||||
#ifdef DEBUG_notme
|
#ifdef DEBUG_notme
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"branding %p (%s) for funobj %p (%s), shape %lu\n",
|
"branding %p (%s) for funobj %p (%s), shape %lu\n",
|
||||||
@ -269,7 +266,6 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
|
|||||||
vshape = scope->shape;
|
vshape = scope->shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
khash = PROPERTY_CACHE_HASH_PC(pc, kshape);
|
|
||||||
if (obj == pobj) {
|
if (obj == pobj) {
|
||||||
JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
|
JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
|
||||||
} else {
|
} else {
|
||||||
@ -288,12 +284,6 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (scopeIndex != 0 || protoIndex != 1) {
|
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
|
* Make sure that a later shadowing assignment will enter
|
||||||
* PurgeProtoChain and invalidate this entry, bug 479198.
|
* PurgeProtoChain and invalidate this entry, bug 479198.
|
||||||
@ -305,31 +295,16 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
|
|||||||
* because the property cache and JIT cache are thread-local.
|
* because the property cache and JIT cache are thread-local.
|
||||||
*/
|
*/
|
||||||
obj->setDelegate();
|
obj->setDelegate();
|
||||||
|
|
||||||
|
return fillByAtom(atom, obj, vshape, scopeIndex, protoIndex, vword);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entry = &cache->table[khash];
|
return fillByPC(pc, kshape, vshape, scopeIndex, protoIndex, vword);
|
||||||
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_REQUIRES_STACK JSAtom *
|
||||||
js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
|
JSPropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject **pobjp,
|
||||||
JSObject **objp, JSObject **pobjp,
|
|
||||||
JSPropCacheEntry **entryp)
|
JSPropCacheEntry **entryp)
|
||||||
{
|
{
|
||||||
JSOp op;
|
JSOp op;
|
||||||
@ -354,15 +329,15 @@ js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
|
|||||||
|
|
||||||
obj = *objp;
|
obj = *objp;
|
||||||
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
||||||
entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_ATOM(atom, obj)];
|
entry = &table[PROPERTY_CACHE_HASH_ATOM(atom, obj)];
|
||||||
*entryp = entry;
|
*entryp = entry;
|
||||||
vcap = entry->vcap;
|
vcap = entry->vcap;
|
||||||
|
|
||||||
if (entry->kpc != (jsbytecode *) atom) {
|
if (entry->kpc != (jsbytecode *) atom) {
|
||||||
PCMETER(JS_PROPERTY_CACHE(cx).idmisses++);
|
PCMETER(idmisses++);
|
||||||
|
|
||||||
#ifdef DEBUG_notme
|
#ifdef DEBUG_notme
|
||||||
entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_PC(pc, OBJ_SHAPE(obj))];
|
entry = &table[PROPERTY_CACHE_HASH_PC(pc, OBJ_SHAPE(obj))];
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"id miss for %s from %s:%u"
|
"id miss for %s from %s:%u"
|
||||||
" (pc %u, kpc %u, kshape %u, shape %u)\n",
|
" (pc %u, kpc %u, kshape %u, shape %u)\n",
|
||||||
@ -382,7 +357,7 @@ js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (entry->kshape != (jsuword) obj) {
|
if (entry->kshape != (jsuword) obj) {
|
||||||
PCMETER(JS_PROPERTY_CACHE(cx).komisses++);
|
PCMETER(komisses++);
|
||||||
return atom;
|
return atom;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,42 +395,43 @@ js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
PCMETER(JS_PROPERTY_CACHE(cx).vcmisses++);
|
PCMETER(vcmisses++);
|
||||||
return atom;
|
return atom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
JSPropertyCache::assertEmpty()
|
||||||
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#define ASSERT_CACHE_IS_EMPTY(cache) \
|
JS_ASSERT(empty);
|
||||||
JS_BEGIN_MACRO \
|
for (uintN i = 0; i < PROPERTY_CACHE_SIZE; i++) {
|
||||||
JSPropertyCache *cache_ = (cache); \
|
JS_ASSERT(!table[i].kpc);
|
||||||
uintN i_; \
|
JS_ASSERT(!table[i].kshape);
|
||||||
JS_ASSERT(cache_->empty); \
|
JS_ASSERT(!table[i].vcap);
|
||||||
for (i_ = 0; i_ < PROPERTY_CACHE_SIZE; i_++) { \
|
JS_ASSERT(!table[i].vword);
|
||||||
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
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
JS_STATIC_ASSERT(PCVAL_NULL == 0);
|
JS_STATIC_ASSERT(PCVAL_NULL == 0);
|
||||||
|
|
||||||
void
|
void
|
||||||
js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache)
|
JSPropertyCache::purge(JSContext *cx)
|
||||||
{
|
{
|
||||||
if (cache->empty) {
|
JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
|
||||||
ASSERT_CACHE_IS_EMPTY(cache);
|
|
||||||
|
if (empty) {
|
||||||
|
assertEmpty();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(cache->table, 0, sizeof cache->table);
|
memset(table, 0, sizeof table);
|
||||||
cache->empty = JS_TRUE;
|
empty = JS_TRUE;
|
||||||
|
|
||||||
#ifdef JS_PROPERTY_CACHE_METERING
|
#ifdef JS_PROPERTY_CACHE_METERING
|
||||||
{ static FILE *fp;
|
{
|
||||||
|
static FILE *fp;
|
||||||
if (!fp)
|
if (!fp)
|
||||||
fp = fopen("/tmp/propcache.stats", "w");
|
fp = fopen("/tmp/propcache.stats", "w");
|
||||||
if (fp) {
|
if (fp) {
|
||||||
@ -465,7 +441,7 @@ js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache)
|
|||||||
# endif
|
# endif
|
||||||
fprintf(fp, "GC %u\n", cx->runtime->gcNumber);
|
fprintf(fp, "GC %u\n", cx->runtime->gcNumber);
|
||||||
|
|
||||||
# define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)cache->mem)
|
# define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)mem)
|
||||||
P(fills);
|
P(fills);
|
||||||
P(nofills);
|
P(nofills);
|
||||||
P(rofills);
|
P(rofills);
|
||||||
@ -498,29 +474,25 @@ js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache)
|
|||||||
# undef P
|
# undef P
|
||||||
|
|
||||||
fprintf(fp, "hit rates: pc %g%% (proto %g%%), set %g%%, ini %g%%, full %g%%\n",
|
fprintf(fp, "hit rates: pc %g%% (proto %g%%), set %g%%, ini %g%%, full %g%%\n",
|
||||||
(100. * cache->pchits) / cache->tests,
|
(100. * pchits) / tests,
|
||||||
(100. * cache->protopchits) / cache->tests,
|
(100. * protopchits) / tests,
|
||||||
(100. * (cache->addpchits + cache->setpchits))
|
(100. * (addpchits + setpchits)) / settests,
|
||||||
/ cache->settests,
|
(100. * inipchits) / initests,
|
||||||
(100. * cache->inipchits) / cache->initests,
|
(100. * (tests - misses)) / tests);
|
||||||
(100. * (cache->tests - cache->misses)) / cache->tests);
|
|
||||||
fflush(fp);
|
fflush(fp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
PCMETER(cache->flushes++);
|
PCMETER(flushes++);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
js_PurgePropertyCacheForScript(JSContext *cx, JSScript *script)
|
JSPropertyCache::purgeForScript(JSContext *cx, JSScript *script)
|
||||||
{
|
{
|
||||||
JSPropertyCache *cache;
|
JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
|
||||||
JSPropCacheEntry *entry;
|
|
||||||
|
|
||||||
cache = &JS_PROPERTY_CACHE(cx);
|
for (JSPropCacheEntry *entry = table; entry < table + PROPERTY_CACHE_SIZE; entry++) {
|
||||||
for (entry = cache->table; entry < cache->table + PROPERTY_CACHE_SIZE;
|
|
||||||
entry++) {
|
|
||||||
if (JS_UPTRDIFF(entry->kpc, script->code) < script->length) {
|
if (JS_UPTRDIFF(entry->kpc, script->code) < script->length) {
|
||||||
entry->kpc = NULL;
|
entry->kpc = NULL;
|
||||||
entry->kshape = 0;
|
entry->kshape = 0;
|
||||||
|
@ -46,46 +46,33 @@
|
|||||||
* polymorphic callsite method/get/set speedups. For details, see
|
* polymorphic callsite method/get/set speedups. For details, see
|
||||||
* <https://developer.mozilla.org/en/SpiderMonkey/Internals/Property_cache>.
|
* <https://developer.mozilla.org/en/SpiderMonkey/Internals/Property_cache>.
|
||||||
*/
|
*/
|
||||||
#define PROPERTY_CACHE_LOG2 12
|
const int PROPERTY_CACHE_LOG2 = 12;
|
||||||
#define PROPERTY_CACHE_SIZE JS_BIT(PROPERTY_CACHE_LOG2)
|
const jsuword PROPERTY_CACHE_SIZE = JS_BIT(PROPERTY_CACHE_LOG2);
|
||||||
#define PROPERTY_CACHE_MASK JS_BITMASK(PROPERTY_CACHE_LOG2)
|
const jsuword 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.
|
* Property cache value capability macros.
|
||||||
*/
|
*/
|
||||||
#define PCVCAP_PROTOBITS 4
|
const int PCVCAP_PROTOBITS = 4;
|
||||||
#define PCVCAP_PROTOSIZE JS_BIT(PCVCAP_PROTOBITS)
|
const jsuword PCVCAP_PROTOSIZE = JS_BIT(PCVCAP_PROTOBITS);
|
||||||
#define PCVCAP_PROTOMASK JS_BITMASK(PCVCAP_PROTOBITS)
|
const jsuword PCVCAP_PROTOMASK = JS_BITMASK(PCVCAP_PROTOBITS);
|
||||||
|
|
||||||
#define PCVCAP_SCOPEBITS 4
|
const int PCVCAP_SCOPEBITS = 4;
|
||||||
#define PCVCAP_SCOPESIZE JS_BIT(PCVCAP_SCOPEBITS)
|
const jsuword PCVCAP_SCOPESIZE = JS_BIT(PCVCAP_SCOPEBITS);
|
||||||
#define PCVCAP_SCOPEMASK JS_BITMASK(PCVCAP_SCOPEBITS)
|
const jsuword PCVCAP_SCOPEMASK = JS_BITMASK(PCVCAP_SCOPEBITS);
|
||||||
|
|
||||||
#define PCVCAP_TAGBITS (PCVCAP_PROTOBITS + PCVCAP_SCOPEBITS)
|
const jsuword PCVCAP_TAGBITS = PCVCAP_PROTOBITS + PCVCAP_SCOPEBITS;
|
||||||
#define PCVCAP_TAGMASK JS_BITMASK(PCVCAP_TAGBITS)
|
const jsuword PCVCAP_TAGMASK = JS_BITMASK(PCVCAP_TAGBITS);
|
||||||
#define PCVCAP_TAG(t) ((t) & PCVCAP_TAGMASK)
|
inline jsuword PCVCAP_TAG(jsuword t) { return t & PCVCAP_TAGMASK; }
|
||||||
|
|
||||||
#define PCVCAP_MAKE(t,s,p) ((uint32(t) << PCVCAP_TAGBITS) | \
|
inline jsuword PCVCAP_MAKE(jsuword t, jsuword s, jsuword p)
|
||||||
((s) << PCVCAP_PROTOBITS) | \
|
{
|
||||||
(p))
|
return (uint32(t) << PCVCAP_TAGBITS) | (s << PCVCAP_PROTOBITS) | p;
|
||||||
#define PCVCAP_SHAPE(t) ((t) >> PCVCAP_TAGBITS)
|
}
|
||||||
|
|
||||||
#define SHAPE_OVERFLOW_BIT JS_BIT(32 - PCVCAP_TAGBITS)
|
inline jsuword PCVCAP_SHAPE(jsuword t) { return t >> PCVCAP_TAGBITS; }
|
||||||
|
|
||||||
|
const jsuword SHAPE_OVERFLOW_BIT = JS_BIT(32 - PCVCAP_TAGBITS);
|
||||||
|
|
||||||
struct JSPropCacheEntry {
|
struct JSPropCacheEntry {
|
||||||
jsbytecode *kpc; /* pc if vcap tag is <= 1, else atom */
|
jsbytecode *kpc; /* pc if vcap tag is <= 1, else atom */
|
||||||
@ -106,15 +93,62 @@ struct JSPropCacheEntry {
|
|||||||
* Special value for functions returning JSPropCacheEntry * to distinguish
|
* Special value for functions returning JSPropCacheEntry * to distinguish
|
||||||
* between failure and no no-cache-fill cases.
|
* between failure and no no-cache-fill cases.
|
||||||
*/
|
*/
|
||||||
#define JS_NO_PROP_CACHE_FILL ((JSPropCacheEntry *) NULL + 1)
|
JSPropCacheEntry * const JS_NO_PROP_CACHE_FILL = (JSPropCacheEntry *) NULL + 1;
|
||||||
|
|
||||||
#if defined DEBUG_brendan || defined DEBUG_brendaneich
|
#if defined DEBUG_brendan || defined DEBUG_brendaneich
|
||||||
#define JS_PROPERTY_CACHE_METERING 1
|
#define JS_PROPERTY_CACHE_METERING 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct JSPropertyCache {
|
class JSPropertyCache {
|
||||||
|
public:
|
||||||
|
JS_ALWAYS_INLINE JS_REQUIRES_STACK void
|
||||||
|
test(JSContext *cx, jsbytecode *pc, JSObject **objp,
|
||||||
|
JSObject **pobjp, JSPropCacheEntry **entryp, JSAtom **atomp);
|
||||||
|
|
||||||
|
JS_ALWAYS_INLINE JS_REQUIRES_STACK bool
|
||||||
|
testForSet(JSContext *cx, jsbytecode *pc, JSObject **objp,
|
||||||
|
JSObject **pobjp, JSPropCacheEntry **entryp, JSAtom **atomp);
|
||||||
|
|
||||||
|
JS_ALWAYS_INLINE bool
|
||||||
|
testForInit(JSContext *cx, jsbytecode *pc, JSObject *obj,
|
||||||
|
JSPropCacheEntry **entryp, JSScopeProperty **spropp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 JSPropCacheEntry *
|
||||||
|
fill(JSContext *cx, JSObject *obj, uintN scopeIndex, uintN protoIndex, JSObject *pobj,
|
||||||
|
JSScopeProperty *sprop, JSBool adding);
|
||||||
|
|
||||||
|
void purge(JSContext *cx);
|
||||||
|
void purgeForScript(JSContext *cx, JSScript *script);
|
||||||
|
|
||||||
|
private:
|
||||||
|
JS_REQUIRES_STACK JSAtom *
|
||||||
|
fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject **pobjp,
|
||||||
|
JSPropCacheEntry **entryp);
|
||||||
|
|
||||||
|
inline void assertEmpty();
|
||||||
|
|
||||||
|
inline JSPropCacheEntry *fillEntry(jsuword khash, jsbytecode *pc, jsuword kshape,
|
||||||
|
jsuword vcap, jsuword vword);
|
||||||
|
|
||||||
|
inline JSPropCacheEntry *fillByPC(jsbytecode *pc, jsuword kshape,
|
||||||
|
jsuword vshape, jsuword scopeIndex, jsuword protoIndex,
|
||||||
|
jsuword vword);
|
||||||
|
|
||||||
|
inline JSPropCacheEntry *fillByAtom(JSAtom *atom, JSObject *obj,
|
||||||
|
jsuword vshape, jsuword scopeIndex, jsuword protoIndex,
|
||||||
|
jsuword vword);
|
||||||
|
|
||||||
JSPropCacheEntry table[PROPERTY_CACHE_SIZE];
|
JSPropCacheEntry table[PROPERTY_CACHE_SIZE];
|
||||||
JSBool empty;
|
JSBool empty;
|
||||||
|
|
||||||
#ifdef JS_PROPERTY_CACHE_METERING
|
#ifdef JS_PROPERTY_CACHE_METERING
|
||||||
JSPropCacheEntry *pctestentry; /* entry of the last PC-based test */
|
JSPropCacheEntry *pctestentry; /* entry of the last PC-based test */
|
||||||
uint32 fills; /* number of cache entry fills */
|
uint32 fills; /* number of cache entry fills */
|
||||||
@ -153,109 +187,43 @@ typedef struct JSPropertyCache {
|
|||||||
#else
|
#else
|
||||||
# define PCMETER(x) ((void)0)
|
# define PCMETER(x) ((void)0)
|
||||||
#endif
|
#endif
|
||||||
} JSPropertyCache;
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Property cache value tagging/untagging macros.
|
* Property cache value tagging/untagging macros.
|
||||||
*/
|
*/
|
||||||
#define PCVAL_OBJECT 0
|
const jsuword PCVAL_OBJECT = 0;
|
||||||
#define PCVAL_SLOT 1
|
const jsuword PCVAL_SLOT = 1;
|
||||||
#define PCVAL_SPROP 2
|
const jsuword PCVAL_SPROP = 2;
|
||||||
|
|
||||||
#define PCVAL_TAGBITS 2
|
const int PCVAL_TAGBITS = 2;
|
||||||
#define PCVAL_TAGMASK JS_BITMASK(PCVAL_TAGBITS)
|
const jsuword 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
|
inline jsuword PCVAL_TAG(jsuword v) { return v & PCVAL_TAGMASK; }
|
||||||
#define PCVAL_IS_NULL(v) ((v) == PCVAL_NULL)
|
inline jsuword PCVAL_CLRTAG(jsuword v) { return v & ~PCVAL_TAGMASK; }
|
||||||
|
inline jsuword PCVAL_SETTAG(jsuword v, jsuword t) { return v | t; }
|
||||||
|
|
||||||
#define PCVAL_IS_OBJECT(v) (PCVAL_TAG(v) == PCVAL_OBJECT)
|
const jsuword PCVAL_NULL = 0;
|
||||||
#define PCVAL_TO_OBJECT(v) ((JSObject *) (v))
|
inline bool PCVAL_IS_NULL(jsuword v) { return v == PCVAL_NULL; }
|
||||||
#define OBJECT_TO_PCVAL(obj) ((jsuword) (obj))
|
|
||||||
|
|
||||||
#define PCVAL_OBJECT_TO_JSVAL(v) OBJECT_TO_JSVAL(PCVAL_TO_OBJECT(v))
|
inline bool PCVAL_IS_OBJECT(jsuword v) { return PCVAL_TAG(v) == PCVAL_OBJECT; }
|
||||||
#define JSVAL_OBJECT_TO_PCVAL(v) OBJECT_TO_PCVAL(JSVAL_TO_OBJECT(v))
|
inline JSObject *PCVAL_TO_OBJECT(jsuword v) { return (JSObject *) v; }
|
||||||
|
inline jsuword OBJECT_TO_PCVAL(JSObject *obj) { return (jsuword) obj; }
|
||||||
|
|
||||||
#define PCVAL_IS_SLOT(v) ((v) & PCVAL_SLOT)
|
inline jsval PCVAL_OBJECT_TO_JSVAL(jsuword v) { return OBJECT_TO_JSVAL(PCVAL_TO_OBJECT(v)); }
|
||||||
#define PCVAL_TO_SLOT(v) ((jsuint)(v) >> 1)
|
inline jsuword JSVAL_OBJECT_TO_PCVAL(jsval v) { return OBJECT_TO_PCVAL(JSVAL_TO_OBJECT(v)); }
|
||||||
#define SLOT_TO_PCVAL(i) (((jsuword)(i) << 1) | PCVAL_SLOT)
|
|
||||||
|
|
||||||
#define PCVAL_IS_SPROP(v) (PCVAL_TAG(v) == PCVAL_SPROP)
|
inline bool PCVAL_IS_SLOT(jsuword v) { return (v & PCVAL_SLOT) != 0; }
|
||||||
#define PCVAL_TO_SPROP(v) ((JSScopeProperty *) PCVAL_CLRTAG(v))
|
inline jsuint PCVAL_TO_SLOT(jsuword v) { return (jsuint) v >> 1; }
|
||||||
#define SPROP_TO_PCVAL(sprop) PCVAL_SETTAG(sprop, PCVAL_SPROP)
|
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; }
|
||||||
* Fill property cache entry for key cx->fp->pc, optimized value word computed
|
inline JSScopeProperty *PCVAL_TO_SPROP(jsuword v) { return (JSScopeProperty *) PCVAL_CLRTAG(v); }
|
||||||
* 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);
|
|
||||||
|
|
||||||
/*
|
inline jsuword
|
||||||
* Property cache lookup macros. PROPERTY_CACHE_TEST is designed to inline the
|
SPROP_TO_PCVAL(JSScopeProperty *sprop)
|
||||||
* 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
|
return PCVAL_SETTAG((jsuword) sprop, PCVAL_SPROP);
|
||||||
* 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___ */
|
#endif /* jspropcache_h___ */
|
||||||
|
223
js/src/jspropcacheinlines.h
Normal file
223
js/src/jspropcacheinlines.h
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
|
*
|
||||||
|
* ***** 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 jspropcacheinlines_h___
|
||||||
|
#define jspropcacheinlines_h___
|
||||||
|
|
||||||
|
inline JSBool
|
||||||
|
js_IsPropertyCacheDisabled(JSContext *cx)
|
||||||
|
{
|
||||||
|
return cx->runtime->shapeGen >= SHAPE_OVERFLOW_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
inline jsuword
|
||||||
|
PROPERTY_CACHE_HASH(jsuword pc, jsuword kshape)
|
||||||
|
{
|
||||||
|
return (((pc >> PROPERTY_CACHE_LOG2) ^ pc) + (kshape)) & PROPERTY_CACHE_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline jsuword
|
||||||
|
PROPERTY_CACHE_HASH_PC(jsbytecode *pc, jsuword kshape)
|
||||||
|
{
|
||||||
|
return PROPERTY_CACHE_HASH((jsuword) pc, kshape);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline jsuword
|
||||||
|
PROPERTY_CACHE_HASH_ATOM(JSAtom *atom, JSObject *obj)
|
||||||
|
{
|
||||||
|
return PROPERTY_CACHE_HASH((jsuword) atom >> 2, OBJ_SHAPE(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Property cache lookup.
|
||||||
|
*
|
||||||
|
* 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 JSPropertyCache::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.
|
||||||
|
*/
|
||||||
|
#define PROPERTY_CACHE_TEST(cx, pc, obj, pobj, entry, atom) \
|
||||||
|
JS_PROPERTY_CACHE(cx).test(cx, pc, &obj, &pobj, &entry, &atom)
|
||||||
|
|
||||||
|
JS_ALWAYS_INLINE void
|
||||||
|
JSPropertyCache::test(JSContext *cx, jsbytecode *pc, JSObject **objp,
|
||||||
|
JSObject **pobjp, JSPropCacheEntry **entryp, JSAtom **atomp)
|
||||||
|
{
|
||||||
|
JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
|
||||||
|
JS_ASSERT(OBJ_IS_NATIVE(*objp));
|
||||||
|
|
||||||
|
uint32 kshape = OBJ_SHAPE(*objp);
|
||||||
|
JSPropCacheEntry *entry = &table[PROPERTY_CACHE_HASH_PC(pc, kshape)];
|
||||||
|
PCMETER(pctestentry = entry);
|
||||||
|
PCMETER(tests++);
|
||||||
|
JS_ASSERT(objp != pobjp);
|
||||||
|
if (entry->kpc == pc && entry->kshape == kshape) {
|
||||||
|
JSObject *tmp;
|
||||||
|
JSObject *pobj = *objp;
|
||||||
|
|
||||||
|
JS_ASSERT(PCVCAP_TAG(entry->vcap) <= 1);
|
||||||
|
if (PCVCAP_TAG(entry->vcap) == 1 && (tmp = pobj->getProto()) != NULL)
|
||||||
|
pobj = tmp;
|
||||||
|
|
||||||
|
if (JS_LOCK_OBJ_IF_SHAPE(cx, pobj, PCVCAP_SHAPE(entry->vcap))) {
|
||||||
|
PCMETER(pchits++);
|
||||||
|
PCMETER(!PCVCAP_TAG(entry->vcap) || protopchits++);
|
||||||
|
*pobjp = pobj;
|
||||||
|
*entryp = entry;
|
||||||
|
*atomp = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*atomp = fullTest(cx, pc, objp, pobjp, entryp);
|
||||||
|
if (*atomp)
|
||||||
|
PCMETER(misses++);
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_ALWAYS_INLINE bool
|
||||||
|
JSPropertyCache::testForSet(JSContext *cx, jsbytecode *pc, JSObject **objp,
|
||||||
|
JSObject **pobjp, JSPropCacheEntry **entryp, JSAtom **atomp)
|
||||||
|
{
|
||||||
|
uint32 kshape = OBJ_SHAPE(*objp);
|
||||||
|
JSPropCacheEntry *entry = &table[PROPERTY_CACHE_HASH_PC(pc, kshape)];
|
||||||
|
PCMETER(pctestentry = entry);
|
||||||
|
PCMETER(tests++);
|
||||||
|
PCMETER(settests++);
|
||||||
|
|
||||||
|
*entryp = entry;
|
||||||
|
if (entry->kpc == pc && entry->kshape == kshape) {
|
||||||
|
JS_ASSERT(PCVCAP_TAG(entry->vcap) <= 1);
|
||||||
|
if (JS_LOCK_OBJ_IF_SHAPE(cx, *objp, kshape))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
*atomp = JS_PROPERTY_CACHE(cx).fullTest(cx, pc, objp, pobjp, entryp);
|
||||||
|
if (*atomp) {
|
||||||
|
PCMETER(cache->misses++);
|
||||||
|
PCMETER(cache->setmisses++);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_ALWAYS_INLINE bool
|
||||||
|
JSPropertyCache::testForInit(JSContext *cx, jsbytecode *pc, JSObject *obj,
|
||||||
|
JSPropCacheEntry **entryp, JSScopeProperty **spropp)
|
||||||
|
{
|
||||||
|
uint32 shape = OBJ_SCOPE(obj)->shape;
|
||||||
|
JSPropCacheEntry *entry = &table[PROPERTY_CACHE_HASH_PC(pc, shape)];
|
||||||
|
PCMETER(pctestentry = entry);
|
||||||
|
PCMETER(tests++);
|
||||||
|
PCMETER(initests++);
|
||||||
|
|
||||||
|
if (entry->kpc == pc &&
|
||||||
|
entry->kshape == shape &&
|
||||||
|
PCVCAP_SHAPE(entry->vcap) == cx->runtime->protoHazardShape)
|
||||||
|
{
|
||||||
|
JS_ASSERT(PCVCAP_TAG(entry->vcap) == 0);
|
||||||
|
PCMETER(cache->pchits++);
|
||||||
|
PCMETER(cache->inipchits++);
|
||||||
|
JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
|
||||||
|
JSScopeProperty *sprop = PCVAL_TO_SPROP(entry->vword);
|
||||||
|
JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
|
||||||
|
*spropp = sprop;
|
||||||
|
*entryp = entry;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline JSPropCacheEntry *
|
||||||
|
JSPropertyCache::fillEntry(jsuword khash, jsbytecode *pc, jsuword kshape,
|
||||||
|
jsuword vcap, jsuword vword)
|
||||||
|
{
|
||||||
|
JSPropCacheEntry *entry = &table[khash];
|
||||||
|
PCMETER(PCVAL_IS_NULL(entry->vword) || recycles++);
|
||||||
|
entry->kpc = pc;
|
||||||
|
entry->kshape = kshape;
|
||||||
|
entry->vcap = vcap;
|
||||||
|
entry->vword = vword;
|
||||||
|
|
||||||
|
empty = JS_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline JSPropCacheEntry *
|
||||||
|
JSPropertyCache::fillByPC(jsbytecode *pc, jsuword kshape,
|
||||||
|
jsuword vshape, jsuword scopeIndex, jsuword protoIndex,
|
||||||
|
jsuword vword)
|
||||||
|
{
|
||||||
|
return fillEntry(PROPERTY_CACHE_HASH_PC(pc, kshape), pc, kshape,
|
||||||
|
PCVCAP_MAKE(vshape, scopeIndex, protoIndex), vword);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline JSPropCacheEntry *
|
||||||
|
JSPropertyCache::fillByAtom(JSAtom *atom, JSObject *obj,
|
||||||
|
jsuword vshape, jsuword scopeIndex, jsuword protoIndex,
|
||||||
|
jsuword vword)
|
||||||
|
{
|
||||||
|
JS_ASSERT(obj->isDelegate());
|
||||||
|
jsuword khash = PROPERTY_CACHE_HASH_ATOM(atom, obj);
|
||||||
|
PCMETER(if (PCVCAP_TAG(table[khash].vcap) <= 1)
|
||||||
|
pcrecycles++);
|
||||||
|
return fillEntry(khash,
|
||||||
|
reinterpret_cast<jsbytecode *>(atom),
|
||||||
|
reinterpret_cast<jsuword>(obj),
|
||||||
|
PCVCAP_MAKE(vshape, scopeIndex, protoIndex),
|
||||||
|
vword);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* jspropcacheinlines_h___ */
|
@ -1619,7 +1619,7 @@ js_DestroyScript(JSContext *cx, JSScript *script)
|
|||||||
* regenerating shapes, so we don't have to purge fragments if the GC is
|
* regenerating shapes, so we don't have to purge fragments if the GC is
|
||||||
* currently running.
|
* currently running.
|
||||||
*
|
*
|
||||||
* JS_THREADSAFE note: js_PurgePropertyCacheForScript purges only the
|
* JS_THREADSAFE note: JSPropertyCache::purgeForScript purges only the
|
||||||
* current thread's property cache, so a script not owned by a function
|
* 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
|
* or object, which hands off lifetime management for that script to the
|
||||||
* GC, must be used by only one thread over its lifetime.
|
* GC, must be used by only one thread over its lifetime.
|
||||||
@ -1638,7 +1638,7 @@ js_DestroyScript(JSContext *cx, JSScript *script)
|
|||||||
JSStackFrame *fp = js_GetTopStackFrame(cx);
|
JSStackFrame *fp = js_GetTopStackFrame(cx);
|
||||||
|
|
||||||
if (!(fp && (fp->flags & JSFRAME_EVAL))) {
|
if (!(fp && (fp->flags & JSFRAME_EVAL))) {
|
||||||
js_PurgePropertyCacheForScript(cx, script);
|
JS_PROPERTY_CACHE(cx).purgeForScript(cx, script);
|
||||||
|
|
||||||
#ifdef CHECK_SCRIPT_OWNER
|
#ifdef CHECK_SCRIPT_OWNER
|
||||||
JS_ASSERT(script->owner == cx->thread);
|
JS_ASSERT(script->owner == cx->thread);
|
||||||
|
@ -77,6 +77,7 @@
|
|||||||
#include "jsxml.h"
|
#include "jsxml.h"
|
||||||
|
|
||||||
#include "jsatominlines.h"
|
#include "jsatominlines.h"
|
||||||
|
#include "jspropcacheinlines.h"
|
||||||
#include "jsscriptinlines.h"
|
#include "jsscriptinlines.h"
|
||||||
|
|
||||||
#include "jsautooplen.h" // generated headers last
|
#include "jsautooplen.h" // generated headers last
|
||||||
@ -8521,7 +8522,7 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2
|
|||||||
obj2->dropProperty(cx, prop);
|
obj2->dropProperty(cx, prop);
|
||||||
ABORT_TRACE("property found on non-native object");
|
ABORT_TRACE("property found on non-native object");
|
||||||
}
|
}
|
||||||
entry = js_FillPropertyCache(cx, aobj, 0, protoIndex, obj2,
|
entry = JS_PROPERTY_CACHE(cx).fill(cx, aobj, 0, protoIndex, obj2,
|
||||||
(JSScopeProperty*) prop, false);
|
(JSScopeProperty*) prop, false);
|
||||||
JS_ASSERT(entry);
|
JS_ASSERT(entry);
|
||||||
if (entry == JS_NO_PROP_CACHE_FILL)
|
if (entry == JS_NO_PROP_CACHE_FILL)
|
||||||
|
Loading…
Reference in New Issue
Block a user