📄 jsscope.c
字号:
}
#define SPROP_MATCH(sprop, child) \
SPROP_MATCH_PARAMS(sprop, (child)->id, (child)->getter, (child)->setter, \
(child)->slot, (child)->attrs, (child)->flags, \
(child)->shortid)
#define SPROP_MATCH_PARAMS(sprop, aid, agetter, asetter, aslot, aattrs, \
aflags, ashortid) \
((sprop)->id == (aid) && \
SPROP_MATCH_PARAMS_AFTER_ID(sprop, agetter, asetter, aslot, aattrs, \
aflags, ashortid))
#define SPROP_MATCH_PARAMS_AFTER_ID(sprop, agetter, asetter, aslot, aattrs, \
aflags, ashortid) \
((sprop)->getter == (agetter) && \
(sprop)->setter == (asetter) && \
(sprop)->slot == (aslot) && \
(sprop)->attrs == (aattrs) && \
(((sprop)->flags ^ (aflags)) & ~SPROP_FLAGS_NOT_MATCHED) == 0 && \
(sprop)->shortid == (ashortid))
JS_STATIC_DLL_CALLBACK(JSBool)
js_MatchScopeProperty(JSDHashTable *table,
const JSDHashEntryHdr *hdr,
const void *key)
{
const JSPropertyTreeEntry *entry = (const JSPropertyTreeEntry *)hdr;
const JSScopeProperty *sprop = entry->child;
const JSScopeProperty *kprop = (const JSScopeProperty *)key;
return SPROP_MATCH(sprop, kprop);
}
static const JSDHashTableOps PropertyTreeHashOps = {
JS_DHashAllocTable,
JS_DHashFreeTable,
JS_DHashGetKeyStub,
js_HashScopeProperty,
js_MatchScopeProperty,
JS_DHashMoveEntryStub,
JS_DHashClearEntryStub,
JS_DHashFinalizeStub,
NULL
};
/*
* A property tree node on rt->propertyFreeList overlays the following prefix
* struct on JSScopeProperty.
*/
typedef struct FreeNode {
jsid id;
JSScopeProperty *next;
JSScopeProperty **prevp;
} FreeNode;
#define FREENODE(sprop) ((FreeNode *) (sprop))
#define FREENODE_INSERT(list, sprop) \
JS_BEGIN_MACRO \
FREENODE(sprop)->next = (list); \
FREENODE(sprop)->prevp = &(list); \
if (list) \
FREENODE(list)->prevp = &FREENODE(sprop)->next; \
(list) = (sprop); \
JS_END_MACRO
#define FREENODE_REMOVE(sprop) \
JS_BEGIN_MACRO \
*FREENODE(sprop)->prevp = FREENODE(sprop)->next; \
if (FREENODE(sprop)->next) \
FREENODE(FREENODE(sprop)->next)->prevp = FREENODE(sprop)->prevp; \
JS_END_MACRO
/* NB: Called with the runtime lock held. */
static JSScopeProperty *
NewScopeProperty(JSRuntime *rt)
{
JSScopeProperty *sprop;
sprop = rt->propertyFreeList;
if (sprop) {
FREENODE_REMOVE(sprop);
} else {
JS_ARENA_ALLOCATE_CAST(sprop, JSScopeProperty *,
&rt->propertyArenaPool,
sizeof(JSScopeProperty));
if (!sprop)
return NULL;
}
JS_RUNTIME_METER(rt, livePropTreeNodes);
JS_RUNTIME_METER(rt, totalPropTreeNodes);
return sprop;
}
#define CHUNKY_KIDS_TAG ((jsuword)1)
#define KIDS_IS_CHUNKY(kids) ((jsuword)(kids) & CHUNKY_KIDS_TAG)
#define KIDS_TO_CHUNK(kids) ((PropTreeKidsChunk *) \
((jsuword)(kids) & ~CHUNKY_KIDS_TAG))
#define CHUNK_TO_KIDS(chunk) ((JSScopeProperty *) \
((jsuword)(chunk) | CHUNKY_KIDS_TAG))
#define MAX_KIDS_PER_CHUNK 10
typedef struct PropTreeKidsChunk PropTreeKidsChunk;
struct PropTreeKidsChunk {
JSScopeProperty *kids[MAX_KIDS_PER_CHUNK];
PropTreeKidsChunk *next;
};
static PropTreeKidsChunk *
NewPropTreeKidsChunk(JSRuntime *rt)
{
PropTreeKidsChunk *chunk;
chunk = calloc(1, sizeof *chunk);
if (!chunk)
return NULL;
JS_ASSERT(((jsuword)chunk & CHUNKY_KIDS_TAG) == 0);
JS_RUNTIME_METER(rt, propTreeKidsChunks);
return chunk;
}
static void
DestroyPropTreeKidsChunk(JSRuntime *rt, PropTreeKidsChunk *chunk)
{
JS_RUNTIME_UNMETER(rt, propTreeKidsChunks);
free(chunk);
}
/* NB: Called with the runtime lock held. */
static JSBool
InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent,
JSScopeProperty *child)
{
JSPropertyTreeEntry *entry;
JSScopeProperty **childp, *kids, *sprop;
PropTreeKidsChunk *chunk, **chunkp;
uintN i;
JS_ASSERT(!parent || child->parent != parent);
if (!parent) {
entry = (JSPropertyTreeEntry *)
JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD);
if (!entry)
return JS_FALSE;
childp = &entry->child;
sprop = *childp;
if (!sprop) {
*childp = child;
} else {
/*
* A "Duplicate child" case.
*
* We can't do away with child, as at least one live scope entry
* still points at it. What's more, that scope's lastProp chains
* through an ancestor line to reach child, and js_Enumerate and
* others count on this linkage. We must leave child out of the
* hash table, and not require it to be there when we eventually
* GC it (see RemovePropertyTreeChild, below).
*
* It is necessary to leave the duplicate child out of the hash
* table to preserve entry uniqueness. It is safe to leave the
* child out of the hash table (unlike the duplicate child cases
* below), because the child's parent link will be null, which
* can't dangle.
*/
JS_ASSERT(sprop != child && SPROP_MATCH(sprop, child));
JS_RUNTIME_METER(rt, duplicatePropTreeNodes);
}
} else {
childp = &parent->kids;
kids = *childp;
if (kids) {
if (KIDS_IS_CHUNKY(kids)) {
chunk = KIDS_TO_CHUNK(kids);
do {
for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) {
childp = &chunk->kids[i];
sprop = *childp;
if (!sprop)
goto insert;
JS_ASSERT(sprop != child);
if (SPROP_MATCH(sprop, child)) {
/*
* Duplicate child, see comment above. In this
* case, we must let the duplicate be inserted at
* this level in the tree, so we keep iterating,
* looking for an empty slot in which to insert.
*/
JS_ASSERT(sprop != child);
JS_RUNTIME_METER(rt, duplicatePropTreeNodes);
}
}
chunkp = &chunk->next;
} while ((chunk = *chunkp) != NULL);
chunk = NewPropTreeKidsChunk(rt);
if (!chunk)
return JS_FALSE;
*chunkp = chunk;
childp = &chunk->kids[0];
} else {
sprop = kids;
JS_ASSERT(sprop != child);
if (SPROP_MATCH(sprop, child)) {
/*
* Duplicate child, see comment above. Once again, we
* must let duplicates created by deletion pile up in a
* kids-chunk-list, in order to find them when sweeping
* and thereby avoid dangling parent pointers.
*/
JS_RUNTIME_METER(rt, duplicatePropTreeNodes);
}
chunk = NewPropTreeKidsChunk(rt);
if (!chunk)
return JS_FALSE;
parent->kids = CHUNK_TO_KIDS(chunk);
chunk->kids[0] = sprop;
childp = &chunk->kids[1];
}
}
insert:
*childp = child;
}
child->parent = parent;
return JS_TRUE;
}
/* NB: Called with the runtime lock held. */
static void
RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child)
{
JSPropertyTreeEntry *entry;
JSScopeProperty *parent, *kids, *kid;
PropTreeKidsChunk *list, *chunk, **chunkp, *lastChunk;
uintN i, j;
parent = child->parent;
if (!parent) {
/*
* Don't remove child if it is not in rt->propertyTreeHash, but only
* matches a root child in the table that has compatible members. See
* the "Duplicate child" comments in InsertPropertyTreeChild, above.
*/
entry = (JSPropertyTreeEntry *)
JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_LOOKUP);
if (entry->child == child)
JS_DHashTableRawRemove(&rt->propertyTreeHash, &entry->hdr);
} else {
kids = parent->kids;
if (KIDS_IS_CHUNKY(kids)) {
list = chunk = KIDS_TO_CHUNK(kids);
chunkp = &list;
do {
for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) {
if (chunk->kids[i] == child) {
lastChunk = chunk;
if (!lastChunk->next) {
j = i + 1;
} else {
j = 0;
do {
chunkp = &lastChunk->next;
lastChunk = *chunkp;
} while (lastChunk->next);
}
for (; j < MAX_KIDS_PER_CHUNK; j++) {
if (!lastChunk->kids[j])
break;
}
--j;
if (chunk != lastChunk || j > i)
chunk->kids[i] = lastChunk->kids[j];
lastChunk->kids[j] = NULL;
if (j == 0) {
*chunkp = NULL;
if (!list)
parent->kids = NULL;
DestroyPropTreeKidsChunk(rt, lastChunk);
}
return;
}
}
chunkp = &chunk->next;
} while ((chunk = *chunkp) != NULL);
} else {
kid = kids;
if (kid == child)
parent->kids = NULL;
}
}
}
/*
* Called *without* the runtime lock held, this function acquires that lock
* only when inserting a new child. Thus there may be races to find or add
* a node that result in duplicates. We expect such races to be rare!
*/
static JSScopeProperty *
GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent,
JSScopeProperty *child)
{
JSRuntime *rt;
JSPropertyTreeEntry *entry;
JSScopeProperty *sprop;
PropTreeKidsChunk *chunk;
uintN i;
rt = cx->runtime;
if (!parent) {
JS_LOCK_RUNTIME(rt);
entry = (JSPropertyTreeEntry *)
JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD);
if (!entry)
goto out_of_memory;
sprop = entry->child;
if (sprop)
goto out;
} else {
/*
* Because chunks are appended at the end and never deleted except by
* the GC, we can search without taking the runtime lock. We may miss
* a matching sprop added by another thread, and make a duplicate one,
* but that is an unlikely, therefore small, cost. The property tree
* has extremely low fan-out below its root in popular embeddings with
* real-world workloads.
*
* If workload changes so as to increase fan-out significantly below
* the property tree root, we'll want to add another tag bit stored in
* parent->kids that indicates a JSDHashTable pointer.
*/
entry = NULL;
sprop = parent->kids;
if (sprop) {
if (KIDS_IS_CHUNKY(sprop)) {
chunk = KIDS_TO_CHUNK(sprop);
do {
for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) {
sprop = chunk->kids[i];
if (!sprop)
goto not_found;
if (SPROP_MATCH(sprop, child))
return sprop;
}
} while ((chunk = chunk->next) != NULL);
} else {
if (SPROP_MATCH(sprop, child))
return sprop;
}
}
not_found:
JS_LOCK_RUNTIME(rt);
}
sprop = NewScopeProperty(rt);
if (!sprop)
goto out_of_memory;
sprop->id = child->id;
sprop->getter = child->getter;
sprop->setter = child->setter;
sprop->slot = child->slot;
sprop->attrs = child->attrs;
sprop->flags = child->flags;
sprop->shortid = child->shortid;
sprop->parent = sprop->kids = NULL;
if (!parent) {
entry->child = sprop;
} else {
if (!InsertPropertyTreeChild(rt, parent, sprop))
goto out_of_memory;
}
out:
JS_UNLOCK_RUNTIME(rt);
return sprop;
out_of_memory:
JS_UNLOCK_RUNTIME(rt);
JS_ReportOutOfMemory(cx);
return NULL;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -