📄 jsscope.c
字号:
scope->lastProp = overwriting;
} else {
sprop = GetPropertyTreeChild(cx, sprop, overwriting);
if (sprop) {
JS_ASSERT(sprop != overwriting);
scope->lastProp = sprop;
}
overwriting = sprop;
}
break;
}
if (sprop == overwriting)
break;
}
if (overwriting) {
if (scope->table)
SPROP_STORE_PRESERVING_COLLISION(spp, overwriting);
scope->entryCount++;
}
CHECK_ANCESTOR_LINE(scope, JS_TRUE);
}
METER(addFailures);
return NULL;
}
JSScopeProperty *
js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope,
JSScopeProperty *sprop, uintN attrs, uintN mask,
JSPropertyOp getter, JSPropertyOp setter)
{
JSScopeProperty child, *newsprop, **spp;
CHECK_ANCESTOR_LINE(scope, JS_TRUE);
/* Allow only shared (slot-less) => unshared (slot-full) transition. */
attrs |= sprop->attrs & mask;
JS_ASSERT(!((attrs ^ sprop->attrs) & JSPROP_SHARED) ||
!(attrs & JSPROP_SHARED));
if (getter == JS_PropertyStub)
getter = NULL;
if (setter == JS_PropertyStub)
setter = NULL;
if (sprop->attrs == attrs &&
sprop->getter == getter &&
sprop->setter == setter) {
return sprop;
}
child.id = sprop->id;
child.getter = getter;
child.setter = setter;
child.slot = sprop->slot;
child.attrs = attrs;
child.flags = sprop->flags;
child.shortid = sprop->shortid;
if (SCOPE_LAST_PROP(scope) == sprop) {
/*
* Optimize the case where the last property added to scope is changed
* to have a different attrs, getter, or setter. In the last property
* case, we need not fork the property tree. But since we do not call
* js_AddScopeProperty, we may need to allocate a new slot directly.
*/
if ((sprop->attrs & JSPROP_SHARED) && !(attrs & JSPROP_SHARED)) {
JS_ASSERT(child.slot == SPROP_INVALID_SLOT);
if (!js_AllocSlot(cx, scope->object, &child.slot))
return NULL;
}
newsprop = GetPropertyTreeChild(cx, sprop->parent, &child);
if (newsprop) {
spp = js_SearchScope(scope, sprop->id, JS_FALSE);
JS_ASSERT(SPROP_FETCH(spp) == sprop);
if (scope->table)
SPROP_STORE_PRESERVING_COLLISION(spp, newsprop);
scope->lastProp = newsprop;
CHECK_ANCESTOR_LINE(scope, JS_TRUE);
}
} else {
/*
* Let js_AddScopeProperty handle this |overwriting| case, including
* the conservation of sprop->slot (if it's valid). We must not call
* js_RemoveScopeProperty here, it will free a valid sprop->slot and
* js_AddScopeProperty won't re-allocate it.
*/
newsprop = js_AddScopeProperty(cx, scope, child.id,
child.getter, child.setter, child.slot,
child.attrs, child.flags, child.shortid);
}
#ifdef DEBUG_brendan
if (!newsprop)
METER(changeFailures);
#endif
return newsprop;
}
JSBool
js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id)
{
JSScopeProperty **spp, *stored, *sprop;
uint32 size;
JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope));
CHECK_ANCESTOR_LINE(scope, JS_TRUE);
if (SCOPE_IS_SEALED(scope)) {
ReportReadOnlyScope(cx, scope);
return JS_FALSE;
}
METER(removes);
spp = js_SearchScope(scope, id, JS_FALSE);
stored = *spp;
sprop = SPROP_CLEAR_COLLISION(stored);
if (!sprop) {
METER(uselessRemoves);
return JS_TRUE;
}
/* Convert from a list to a hash so we can handle "middle deletes". */
if (!scope->table && sprop != scope->lastProp) {
if (!CreateScopeTable(scope)) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
spp = js_SearchScope(scope, id, JS_FALSE);
stored = *spp;
sprop = SPROP_CLEAR_COLLISION(stored);
}
/* First, if sprop is unshared and not cleared, free its slot number. */
if (SPROP_HAS_VALID_SLOT(sprop, scope))
js_FreeSlot(cx, scope->object, sprop->slot);
/* Next, remove id by setting its entry to a removed or free sentinel. */
if (SPROP_HAD_COLLISION(stored)) {
JS_ASSERT(scope->table);
*spp = SPROP_REMOVED;
scope->removedCount++;
} else {
METER(removeFrees);
if (scope->table)
*spp = NULL;
}
scope->entryCount--;
JS_RUNTIME_UNMETER(cx->runtime, liveScopeProps);
/* Update scope->lastProp directly, or set its deferred update flag. */
if (sprop == SCOPE_LAST_PROP(scope)) {
do {
SCOPE_REMOVE_LAST_PROP(scope);
if (!SCOPE_HAD_MIDDLE_DELETE(scope))
break;
sprop = SCOPE_LAST_PROP(scope);
} while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop));
} else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) {
SCOPE_SET_MIDDLE_DELETE(scope);
}
CHECK_ANCESTOR_LINE(scope, JS_TRUE);
/* Last, consider shrinking scope's table if its load factor is <= .25. */
size = SCOPE_CAPACITY(scope);
if (size > MIN_SCOPE_SIZE && scope->entryCount <= size >> 2) {
METER(shrinks);
(void) ChangeScope(cx, scope, -1);
}
return JS_TRUE;
}
void
js_ClearScope(JSContext *cx, JSScope *scope)
{
CHECK_ANCESTOR_LINE(scope, JS_TRUE);
#ifdef DEBUG
JS_LOCK_RUNTIME_VOID(cx->runtime,
cx->runtime->liveScopeProps -= scope->entryCount);
#endif
if (scope->table)
free(scope->table);
SCOPE_CLR_MIDDLE_DELETE(scope);
InitMinimalScope(scope);
}
#ifdef DEBUG_brendan
#include <stdio.h>
#include <math.h>
uint32 js_nkids_max;
uint32 js_nkids_sum;
double js_nkids_sqsum;
uint32 js_nkids_hist[11];
static void
MeterKidCount(uintN nkids)
{
if (nkids) {
js_nkids_sum += nkids;
js_nkids_sqsum += (double)nkids * nkids;
if (nkids > js_nkids_max)
js_nkids_max = nkids;
}
js_nkids_hist[JS_MIN(nkids, 10)]++;
}
static void
MeterPropertyTree(JSScopeProperty *node)
{
uintN i, nkids;
JSScopeProperty *kids, *kid;
PropTreeKidsChunk *chunk;
nkids = 0;
kids = node->kids;
if (kids) {
if (KIDS_IS_CHUNKY(kids)) {
for (chunk = KIDS_TO_CHUNK(kids); chunk; chunk = chunk->next) {
for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) {
kid = chunk->kids[i];
if (!kid)
break;
MeterPropertyTree(kid);
nkids++;
}
}
} else {
MeterPropertyTree(kids);
nkids = 1;
}
}
MeterKidCount(nkids);
}
JS_STATIC_DLL_CALLBACK(JSDHashOperator)
js_MeterPropertyTree(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number,
void *arg)
{
JSPropertyTreeEntry *entry = (JSPropertyTreeEntry *)hdr;
MeterPropertyTree(entry->child);
return JS_DHASH_NEXT;
}
#endif /* DEBUG_brendan */
void
js_SweepScopeProperties(JSRuntime *rt)
{
JSArena **ap, *a;
JSScopeProperty *limit, *sprop, *parent, *kids, *kid;
uintN liveCount;
PropTreeKidsChunk *chunk, *nextChunk;
uintN i;
#ifdef DEBUG_brendan
uint32 livePropCapacity = 0, totalLiveCount = 0;
static FILE *logfp;
if (!logfp)
logfp = fopen("/tmp/proptree.stats", "a");
MeterKidCount(rt->propertyTreeHash.entryCount);
JS_DHashTableEnumerate(&rt->propertyTreeHash, js_MeterPropertyTree, NULL);
{
double mean = 0., var = 0., sigma = 0.;
double nodesum = rt->livePropTreeNodes;
double kidsum = js_nkids_sum;
if (nodesum > 0 && kidsum >= 0) {
mean = kidsum / nodesum;
var = nodesum * js_nkids_sqsum - kidsum * kidsum;
if (var < 0.0 || nodesum <= 1)
var = 0.0;
else
var /= nodesum * (nodesum - 1);
/* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
sigma = (var != 0.) ? sqrt(var) : 0.;
}
fprintf(logfp,
"props %u nodes %g beta %g meankids %g sigma %g max %u",
rt->liveScopeProps, nodesum, nodesum / rt->liveScopeProps,
mean, sigma, js_nkids_max);
}
fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u",
js_nkids_hist[0], js_nkids_hist[1],
js_nkids_hist[2], js_nkids_hist[3],
js_nkids_hist[4], js_nkids_hist[5],
js_nkids_hist[6], js_nkids_hist[7],
js_nkids_hist[8], js_nkids_hist[9],
js_nkids_hist[10]);
js_nkids_sum = js_nkids_max = 0;
js_nkids_sqsum = 0;
memset(js_nkids_hist, 0, sizeof js_nkids_hist);
#endif
ap = &rt->propertyArenaPool.first.next;
while ((a = *ap) != NULL) {
limit = (JSScopeProperty *) a->avail;
liveCount = 0;
for (sprop = (JSScopeProperty *) a->base; sprop < limit; sprop++) {
/* If the id is null, sprop is already on the freelist. */
if (sprop->id == JSVAL_NULL)
continue;
/* If the mark bit is set, sprop is alive, so we skip it. */
if (sprop->flags & SPROP_MARK) {
sprop->flags &= ~SPROP_MARK;
liveCount++;
continue;
}
/* Ok, sprop is garbage to collect: unlink it from its parent. */
RemovePropertyTreeChild(rt, sprop);
/* Take care to reparent all sprop's kids to their grandparent. */
kids = sprop->kids;
if (kids) {
sprop->kids = NULL;
parent = sprop->parent;
if (KIDS_IS_CHUNKY(kids)) {
chunk = KIDS_TO_CHUNK(kids);
do {
for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) {
kid = chunk->kids[i];
if (!kid)
break;
JS_ASSERT(kid->parent == sprop);
InsertPropertyTreeChild(rt, parent, kid);
}
nextChunk = chunk->next;
DestroyPropTreeKidsChunk(rt, chunk);
} while ((chunk = nextChunk) != NULL);
} else {
kid = kids;
InsertPropertyTreeChild(rt, parent, kid);
}
}
/* Clear id so we know (above) that sprop is on the freelist. */
sprop->id = JSVAL_NULL;
FREENODE_INSERT(rt->propertyFreeList, sprop);
JS_RUNTIME_UNMETER(rt, livePropTreeNodes);
}
/* If a contains no live properties, return it to the malloc heap. */
if (liveCount == 0) {
for (sprop = (JSScopeProperty *) a->base; sprop < limit; sprop++)
FREENODE_REMOVE(sprop);
JS_ARENA_DESTROY(&rt->propertyArenaPool, a, ap);
} else {
#ifdef DEBUG_brendan
livePropCapacity += limit - (JSScopeProperty *) a->base;
totalLiveCount += liveCount;
#endif
ap = &a->next;
}
}
#ifdef DEBUG_brendan
fprintf(logfp, " arenautil %g%%\n",
(totalLiveCount * 100.) / livePropCapacity);
fflush(logfp);
#endif
}
JSBool
js_InitPropertyTree(JSRuntime *rt)
{
if (!JS_DHashTableInit(&rt->propertyTreeHash, &PropertyTreeHashOps, NULL,
sizeof(JSPropertyTreeEntry), JS_DHASH_MIN_SIZE)) {
rt->propertyTreeHash.ops = NULL;
return JS_FALSE;
}
JS_InitArenaPool(&rt->propertyArenaPool, "properties",
256 * sizeof(JSScopeProperty), sizeof(void *));
return JS_TRUE;
}
void
js_FinishPropertyTree(JSRuntime *rt)
{
if (rt->propertyTreeHash.ops) {
JS_DHashTableFinish(&rt->propertyTreeHash);
rt->propertyTreeHash.ops = NULL;
}
JS_FinishArenaPool(&rt->propertyArenaPool);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -