зеркало из https://github.com/mozilla/pjs.git
Bug 581875 - use js::HashSet in JSAtomState (r=igor)
This commit is contained in:
Родитель
ca8bd3912e
Коммит
832e801c21
|
@ -283,81 +283,38 @@ const char js_close_str[] = "close";
|
||||||
const char js_send_str[] = "send";
|
const char js_send_str[] = "send";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
|
||||||
* JSAtomState.stringAtoms hashtable entry. To support pinned and interned
|
|
||||||
* string atoms, we use the lowest bits of the keyAndFlags field to store
|
|
||||||
* ATOM_PINNED and ATOM_INTERNED flags.
|
|
||||||
*/
|
|
||||||
typedef struct JSAtomHashEntry {
|
|
||||||
JSDHashEntryHdr hdr;
|
|
||||||
jsuword keyAndFlags;
|
|
||||||
} JSAtomHashEntry;
|
|
||||||
|
|
||||||
#define ATOM_ENTRY_FLAG_MASK (ATOM_PINNED | ATOM_INTERNED)
|
|
||||||
|
|
||||||
JS_STATIC_ASSERT(ATOM_ENTRY_FLAG_MASK < JS_GCTHING_ALIGN);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper macros to access and modify JSAtomHashEntry.
|
* Helper macros to access and modify JSAtomHashEntry.
|
||||||
*/
|
*/
|
||||||
#define TO_ATOM_ENTRY(hdr) ((JSAtomHashEntry *) hdr)
|
|
||||||
#define ATOM_ENTRY_KEY(entry) \
|
|
||||||
((JSString *)((entry)->keyAndFlags & ~ATOM_ENTRY_FLAG_MASK))
|
|
||||||
#define ATOM_ENTRY_FLAGS(entry) \
|
|
||||||
((uintN)((entry)->keyAndFlags & ATOM_ENTRY_FLAG_MASK))
|
|
||||||
#define INIT_ATOM_ENTRY(entry, key) \
|
|
||||||
((void)((entry)->keyAndFlags = (jsuword)(key)))
|
|
||||||
#define ADD_ATOM_ENTRY_FLAGS(entry, flags) \
|
|
||||||
((void)((entry)->keyAndFlags |= (jsuword)(flags)))
|
|
||||||
#define CLEAR_ATOM_ENTRY_FLAGS(entry, flags) \
|
|
||||||
((void)((entry)->keyAndFlags &= ~(jsuword)(flags)))
|
|
||||||
|
|
||||||
static JSDHashNumber
|
inline AtomEntryType
|
||||||
HashString(JSDHashTable *table, const void *key);
|
StringToInitialAtomEntry(JSString *str)
|
||||||
|
|
||||||
static JSBool
|
|
||||||
MatchString(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key);
|
|
||||||
|
|
||||||
static const JSDHashTableOps StringHashOps = {
|
|
||||||
JS_DHashAllocTable,
|
|
||||||
JS_DHashFreeTable,
|
|
||||||
HashString,
|
|
||||||
MatchString,
|
|
||||||
JS_DHashMoveEntryStub,
|
|
||||||
JS_DHashClearEntryStub,
|
|
||||||
JS_DHashFinalizeStub,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
#define IS_INITIALIZED_STATE(state) ((state)->stringAtoms.ops != NULL)
|
|
||||||
|
|
||||||
static JSDHashNumber
|
|
||||||
HashString(JSDHashTable *table, const void *key)
|
|
||||||
{
|
{
|
||||||
return js_HashString((JSString *)key);
|
return (AtomEntryType) str;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSBool
|
inline uintN
|
||||||
MatchString(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key)
|
AtomEntryFlags(AtomEntryType entry)
|
||||||
{
|
{
|
||||||
JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
|
return (uintN) (entry & ATOM_ENTRY_FLAG_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
if (entry->keyAndFlags == 0) {
|
/*
|
||||||
/*
|
* Conceptually, we have compressed a HashMap<JSAtom *, uint> into a
|
||||||
* This happens when js_AtomizeString adds a new hash entry and
|
* HashMap<size_t>. Here, we promise that we are only changing the "value" of
|
||||||
* releases the lock but before it takes the lock the second time to
|
* the HashMap entry, so the const_cast is safe.
|
||||||
* initialize keyAndFlags for the entry.
|
*/
|
||||||
*
|
|
||||||
* We always return false for such entries so JS_DHashTableOperate
|
inline void
|
||||||
* never finds them. We clean them during GC's sweep phase.
|
AddAtomEntryFlags(const AtomEntryType &entry, uintN flags)
|
||||||
*
|
{
|
||||||
* It means that with a contested lock or when GC is triggered outside
|
const_cast<AtomEntryType &>(entry) |= AtomEntryType(flags);
|
||||||
* the lock we may end up adding two entries, but this is a price for
|
}
|
||||||
* simpler code.
|
|
||||||
*/
|
inline void
|
||||||
return JS_FALSE;
|
ClearAtomEntryFlags(const AtomEntryType &entry, uintN flags)
|
||||||
}
|
{
|
||||||
return js_EqualStrings(ATOM_ENTRY_KEY(entry), (JSString *)key);
|
const_cast<AtomEntryType &>(entry) &= ~AtomEntryType(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -373,50 +330,23 @@ js_InitAtomState(JSRuntime *rt)
|
||||||
{
|
{
|
||||||
JSAtomState *state = &rt->atomState;
|
JSAtomState *state = &rt->atomState;
|
||||||
|
|
||||||
/*
|
JS_ASSERT(!state->atoms.initialized());
|
||||||
* The caller must zero the state before calling this function.
|
if (!state->atoms.init(JS_STRING_HASH_COUNT))
|
||||||
*/
|
return false;
|
||||||
JS_ASSERT(!state->stringAtoms.ops);
|
|
||||||
|
|
||||||
if (!JS_DHashTableInit(&state->stringAtoms, &StringHashOps,
|
|
||||||
NULL, sizeof(JSAtomHashEntry),
|
|
||||||
JS_DHASH_DEFAULT_CAPACITY(JS_STRING_HASH_COUNT))) {
|
|
||||||
state->stringAtoms.ops = NULL;
|
|
||||||
return JS_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
js_InitLock(&state->lock);
|
js_InitLock(&state->lock);
|
||||||
#endif
|
#endif
|
||||||
JS_ASSERT(IS_INITIALIZED_STATE(state));
|
JS_ASSERT(state->atoms.initialized());
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSDHashOperator
|
|
||||||
js_string_uninterner(JSDHashTable *table, JSDHashEntryHdr *hdr,
|
|
||||||
uint32 number, void *arg)
|
|
||||||
{
|
|
||||||
JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
|
|
||||||
JSRuntime *rt = (JSRuntime *)arg;
|
|
||||||
JSString *str;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Any string entry that remains at this point must be initialized, as the
|
|
||||||
* last GC should clean any uninitialized ones.
|
|
||||||
*/
|
|
||||||
JS_ASSERT(entry->keyAndFlags != 0);
|
|
||||||
str = ATOM_ENTRY_KEY(entry);
|
|
||||||
|
|
||||||
js_FinalizeStringRT(rt, str);
|
|
||||||
return JS_DHASH_NEXT;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
js_FinishAtomState(JSRuntime *rt)
|
js_FinishAtomState(JSRuntime *rt)
|
||||||
{
|
{
|
||||||
JSAtomState *state = &rt->atomState;
|
JSAtomState *state = &rt->atomState;
|
||||||
|
|
||||||
if (!IS_INITIALIZED_STATE(state)) {
|
if (!state->atoms.initialized()) {
|
||||||
/*
|
/*
|
||||||
* We are called with uninitialized state when JS_NewRuntime fails and
|
* We are called with uninitialized state when JS_NewRuntime fails and
|
||||||
* calls JS_DestroyRuntime on a partially initialized runtime.
|
* calls JS_DestroyRuntime on a partially initialized runtime.
|
||||||
|
@ -424,15 +354,14 @@ js_FinishAtomState(JSRuntime *rt)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_DHashTableEnumerate(&state->stringAtoms, js_string_uninterner, rt);
|
for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) {
|
||||||
JS_DHashTableFinish(&state->stringAtoms);
|
JSString *str = AtomEntryToKey(r.front());
|
||||||
|
js_FinalizeStringRT(rt, str);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
js_FinishLock(&state->lock);
|
js_FinishLock(&state->lock);
|
||||||
#endif
|
#endif
|
||||||
#ifdef DEBUG
|
|
||||||
memset(state, JS_FREE_PATTERN, sizeof *state);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSBool
|
JSBool
|
||||||
|
@ -456,91 +385,50 @@ js_InitCommonAtoms(JSContext *cx)
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSDHashOperator
|
|
||||||
js_atom_unpinner(JSDHashTable *table, JSDHashEntryHdr *hdr,
|
|
||||||
uint32 number, void *arg)
|
|
||||||
{
|
|
||||||
CLEAR_ATOM_ENTRY_FLAGS(TO_ATOM_ENTRY(hdr), ATOM_PINNED);
|
|
||||||
return JS_DHASH_NEXT;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
js_FinishCommonAtoms(JSContext *cx)
|
js_FinishCommonAtoms(JSContext *cx)
|
||||||
{
|
{
|
||||||
cx->runtime->emptyString = NULL;
|
cx->runtime->emptyString = NULL;
|
||||||
JSAtomState *state = &cx->runtime->atomState;
|
JSAtomState *state = &cx->runtime->atomState;
|
||||||
JS_DHashTableEnumerate(&state->stringAtoms, js_atom_unpinner, NULL);
|
|
||||||
|
for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront())
|
||||||
|
ClearAtomEntryFlags(r.front(), ATOM_PINNED);
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
memset(COMMON_ATOMS_START(state), JS_FREE_PATTERN,
|
memset(COMMON_ATOMS_START(state), JS_FREE_PATTERN,
|
||||||
ATOM_OFFSET_LIMIT - ATOM_OFFSET_START);
|
ATOM_OFFSET_LIMIT - ATOM_OFFSET_START);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSDHashOperator
|
|
||||||
js_locked_atom_tracer(JSDHashTable *table, JSDHashEntryHdr *hdr,
|
|
||||||
uint32 number, void *arg)
|
|
||||||
{
|
|
||||||
JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
|
|
||||||
JSTracer *trc = (JSTracer *)arg;
|
|
||||||
|
|
||||||
if (entry->keyAndFlags == 0) {
|
|
||||||
/* Ignore uninitialized entries during tracing. */
|
|
||||||
return JS_DHASH_NEXT;
|
|
||||||
}
|
|
||||||
JS_SET_TRACING_INDEX(trc, "locked_atom", (size_t)number);
|
|
||||||
Mark(trc, ATOM_ENTRY_KEY(entry), JSTRACE_STRING);
|
|
||||||
return JS_DHASH_NEXT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static JSDHashOperator
|
|
||||||
js_pinned_atom_tracer(JSDHashTable *table, JSDHashEntryHdr *hdr,
|
|
||||||
uint32 number, void *arg)
|
|
||||||
{
|
|
||||||
JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
|
|
||||||
JSTracer *trc = (JSTracer *)arg;
|
|
||||||
uintN flags = ATOM_ENTRY_FLAGS(entry);
|
|
||||||
|
|
||||||
if (flags & (ATOM_PINNED | ATOM_INTERNED)) {
|
|
||||||
JS_SET_TRACING_INDEX(trc,
|
|
||||||
flags & ATOM_PINNED
|
|
||||||
? "pinned_atom"
|
|
||||||
: "interned_atom",
|
|
||||||
(size_t)number);
|
|
||||||
Mark(trc, ATOM_ENTRY_KEY(entry), JSTRACE_STRING);
|
|
||||||
}
|
|
||||||
return JS_DHASH_NEXT;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
js_TraceAtomState(JSTracer *trc)
|
js_TraceAtomState(JSTracer *trc)
|
||||||
{
|
{
|
||||||
JSRuntime *rt = trc->context->runtime;
|
JSRuntime *rt = trc->context->runtime;
|
||||||
JSAtomState *state = &rt->atomState;
|
JSAtomState *state = &rt->atomState;
|
||||||
|
|
||||||
if (rt->gcKeepAtoms)
|
#ifdef DEBUG
|
||||||
JS_DHashTableEnumerate(&state->stringAtoms, js_locked_atom_tracer, trc);
|
size_t number = 0;
|
||||||
else
|
#endif
|
||||||
JS_DHashTableEnumerate(&state->stringAtoms, js_pinned_atom_tracer, trc);
|
|
||||||
}
|
|
||||||
|
|
||||||
static JSDHashOperator
|
if (rt->gcKeepAtoms) {
|
||||||
js_atom_sweeper(JSDHashTable *table, JSDHashEntryHdr *hdr,
|
for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) {
|
||||||
uint32 number, void *arg)
|
JS_SET_TRACING_INDEX(trc, "locked_atom", number++);
|
||||||
{
|
MarkString(trc, AtomEntryToKey(r.front()));
|
||||||
JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
|
}
|
||||||
|
} else {
|
||||||
/* Remove uninitialized entries. */
|
for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) {
|
||||||
if (entry->keyAndFlags == 0)
|
AtomEntryType entry = r.front();
|
||||||
return JS_DHASH_REMOVE;
|
uintN flags = AtomEntryFlags(entry);
|
||||||
|
if (flags & (ATOM_PINNED | ATOM_INTERNED)) {
|
||||||
if (ATOM_ENTRY_FLAGS(entry) & (ATOM_PINNED | ATOM_INTERNED)) {
|
JS_SET_TRACING_INDEX(trc,
|
||||||
/* Pinned or interned key cannot be finalized. */
|
flags & ATOM_PINNED
|
||||||
JS_ASSERT(!js_IsAboutToBeFinalized(ATOM_ENTRY_KEY(entry)));
|
? "pinned_atom"
|
||||||
} else if (js_IsAboutToBeFinalized(ATOM_ENTRY_KEY(entry))) {
|
: "interned_atom",
|
||||||
/* Remove entries with things about to be GC'ed. */
|
number++);
|
||||||
return JS_DHASH_REMOVE;
|
MarkString(trc, AtomEntryToKey(entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return JS_DHASH_NEXT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -548,25 +436,20 @@ js_SweepAtomState(JSContext *cx)
|
||||||
{
|
{
|
||||||
JSAtomState *state = &cx->runtime->atomState;
|
JSAtomState *state = &cx->runtime->atomState;
|
||||||
|
|
||||||
JS_DHashTableEnumerate(&state->stringAtoms, js_atom_sweeper, NULL);
|
for (AtomSet::Enum e(state->atoms); !e.empty(); e.popFront()) {
|
||||||
|
AtomEntryType entry = e.front();
|
||||||
/*
|
if (AtomEntryFlags(entry) & (ATOM_PINNED | ATOM_INTERNED)) {
|
||||||
* Optimize for simplicity and mutate table generation numbers even if the
|
/* Pinned or interned key cannot be finalized. */
|
||||||
* sweeper has not removed any entries.
|
JS_ASSERT(!js_IsAboutToBeFinalized(AtomEntryToKey(entry)));
|
||||||
*/
|
} else if (js_IsAboutToBeFinalized(AtomEntryToKey(entry))) {
|
||||||
state->stringAtoms.generation++;
|
e.removeFront();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JSAtom *
|
JSAtom *
|
||||||
js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
|
js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
|
||||||
{
|
{
|
||||||
JSAtom *atom;
|
|
||||||
JSAtomState *state;
|
|
||||||
JSDHashTable *table;
|
|
||||||
JSAtomHashEntry *entry;
|
|
||||||
JSString *key;
|
|
||||||
uint32 gen;
|
|
||||||
|
|
||||||
JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY)));
|
JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY)));
|
||||||
JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR);
|
JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR);
|
||||||
|
|
||||||
|
@ -610,30 +493,30 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state = &cx->runtime->atomState;
|
JSAtomState *state = &cx->runtime->atomState;
|
||||||
table = &state->stringAtoms;
|
AtomSet &atoms = state->atoms;
|
||||||
|
|
||||||
JS_LOCK(cx, &state->lock);
|
JS_LOCK(cx, &state->lock);
|
||||||
entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, str, JS_DHASH_ADD));
|
AtomSet::AddPtr p = atoms.lookupForAdd(str);
|
||||||
if (!entry)
|
|
||||||
goto failed_hash_add;
|
|
||||||
/* Hashing the string should have flattened it if it was a rope. */
|
/* Hashing the string should have flattened it if it was a rope. */
|
||||||
JS_ASSERT(str->isFlat() || str->isDependent());
|
JS_ASSERT(str->isFlat() || str->isDependent());
|
||||||
if (entry->keyAndFlags != 0) {
|
|
||||||
key = ATOM_ENTRY_KEY(entry);
|
JSString *key;
|
||||||
|
if (p) {
|
||||||
|
key = AtomEntryToKey(*p);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* We created a new hashtable entry. Unless str is already allocated
|
* Unless str is already allocated from the GC heap and flat, we have
|
||||||
* from the GC heap and flat, we have to release state->lock as
|
* to release state->lock as string construction is a complex
|
||||||
* string construction is a complex operation. For example, it can
|
* operation. For example, it can trigger GC which may rehash the table
|
||||||
* trigger GC which may rehash the table and make the entry invalid.
|
* and make the entry invalid.
|
||||||
*/
|
*/
|
||||||
++table->generation;
|
|
||||||
if (!(flags & ATOM_TMPSTR) && str->isFlat()) {
|
if (!(flags & ATOM_TMPSTR) && str->isFlat()) {
|
||||||
str->flatClearMutable();
|
str->flatClearMutable();
|
||||||
key = str;
|
key = str;
|
||||||
|
atoms.add(p, StringToInitialAtomEntry(key));
|
||||||
} else {
|
} else {
|
||||||
gen = table->generation;
|
|
||||||
JS_UNLOCK(cx, &state->lock);
|
JS_UNLOCK(cx, &state->lock);
|
||||||
|
|
||||||
if (flags & ATOM_TMPSTR) {
|
if (flags & ATOM_TMPSTR) {
|
||||||
|
@ -657,36 +540,22 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_LOCK(cx, &state->lock);
|
JS_LOCK(cx, &state->lock);
|
||||||
if (table->generation == gen) {
|
if (!atoms.relookupOrAdd(p, key, StringToInitialAtomEntry(key))) {
|
||||||
JS_ASSERT(entry->keyAndFlags == 0);
|
JS_UNLOCK(cx, &state->lock);
|
||||||
} else {
|
JS_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report */
|
||||||
entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key,
|
return NULL;
|
||||||
JS_DHASH_ADD));
|
|
||||||
if (!entry)
|
|
||||||
goto failed_hash_add;
|
|
||||||
if (entry->keyAndFlags != 0) {
|
|
||||||
key = ATOM_ENTRY_KEY(entry);
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
++table->generation;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
INIT_ATOM_ENTRY(entry, key);
|
|
||||||
key->flatSetAtomized();
|
key->flatSetAtomized();
|
||||||
}
|
}
|
||||||
|
|
||||||
finish:
|
AddAtomEntryFlags(*p, flags & (ATOM_PINNED | ATOM_INTERNED));
|
||||||
ADD_ATOM_ENTRY_FLAGS(entry, flags & (ATOM_PINNED | ATOM_INTERNED));
|
|
||||||
JS_ASSERT(key->isAtomized());
|
JS_ASSERT(key->isAtomized());
|
||||||
atom = STRING_TO_ATOM(key);
|
JSAtom *atom = STRING_TO_ATOM(key);
|
||||||
cx->weakRoots.lastAtom = atom;
|
cx->weakRoots.lastAtom = atom;
|
||||||
JS_UNLOCK(cx, &state->lock);
|
JS_UNLOCK(cx, &state->lock);
|
||||||
return atom;
|
return atom;
|
||||||
|
|
||||||
failed_hash_add:
|
|
||||||
JS_UNLOCK(cx, &state->lock);
|
|
||||||
JS_ReportOutOfMemory(cx);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSAtom *
|
JSAtom *
|
||||||
|
@ -743,7 +612,6 @@ js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length)
|
||||||
{
|
{
|
||||||
JSString str, *str2;
|
JSString str, *str2;
|
||||||
JSAtomState *state;
|
JSAtomState *state;
|
||||||
JSDHashEntryHdr *hdr;
|
|
||||||
|
|
||||||
if (length == 1) {
|
if (length == 1) {
|
||||||
jschar c = *chars;
|
jschar c = *chars;
|
||||||
|
@ -755,63 +623,41 @@ js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length)
|
||||||
state = &cx->runtime->atomState;
|
state = &cx->runtime->atomState;
|
||||||
|
|
||||||
JS_LOCK(cx, &state->lock);
|
JS_LOCK(cx, &state->lock);
|
||||||
hdr = JS_DHashTableOperate(&state->stringAtoms, &str, JS_DHASH_LOOKUP);
|
AtomSet::Ptr p = state->atoms.lookup(&str);
|
||||||
str2 = JS_DHASH_ENTRY_IS_BUSY(hdr)
|
str2 = p ? AtomEntryToKey(*p) : NULL;
|
||||||
? ATOM_ENTRY_KEY(TO_ATOM_ENTRY(hdr))
|
|
||||||
: NULL;
|
|
||||||
JS_UNLOCK(cx, &state->lock);
|
JS_UNLOCK(cx, &state->lock);
|
||||||
|
|
||||||
return str2 ? STRING_TO_ATOM(str2) : NULL;
|
return str2 ? STRING_TO_ATOM(str2) : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
||||||
static JSDHashOperator
|
|
||||||
atom_dumper(JSDHashTable *table, JSDHashEntryHdr *hdr,
|
|
||||||
uint32 number, void *arg)
|
|
||||||
{
|
|
||||||
JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
|
|
||||||
FILE *fp = (FILE *)arg;
|
|
||||||
JSString *key;
|
|
||||||
uintN flags;
|
|
||||||
|
|
||||||
fprintf(fp, "%3u %08x ", number, (uintN)entry->hdr.keyHash);
|
|
||||||
if (entry->keyAndFlags == 0) {
|
|
||||||
fputs("<uninitialized>", fp);
|
|
||||||
} else {
|
|
||||||
key = ATOM_ENTRY_KEY(entry);
|
|
||||||
js_FileEscapedString(fp, key, '"');
|
|
||||||
flags = ATOM_ENTRY_FLAGS(entry);
|
|
||||||
if (flags != 0) {
|
|
||||||
fputs((flags & (ATOM_PINNED | ATOM_INTERNED))
|
|
||||||
? " pinned | interned"
|
|
||||||
: (flags & ATOM_PINNED) ? " pinned" : " interned",
|
|
||||||
fp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
putc('\n', fp);
|
|
||||||
return JS_DHASH_NEXT;
|
|
||||||
}
|
|
||||||
|
|
||||||
JS_FRIEND_API(void)
|
JS_FRIEND_API(void)
|
||||||
js_DumpAtoms(JSContext *cx, FILE *fp)
|
js_DumpAtoms(JSContext *cx, FILE *fp)
|
||||||
{
|
{
|
||||||
JSAtomState *state = &cx->runtime->atomState;
|
JSAtomState *state = &cx->runtime->atomState;
|
||||||
|
|
||||||
fprintf(fp, "stringAtoms table contents:\n");
|
fprintf(fp, "atoms table contents:\n");
|
||||||
JS_DHashTableEnumerate(&state->stringAtoms, atom_dumper, fp);
|
size_t number;
|
||||||
#ifdef JS_DHASHMETER
|
for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) {
|
||||||
JS_DHashTableDumpMeter(&state->stringAtoms, atom_dumper, fp);
|
AtomEntryType entry = r.front();
|
||||||
#endif
|
fprintf(fp, "%3u ", number++);
|
||||||
putc('\n', fp);
|
if (entry == 0) {
|
||||||
|
fputs("<uninitialized>", fp);
|
||||||
fprintf(fp, "doubleAtoms table contents:\n");
|
} else {
|
||||||
#ifdef JS_DHASHMETER
|
JSString *key = AtomEntryToKey(entry);
|
||||||
JS_DHashTableDumpMeter(&state->doubleAtoms, atom_dumper, fp);
|
js_FileEscapedString(fp, key, '"');
|
||||||
#endif
|
uintN flags = AtomEntryFlags(entry);
|
||||||
|
if (flags != 0) {
|
||||||
|
fputs((flags & (ATOM_PINNED | ATOM_INTERNED))
|
||||||
|
? " pinned | interned"
|
||||||
|
: (flags & ATOM_PINNED) ? " pinned" : " interned",
|
||||||
|
fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
putc('\n', fp);
|
||||||
|
}
|
||||||
putc('\n', fp);
|
putc('\n', fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static JSHashNumber
|
static JSHashNumber
|
||||||
|
|
|
@ -44,12 +44,12 @@
|
||||||
*/
|
*/
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include "jsversion.h"
|
#include "jsversion.h"
|
||||||
#include "jstypes.h"
|
|
||||||
#include "jshash.h" /* Added by JSIFY */
|
|
||||||
#include "jsdhash.h"
|
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
#include "jsprvtd.h"
|
#include "jsprvtd.h"
|
||||||
|
#include "jshash.h"
|
||||||
|
#include "jshashtable.h"
|
||||||
#include "jspubtd.h"
|
#include "jspubtd.h"
|
||||||
|
#include "jsstr.h"
|
||||||
#include "jslock.h"
|
#include "jslock.h"
|
||||||
#include "jsvalue.h"
|
#include "jsvalue.h"
|
||||||
|
|
||||||
|
@ -258,8 +258,42 @@ struct JSAtomMap {
|
||||||
jsatomid length; /* count of (to-be-)indexed atoms */
|
jsatomid length; /* count of (to-be-)indexed atoms */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct JSAtomState {
|
namespace js {
|
||||||
JSDHashTable stringAtoms; /* hash table with shared strings */
|
|
||||||
|
#define ATOM_ENTRY_FLAG_MASK ((size_t)(ATOM_PINNED | ATOM_INTERNED))
|
||||||
|
|
||||||
|
JS_STATIC_ASSERT(ATOM_ENTRY_FLAG_MASK < JS_GCTHING_ALIGN);
|
||||||
|
|
||||||
|
typedef uintptr_t AtomEntryType;
|
||||||
|
|
||||||
|
static JS_ALWAYS_INLINE JSString *
|
||||||
|
AtomEntryToKey(AtomEntryType entry)
|
||||||
|
{
|
||||||
|
JS_ASSERT(entry != 0);
|
||||||
|
return (JSString *)(entry & ~ATOM_ENTRY_FLAG_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AtomHasher
|
||||||
|
{
|
||||||
|
typedef JSString *Lookup;
|
||||||
|
|
||||||
|
static HashNumber hash(JSString *str) {
|
||||||
|
return js_HashString(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool match(AtomEntryType entry, JSString *lookup) {
|
||||||
|
return entry ? js_EqualStrings(AtomEntryToKey(entry), lookup) : false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef HashSet<AtomEntryType, AtomHasher, SystemAllocPolicy> AtomSet;
|
||||||
|
|
||||||
|
} /* namespace js */
|
||||||
|
|
||||||
|
struct JSAtomState
|
||||||
|
{
|
||||||
|
js::AtomSet atoms;
|
||||||
|
|
||||||
#ifdef JS_THREADSAFE
|
#ifdef JS_THREADSAFE
|
||||||
JSThinLock lock;
|
JSThinLock lock;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -526,6 +526,13 @@ Mark(JSTracer *trc, void *thing, uint32 kind, const char *name)
|
||||||
Mark(trc, thing, kind);
|
Mark(trc, thing, kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
MarkString(JSTracer *trc, JSString *str)
|
||||||
|
{
|
||||||
|
JS_ASSERT(str);
|
||||||
|
Mark(trc, str, JSTRACE_STRING);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
MarkString(JSTracer *trc, JSString *str, const char *name)
|
MarkString(JSTracer *trc, JSString *str, const char *name)
|
||||||
{
|
{
|
||||||
|
|
|
@ -847,8 +847,8 @@ class HashMap
|
||||||
* N.B. The caller must ensure that no mutating hash table operations
|
* N.B. The caller must ensure that no mutating hash table operations
|
||||||
* occur between a pair of |lookupForAdd| and |add| calls. To avoid
|
* occur between a pair of |lookupForAdd| and |add| calls. To avoid
|
||||||
* looking up the key a second time, the caller may use the more efficient
|
* looking up the key a second time, the caller may use the more efficient
|
||||||
* relookupOrAdd method. That method relookups the map if necessary and
|
* relookupOrAdd method. This method reuses part of the hashing computation
|
||||||
* inserts the new value only if the key still does not exist. For
|
* to more efficiently insert the key if it has not been added. For
|
||||||
* example, a mutation-handling version of the previous example:
|
* example, a mutation-handling version of the previous example:
|
||||||
*
|
*
|
||||||
* HM::AddPtr p = h.lookupForAdd(3);
|
* HM::AddPtr p = h.lookupForAdd(3);
|
||||||
|
@ -1026,13 +1026,37 @@ class HashSet
|
||||||
* assert(*p == 3); // p acts like a pointer to int
|
* assert(*p == 3); // p acts like a pointer to int
|
||||||
*
|
*
|
||||||
* Also see the definition of AddPtr in HashTable above.
|
* Also see the definition of AddPtr in HashTable above.
|
||||||
|
*
|
||||||
|
* N.B. The caller must ensure that no mutating hash table operations
|
||||||
|
* occur between a pair of |lookupForAdd| and |add| calls. To avoid
|
||||||
|
* looking up the key a second time, the caller may use the more efficient
|
||||||
|
* relookupOrAdd method. This method reuses part of the hashing computation
|
||||||
|
* to more efficiently insert the key if it has not been added. For
|
||||||
|
* example, a mutation-handling version of the previous example:
|
||||||
|
*
|
||||||
|
* HS::AddPtr p = h.lookupForAdd(3);
|
||||||
|
* if (!p) {
|
||||||
|
* call_that_may_mutate_h();
|
||||||
|
* if (!h.relookupOrAdd(p, 3, 3))
|
||||||
|
* return false;
|
||||||
|
* }
|
||||||
|
* assert(*p == 3);
|
||||||
|
*
|
||||||
|
* Note that relookupOrAdd(p,l,t) performs Lookup using l and adds the
|
||||||
|
* entry t, where the caller ensures match(l,t).
|
||||||
*/
|
*/
|
||||||
typedef typename Impl::AddPtr AddPtr;
|
typedef typename Impl::AddPtr AddPtr;
|
||||||
AddPtr lookupForAdd(const Lookup &l) const {
|
AddPtr lookupForAdd(const Lookup &l) const {
|
||||||
return impl.lookupForAdd(l);
|
return impl.lookupForAdd(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool add(AddPtr &p, const T &t) { return impl.add(p, t); }
|
bool add(AddPtr &p, const T &t) {
|
||||||
|
return impl.add(p, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool relookupOrAdd(AddPtr &p, const Lookup &l, const T &t) {
|
||||||
|
return impl.relookupOrAdd(p, l, t);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* |all()| returns a Range containing |count()| elements:
|
* |all()| returns a Range containing |count()| elements:
|
||||||
|
|
Загрузка…
Ссылка в новой задаче