📄 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 10typedef 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 voidDestroyPropTreeKidsChunk(JSRuntime *rt, PropTreeKidsChunk *chunk){ JS_RUNTIME_UNMETER(rt, propTreeKidsChunks); free(chunk);}/* NB: Called with the runtime lock held. */static JSBoolInsertPropertyTreeChild(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 voidRemovePropertyTreeChild(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 + -