diff --git a/mozilla/js/src/jsscope.c b/mozilla/js/src/jsscope.c index 0d36cf88196..788238759ea 100644 --- a/mozilla/js/src/jsscope.c +++ b/mozilla/js/src/jsscope.c @@ -505,11 +505,13 @@ NewScopeProperty(JSRuntime *rt) #define CHUNK_TO_KIDS(chunk) ((JSScopeProperty *) \ ((jsuword)(chunk) | CHUNKY_KIDS_TAG)) #define MAX_KIDS_PER_CHUNK 10 +#define CHUNK_HASH_THRESHOLD 30 typedef struct PropTreeKidsChunk PropTreeKidsChunk; struct PropTreeKidsChunk { JSScopeProperty *kids[MAX_KIDS_PER_CHUNK]; + JSDHashTable *table; PropTreeKidsChunk *next; }; @@ -530,6 +532,8 @@ static void DestroyPropTreeKidsChunk(JSRuntime *rt, PropTreeKidsChunk *chunk) { JS_RUNTIME_UNMETER(rt, propTreeKidsChunks); + if (chunk->table) + JS_DHashTableDestroy(chunk->table); free(chunk); } @@ -538,6 +542,7 @@ static JSBool InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent, JSScopeProperty *child, PropTreeKidsChunk *sweptChunk) { + JSDHashTable *table; JSPropertyTreeEntry *entry; JSScopeProperty **childp, *kids, *sprop; PropTreeKidsChunk *chunk, **chunkp; @@ -546,8 +551,9 @@ InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent, JS_ASSERT(!parent || child->parent != parent); if (!parent) { + table = &rt->propertyTreeHash; entry = (JSPropertyTreeEntry *) - JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD); + JS_DHashTableOperate(table, child, JS_DHASH_ADD); if (!entry) return JS_FALSE; childp = &entry->child; @@ -580,6 +586,28 @@ InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent, if (kids) { if (KIDS_IS_CHUNKY(kids)) { chunk = KIDS_TO_CHUNK(kids); + + table = chunk->table; + if (table) { + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(table, child, JS_DHASH_ADD); + if (!entry) + return JS_FALSE; + if (!entry->child) { + entry->child = child; + while (chunk->next) + chunk = chunk->next; + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + childp = &chunk->kids[i]; + sprop = *childp; + if (!sprop) + goto insert; + } + chunkp = &chunk->next; + goto new_chunk; + } + } + do { for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { childp = &chunk->kids[i]; @@ -602,6 +630,7 @@ InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent, chunkp = &chunk->next; } while ((chunk = *chunkp) != NULL); + new_chunk: if (sweptChunk) { chunk = sweptChunk; } else { @@ -647,11 +676,14 @@ InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent, static PropTreeKidsChunk * RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child) { - JSPropertyTreeEntry *entry; + PropTreeKidsChunk *freeChunk; JSScopeProperty *parent, *kids, *kid; + JSDHashTable *table; PropTreeKidsChunk *list, *chunk, **chunkp, *lastChunk; uintN i, j; + JSPropertyTreeEntry *entry; + freeChunk = NULL; parent = child->parent; if (!parent) { /* @@ -659,16 +691,13 @@ RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child) * 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); + table = &rt->propertyTreeHash; } else { kids = parent->kids; if (KIDS_IS_CHUNKY(kids)) { list = chunk = KIDS_TO_CHUNK(kids); chunkp = &list; + table = chunk->table; do { for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { @@ -695,21 +724,58 @@ RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child) *chunkp = NULL; if (!list) parent->kids = NULL; - return lastChunk; + freeChunk = lastChunk; + goto out; } - return NULL; + goto out; } } chunkp = &chunk->next; } while ((chunk = *chunkp) != NULL); } else { + table = NULL; kid = kids; if (kid == child) parent->kids = NULL; } } - return NULL; + +out: + if (table) { + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(table, child, JS_DHASH_LOOKUP); + + if (entry->child == child) + JS_DHashTableRawRemove(table, &entry->hdr); + } + return freeChunk; +} + +static JSDHashTable * +HashChunks(PropTreeKidsChunk *chunk, uintN n) +{ + JSDHashTable *table; + uintN i; + JSScopeProperty *sprop; + JSPropertyTreeEntry *entry; + + table = JS_NewDHashTable(&PropertyTreeHashOps, NULL, + sizeof(JSPropertyTreeEntry), + JS_DHASH_DEFAULT_CAPACITY(n + 1)); + if (!table) + return NULL; + do { + for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { + sprop = chunk->kids[i]; + if (!sprop) + break; + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(table, sprop, JS_DHASH_ADD); + entry->child = sprop; + } + } while ((chunk = chunk->next) != NULL); + return table; } /* @@ -722,17 +788,19 @@ GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent, JSScopeProperty *child) { JSRuntime *rt; + JSDHashTable *table; JSPropertyTreeEntry *entry; JSScopeProperty *sprop; PropTreeKidsChunk *chunk; - uintN i; + uintN i, n; rt = cx->runtime; if (!parent) { JS_LOCK_RUNTIME(rt); + table = &rt->propertyTreeHash; entry = (JSPropertyTreeEntry *) - JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD); + JS_DHashTableOperate(table, child, JS_DHASH_ADD); if (!entry) goto out_of_memory; @@ -748,24 +816,57 @@ GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent, * 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. + * Patterns such as defining closures that capture a constructor's + * environment as getters or setters on the new object that is passed + * in as |this| can significantly increase fan-out below the property + * tree root -- see bug 335700 for details. */ entry = NULL; sprop = parent->kids; if (sprop) { if (KIDS_IS_CHUNKY(sprop)) { chunk = KIDS_TO_CHUNK(sprop); + + table = chunk->table; + if (table) { + JS_LOCK_RUNTIME(rt); + entry = (JSPropertyTreeEntry *) + JS_DHashTableOperate(table, child, JS_DHASH_LOOKUP); + sprop = entry->child; + if (sprop) { + JS_UNLOCK_RUNTIME(rt); + return sprop; + } + goto locked_not_found; + } + + n = 0; do { for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { sprop = chunk->kids[i]; - if (!sprop) + if (!sprop) { + n += i; + if (n >= CHUNK_HASH_THRESHOLD) { + chunk = KIDS_TO_CHUNK(parent->kids); + if (!chunk->table) { + table = HashChunks(chunk, n); + JS_LOCK_RUNTIME(rt); + if (!table) + goto out_of_memory; + if (chunk->table) + JS_DHashTableDestroy(table); + else + chunk->table = table; + goto locked_not_found; + } + } goto not_found; + } if (SPROP_MATCH(sprop, child)) return sprop; } + n += MAX_KIDS_PER_CHUNK; } while ((chunk = chunk->next) != NULL); } else { if (SPROP_MATCH(sprop, child)) @@ -777,6 +878,7 @@ GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent, JS_LOCK_RUNTIME(rt); } +locked_not_found: sprop = NewScopeProperty(rt); if (!sprop) goto out_of_memory; @@ -1654,7 +1756,8 @@ js_SweepScopeProperties(JSRuntime *rt) if (kids) { sprop->kids = NULL; parent = sprop->parent; - /* Validate that grandparent has no kids or chunky kids. */ + + /* Assert that grandparent has no kids or chunky kids. */ JS_ASSERT(!parent || !parent->kids || KIDS_IS_CHUNKY(parent->kids)); if (KIDS_IS_CHUNKY(kids)) {