void js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, JSResolvingEntry *entry, uint32 generation) { JSDHashTable *table; /* * Clear flag from entry->flags and return early if other flags remain. * We must take care to re-lookup entry if the table has changed since * it was found by js_StartResolving. */ table = cx->resolvingTable; if (!entry || table->generation != generation) { entry = (JSResolvingEntry *) JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP); } JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)); entry->flags &= ~flag; if (entry->flags) return; /* * Do a raw remove only if fewer entries were removed than would cause * alpha to be less than .5 (alpha is at most .75). Otherwise, we just * call JS_DHashTableOperate to re-lookup the key and remove its entry, * compressing or shrinking the table as needed. */ if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2) JS_DHashTableRawRemove(table, &entry->hdr); else JS_DHashTableOperate(table, key, JS_DHASH_REMOVE); }
JSAtom * js_AtomizeDouble(JSContext *cx, jsdouble d) { JSAtomState *state; JSDHashTable *table; JSAtomHashEntry *entry; uint32 gen; jsdouble *key; jsval v; state = &cx->runtime->atomState; table = &state->doubleAtoms; JS_LOCK(cx, &state->lock); entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, &d, JS_DHASH_ADD)); if (!entry) goto failed_hash_add; if (entry->keyAndFlags == 0) { gen = ++table->generation; JS_UNLOCK(cx, &state->lock); key = js_NewWeaklyRootedDouble(cx, d); if (!key) return NULL; JS_LOCK(cx, &state->lock); if (table->generation == gen) { JS_ASSERT(entry->keyAndFlags == 0); } else { entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key, JS_DHASH_ADD)); if (!entry) goto failed_hash_add; if (entry->keyAndFlags != 0) goto finish; ++table->generation; } INIT_ATOM_ENTRY(entry, key); } finish: v = DOUBLE_TO_JSVAL((jsdouble *)ATOM_ENTRY_KEY(entry)); cx->weakRoots.lastAtom = v; JS_UNLOCK(cx, &state->lock); return (JSAtom *)v; failed_hash_add: JS_UNLOCK(cx, &state->lock); JS_ReportOutOfMemory(cx); return NULL; }
void XPCJSRuntime::UnsetContextGlobals() { if(!mClearedGlobalObjects.ops) return; RestoreContextGlobals(); JSContext *iter = nsnull, *acx; while((acx = JS_ContextIterator(GetJSRuntime(), &iter))) { if(nsXPConnect::GetXPConnect()->GetRequestDepth(acx) == 0) { JS_ClearNewbornRoots(acx); if(acx->globalObject) { JSDHashEntryHdr* entry = JS_DHashTableOperate(&mClearedGlobalObjects, acx, JS_DHASH_ADD); ClearedGlobalObject* clearedGlobal = reinterpret_cast<ClearedGlobalObject*>(entry); if(clearedGlobal) { clearedGlobal->mContext = acx; clearedGlobal->mGlobalObject = acx->globalObject; acx->globalObject = nsnull; } } } } }
void XPCJSRuntime::TraceXPConnectRoots(JSTracer *trc) { if(mClearedGlobalObjects.ops) { JSContext *iter = nsnull, *acx; while((acx = JS_ContextIterator(GetJSRuntime(), &iter))) { JSDHashEntryHdr* entry = JS_DHashTableOperate(&mClearedGlobalObjects, acx, JS_DHASH_LOOKUP); if(JS_DHASH_ENTRY_IS_BUSY(entry)) { ClearedGlobalObject* clearedGlobal = reinterpret_cast<ClearedGlobalObject*>(entry); JS_CALL_OBJECT_TRACER(trc, clearedGlobal->mGlobalObject, "global object"); } } } XPCWrappedNativeScope::TraceJS(trc, this); for(XPCRootSetElem *e = mVariantRoots; e ; e = e->GetNextRoot()) static_cast<XPCTraceableVariant*>(e)->TraceJS(trc); for(XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot()) static_cast<nsXPCWrappedJS*>(e)->TraceJS(trc); if(mJSHolders.ops) JS_DHashTableEnumerate(&mJSHolders, TraceJSHolder, trc); }
JSAtom * js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length) { JSString str, *str2; JSAtomState *state; JSDHashEntryHdr *hdr; if (length == 1) { jschar c = *chars; if (c < UNIT_STRING_LIMIT) return (JSAtom *) STRING_TO_JSVAL(JSString::unitString(c)); } str.initFlat((jschar *)chars, length); state = &cx->runtime->atomState; JS_LOCK(cx, &state->lock); hdr = JS_DHashTableOperate(&state->stringAtoms, &str, JS_DHASH_LOOKUP); str2 = JS_DHASH_ENTRY_IS_BUSY(hdr) ? (JSString *)ATOM_ENTRY_KEY(TO_ATOM_ENTRY(hdr)) : NULL; JS_UNLOCK(cx, &state->lock); return str2 ? (JSAtom *)STRING_TO_JSVAL(str2) : NULL; }
JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *idp) { uintN numclasses, maxclasses; JSClass **registry; numclasses = xdr->numclasses; maxclasses = xdr->maxclasses; if (numclasses == maxclasses) { maxclasses = (maxclasses == 0) ? CLASS_REGISTRY_MIN : maxclasses << 1; registry = (JSClass **) JS_realloc(xdr->cx, xdr->registry, maxclasses * sizeof(JSClass *)); if (!registry) return JS_FALSE; xdr->registry = registry; xdr->maxclasses = maxclasses; } else { JS_ASSERT(numclasses && numclasses < maxclasses); registry = xdr->registry; } registry[numclasses] = clasp; if (xdr->reghash) { JSRegHashEntry *entry = (JSRegHashEntry *) JS_DHashTableOperate(xdr->reghash, clasp->name, JS_DHASH_ADD); if (!entry) { JS_ReportOutOfMemory(xdr->cx); return JS_FALSE; } entry->name = clasp->name; entry->index = numclasses; } *idp = CLASS_INDEX_TO_ID(numclasses); xdr->numclasses = ++numclasses; return JS_TRUE; }
nsresult XPCJSRuntime::RemoveJSHolder(void* aHolder) { if(!mJSHolders.ops) return NS_ERROR_OUT_OF_MEMORY; JS_DHashTableOperate(&mJSHolders, aHolder, JS_DHASH_REMOVE); return NS_OK; }
JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name) { uintN i, numclasses; numclasses = xdr->numclasses; if (numclasses >= 10) { JSRegHashEntry *entry; /* Bootstrap reghash from registry on first overpopulated Find. */ if (!xdr->reghash) { xdr->reghash = JS_NewDHashTable(JS_DHashGetStubOps(), NULL, sizeof(JSRegHashEntry), JS_DHASH_DEFAULT_CAPACITY(numclasses)); if (xdr->reghash) { for (i = 0; i < numclasses; i++) { JSClass *clasp = xdr->registry[i]; entry = (JSRegHashEntry *) JS_DHashTableOperate((JSDHashTable *) xdr->reghash, clasp->name, JS_DHASH_ADD); entry->name = clasp->name; entry->index = i; } } } /* If we managed to create reghash, use it for O(1) Find. */ if (xdr->reghash) { entry = (JSRegHashEntry *) JS_DHashTableOperate((JSDHashTable *) xdr->reghash, name, JS_DHASH_LOOKUP); if (JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)) return CLASS_INDEX_TO_ID(entry->index); } } /* Only a few classes, or we couldn't malloc reghash: use linear search. */ for (i = 0; i < numclasses; i++) { if (!strcmp(name, xdr->registry[i]->name)) return CLASS_INDEX_TO_ID(i); } return 0; }
JSObject* XPCJSRuntime::GetUnsetContextGlobal(JSContext* cx) { if(!mClearedGlobalObjects.ops) return nsnull; JSDHashEntryHdr* entry = JS_DHashTableOperate(&mClearedGlobalObjects, cx, JS_DHASH_LOOKUP); ClearedGlobalObject* clearedGlobal = reinterpret_cast<ClearedGlobalObject*>(entry); return JS_DHASH_ENTRY_IS_BUSY(entry) ? clearedGlobal->mGlobalObject : nsnull; }
nsresult XPCJSRuntime::AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer) { if(!mJSHolders.ops) return NS_ERROR_OUT_OF_MEMORY; ObjectHolder *entry = reinterpret_cast<ObjectHolder*>(JS_DHashTableOperate(&mJSHolders, aHolder, JS_DHASH_ADD)); if(!entry) return NS_ERROR_OUT_OF_MEMORY; entry->holder = aHolder; entry->tracer = aTracer; return NS_OK; }
void XPCJSRuntime::RestoreContextGlobals() { if(!mClearedGlobalObjects.ops || mClearedGlobalObjects.entryCount == 0) return; JSContext *iter = nsnull, *acx; while((acx = JS_ContextIterator(GetJSRuntime(), &iter))) { JSDHashEntryHdr* entry = JS_DHashTableOperate(&mClearedGlobalObjects, acx, JS_DHASH_LOOKUP); if(JS_DHASH_ENTRY_IS_BUSY(entry)) { ClearedGlobalObject* clearedGlobal = reinterpret_cast<ClearedGlobalObject*>(entry); acx->globalObject = clearedGlobal->mGlobalObject; } } JS_DHashTableEnumerate(&mClearedGlobalObjects, RemoveContextGlobal, nsnull); }
JSBool js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, JSResolvingEntry **entryp) { JSDHashTable *table; JSResolvingEntry *entry; table = cx->resolvingTable; if (!table) { table = JS_NewDHashTable(&resolving_dhash_ops, NULL, sizeof(JSResolvingEntry), JS_DHASH_MIN_SIZE); if (!table) goto outofmem; cx->resolvingTable = table; } entry = (JSResolvingEntry *) JS_DHashTableOperate(table, key, JS_DHASH_ADD); if (!entry) goto outofmem; if (entry->flags & flag) { /* An entry for (key, flag) exists already -- dampen recursion. */ entry = NULL; } else { /* Fill in key if we were the first to add entry, then set flag. */ if (!entry->key.obj) entry->key = *key; entry->flags |= flag; } *entryp = entry; return JS_TRUE; outofmem: JS_ReportOutOfMemory(cx); return JS_FALSE; }
JSAtom * js_AtomizeString(JSContext *cx, JSString *str, uintN flags) { jsval v; JSAtomState *state; JSDHashTable *table; JSAtomHashEntry *entry; JSString *key; uint32 gen; JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY))); JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR); if (str->isAtomized()) return (JSAtom *) STRING_TO_JSVAL(str); size_t length = str->length(); if (length == 1) { jschar c = str->chars()[0]; if (c < UNIT_STRING_LIMIT) return (JSAtom *) STRING_TO_JSVAL(JSString::unitString(c)); } /* * Here we know that JSString::intStringTable covers only 256 (or at least * not 1000 or more) chars. We rely on order here to resolve the unit vs. * int string atom identity issue by giving priority to unit strings for * '0' through '9' (see JSString::intString in jsstrinlines.h). */ JS_STATIC_ASSERT(INT_STRING_LIMIT <= 999); if (2 <= length && length <= 3) { const jschar *chars = str->chars(); if ('1' <= chars[0] && chars[0] <= '9' && '0' <= chars[1] && chars[1] <= '9' && (length == 2 || ('0' <= chars[2] && chars[2] <= '9'))) { jsint i = (chars[0] - '0') * 10 + chars[1] - '0'; if (length == 3) i = i * 10 + chars[2] - '0'; if (jsuint(i) < INT_STRING_LIMIT) return (JSAtom *) STRING_TO_JSVAL(JSString::intString(i)); } } state = &cx->runtime->atomState; table = &state->stringAtoms; JS_LOCK(cx, &state->lock); entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, str, JS_DHASH_ADD)); if (!entry) goto failed_hash_add; if (entry->keyAndFlags != 0) { key = (JSString *)ATOM_ENTRY_KEY(entry); } else { /* * We created a new hashtable entry. Unless str is already allocated * from the GC heap and flat, we have to release state->lock as * string construction is a complex operation. For example, it can * trigger GC which may rehash the table and make the entry invalid. */ ++table->generation; if (!(flags & ATOM_TMPSTR) && str->isFlat()) { str->flatClearMutable(); key = str; } else { gen = table->generation; JS_UNLOCK(cx, &state->lock); if (flags & ATOM_TMPSTR) { if (flags & ATOM_NOCOPY) { key = js_NewString(cx, str->flatChars(), str->flatLength()); if (!key) return NULL; /* Finish handing off chars to the GC'ed key string. */ str->mChars = NULL; } else { key = js_NewStringCopyN(cx, str->flatChars(), str->flatLength()); if (!key) return NULL; } } else { JS_ASSERT(str->isDependent()); if (!js_UndependString(cx, str)) return NULL; key = str; } JS_LOCK(cx, &state->lock); if (table->generation == gen) { JS_ASSERT(entry->keyAndFlags == 0); } else { entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key, JS_DHASH_ADD)); if (!entry) goto failed_hash_add; if (entry->keyAndFlags != 0) { key = (JSString *)ATOM_ENTRY_KEY(entry); goto finish; } ++table->generation; } } INIT_ATOM_ENTRY(entry, key); key->flatSetAtomized(); } finish: ADD_ATOM_ENTRY_FLAGS(entry, flags & (ATOM_PINNED | ATOM_INTERNED)); JS_ASSERT(key->isAtomized()); v = STRING_TO_JSVAL(key); cx->weakRoots.lastAtom = v; JS_UNLOCK(cx, &state->lock); return (JSAtom *)v; failed_hash_add: JS_UNLOCK(cx, &state->lock); JS_ReportOutOfMemory(cx); return NULL; }