diff --git a/js/src/js.c b/js/src/js.c index 3a4851f093ca..1de9feec8ec9 100644 --- a/js/src/js.c +++ b/js/src/js.c @@ -966,9 +966,9 @@ SrcNotes(JSContext *cx, JSScript *script) jssrcnote *notes, *sn; JSSrcNoteType type; const char *name; - jsatomid atomIndex; uint32 index; JSAtom *atom; + JSString *str; fprintf(gOutFile, "\nSource notes:\n"); offset = 0; @@ -1015,20 +1015,19 @@ SrcNotes(JSContext *cx, JSScript *script) case SRC_LABEL: case SRC_LABELBRACE: case SRC_BREAK2LABEL: - case SRC_CONT2LABEL: { - const char *bytes; - - atomIndex = (jsatomid) js_GetSrcNoteOffset(sn, 0); - JS_GET_SCRIPT_ATOM(script, atomIndex, atom); - bytes = js_AtomToPrintableString(cx, atom); - fprintf(gOutFile, " atom %u (%s)", (uintN)atomIndex, bytes); + case SRC_CONT2LABEL: + index = js_GetSrcNoteOffset(sn, 0); + JS_GET_SCRIPT_ATOM(script, index, atom); + JS_ASSERT(ATOM_IS_STRING(atom)); + str = ATOM_TO_STRING(atom); + fprintf(gOutFile, " atom %u (", index); + js_FileEscapedString(gOutFile, str, 0); + putc(')', gOutFile); break; - } case SRC_FUNCDEF: { const char *bytes; JSObject *obj; JSFunction *fun; - JSString *str; index = js_GetSrcNoteOffset(sn, 0); JS_GET_SCRIPT_OBJECT(script, index, obj); @@ -1273,23 +1272,35 @@ DumpScope(JSContext *cx, JSObject *obj, FILE *fp) uintN i; JSScope *scope; JSScopeProperty *sprop; + jsval v; + JSString *str; i = 0; scope = OBJ_SCOPE(obj); for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) continue; - fprintf(fp, "%3u %p", i, (void *)sprop); - if (JSID_IS_INT(sprop->id)) { - fprintf(fp, " [%ld]", (long)JSVAL_TO_INT(sprop->id)); - } else if (JSID_IS_ATOM(sprop->id)) { - JSAtom *atom = JSID_TO_ATOM(sprop->id); - fprintf(fp, " \"%s\"", js_AtomToPrintableString(cx, atom)); - } else { - jsval v = OBJECT_TO_JSVAL(JSID_TO_OBJECT(sprop->id)); - fprintf(fp, " \"%s\"", js_ValueToPrintableString(cx, v)); - } + fprintf(fp, "%3u %p ", i, (void *)sprop); + v = ID_TO_VALUE(sprop->id); + if (JSID_IS_INT(sprop->id)) { + fprintf(fp, "[%ld]", (long)JSVAL_TO_INT(v)); + } else { + if (JSID_IS_ATOM(sprop->id)) { + str = JSVAL_TO_STRING(v); + } else if (JSID_IS_HIDDEN(sprop->id)) { + str = JSVAL_TO_STRING(v); + fputs("hidden ", fp); + } else { + JS_ASSERT(JSID_IS_OBJECT(sprop->id)); + str = js_ValueToString(cx, v); + fputs("object ", fp); + } + if (!str) + fputs("", fp); + else + js_FileEscapedString(fp, str, '"'); + } #define DUMP_ATTR(name) if (sprop->attrs & JSPROP_##name) fputs(" " #name, fp) DUMP_ATTR(ENUMERATE); DUMP_ATTR(READONLY); diff --git a/js/src/jsapi.c b/js/src/jsapi.c index ba9a79e04182..ce4913d1ec59 100644 --- a/js/src/jsapi.c +++ b/js/src/jsapi.c @@ -1305,7 +1305,7 @@ StdNameToAtom(JSContext *cx, JSStdName *stdn) if (!atom) { name = stdn->name; if (name) { - atom = js_Atomize(cx, name, strlen(name), ATOM_PINNED); + atom = js_Atomize(cx, name, strlen(name), 0); OFFSET_TO_ATOM(cx->runtime, offset) = atom; } } @@ -1964,10 +1964,6 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, name = "function"; break; - case JSTRACE_ATOM: - name = "atom"; - break; - #if JS_HAS_XML_SUPPORT case JSTRACE_NAMESPACE: name = "namespace"; @@ -2028,21 +2024,8 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, break; } - case JSTRACE_ATOM: - { - JSAtom *atom = (JSAtom *)thing; - - if (ATOM_IS_INT(atom)) - JS_snprintf(buf, bufsize, "%d", ATOM_TO_INT(atom)); - else if (ATOM_IS_STRING(atom)) - js_PutEscapedString(buf, bufsize, ATOM_TO_STRING(atom), 0); - else - JS_snprintf(buf, bufsize, "object"); - break; - } - #if JS_HAS_XML_SUPPORT - case GCX_NAMESPACE: + case JSTRACE_NAMESPACE: { JSXMLNamespace *ns = (JSXMLNamespace *)thing; @@ -2059,7 +2042,7 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, break; } - case GCX_QNAME: + case JSTRACE_QNAME: { JSXMLQName *qn = (JSXMLQName *)thing; @@ -2084,7 +2067,7 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, break; } - case GCX_XML: + case JSTRACE_XML: { extern const char *js_xml_class_str[]; JSXML *xml = (JSXML *)thing; @@ -2897,7 +2880,7 @@ JS_GetConstructor(JSContext *cx, JSObject *proto) JS_PUBLIC_API(JSBool) JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp) { - JS_ASSERT(((jsid)obj & JSID_TAGMASK) == 0); + JS_ASSERT(JSID_IS_OBJECT(obj)); *idp = OBJECT_TO_JSID(obj); return JS_TRUE; } diff --git a/js/src/jsatom.c b/js/src/jsatom.c index 8b63bc5d5f34..c48e47c5ccf6 100644 --- a/js/src/jsatom.c +++ b/js/src/jsatom.c @@ -212,98 +212,138 @@ const char js_ExecutionContext_str[] = "ExecutionContext"; const char js_current_str[] = "current"; #endif -#define HASH_DOUBLE(dp) ((JSDOUBLE_HI32(*dp) ^ JSDOUBLE_LO32(*dp))) +/* + * JSAtomState.doubleAtoms and 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; -JS_STATIC_DLL_CALLBACK(JSHashNumber) -js_hash_atom_key(const void *key) +#define ATOM_ENTRY_FLAG_MASK (ATOM_PINNED | ATOM_INTERNED) + +JS_STATIC_ASSERT(ATOM_ENTRY_FLAG_MASK < JSVAL_ALIGN); + +/* + * Helper macros to access and modify JSAtomHashEntry. + */ +#define TO_ATOM_ENTRY(hdr) ((JSAtomHashEntry *) hdr) +#define ATOM_ENTRY_KEY(entry) \ + ((void *)((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 const JSDHashTableOps DoubleHashOps; +static const JSDHashTableOps StringHashOps; + +#define IS_DOUBLE_TABLE(table) ((table)->ops == &DoubleHashOps) +#define IS_STRING_TABLE(table) ((table)->ops == &StringHashOps) + +#define IS_INITIALIZED_STATE(state) IS_DOUBLE_TABLE(&(state)->doubleAtoms) + +JS_STATIC_DLL_CALLBACK(JSDHashNumber) +HashDouble(JSDHashTable *table, const void *key) { - jsval v; - jsdouble *dp; + jsdouble d; - v = (jsval)key; - if (JSVAL_IS_STRING(v)) - return js_HashString(JSVAL_TO_STRING(v)); - if (JSVAL_IS_DOUBLE(v)) { - dp = JSVAL_TO_DOUBLE(v); - return HASH_DOUBLE(dp); - } - JS_ASSERT(JSVAL_IS_INT(v) || v == JSVAL_TRUE || v == JSVAL_FALSE || - v == JSVAL_NULL || v == JSVAL_VOID); - return (JSHashNumber)v; + JS_ASSERT(IS_DOUBLE_TABLE(table)); + d = *(jsdouble *)key; + return JSDOUBLE_HI32(d) ^ JSDOUBLE_LO32(d); } -JS_STATIC_DLL_CALLBACK(intN) -js_compare_atom_keys(const void *k1, const void *k2) +JS_STATIC_DLL_CALLBACK(JSDHashNumber) +HashString(JSDHashTable *table, const void *key) { - jsval v1, v2; + JS_ASSERT(IS_STRING_TABLE(table)); + return js_HashString((JSString *)key); +} - v1 = (jsval)k1, v2 = (jsval)k2; - if (JSVAL_IS_STRING(v1) && JSVAL_IS_STRING(v2)) - return js_EqualStrings(JSVAL_TO_STRING(v1), JSVAL_TO_STRING(v2)); - if (JSVAL_IS_DOUBLE(v1) && JSVAL_IS_DOUBLE(v2)) { - double d1 = *JSVAL_TO_DOUBLE(v1); - double d2 = *JSVAL_TO_DOUBLE(v2); - if (JSDOUBLE_IS_NaN(d1)) - return JSDOUBLE_IS_NaN(d2); +JS_STATIC_DLL_CALLBACK(JSBool) +MatchDouble(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key) +{ + JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); + jsdouble d1, d2; + + JS_ASSERT(IS_DOUBLE_TABLE(table)); + if (entry->keyAndFlags == 0) { + /* See comments in MatchString. */ + return JS_FALSE; + } + + d1 = *(jsdouble *)ATOM_ENTRY_KEY(entry); + d2 = *(jsdouble *)key; + if (JSDOUBLE_IS_NaN(d1)) + return JSDOUBLE_IS_NaN(d2); #if defined(XP_WIN) - /* XXX MSVC miscompiles such that (NaN == 0) */ - if (JSDOUBLE_IS_NaN(d2)) - return JS_FALSE; + /* XXX MSVC miscompiles such that (NaN == 0) */ + if (JSDOUBLE_IS_NaN(d2)) + return JS_FALSE; #endif - return d1 == d2; + return d1 == d2; +} + +JS_STATIC_DLL_CALLBACK(JSBool) +MatchString(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key) +{ + JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); + + JS_ASSERT(IS_STRING_TABLE(table)); + if (entry->keyAndFlags == 0) { + /* + * This happens when js_AtomizeString adds a new hash entry and + * releases the lock but before it takes the lock the second time to + * initialize keyAndFlags for the entry. + * + * We always return false for such entries so JS_DHashTableOperate + * never finds them. We clean them during GC's sweep phase. + * + * It means that with a contested lock or when GC is triggered outside + * the lock we may end up adding two entries, but this is a price for + * simpler code. + */ + return JS_FALSE; } - return v1 == v2; + return js_EqualStrings((JSString *)ATOM_ENTRY_KEY(entry), (JSString *)key); } -JS_STATIC_DLL_CALLBACK(int) -js_compare_stub(const void *v1, const void *v2) -{ - return 1; -} - -/* These next two are exported to jsscript.c and used similarly there. */ -void * JS_DLL_CALLBACK -js_alloc_table_space(void *priv, size_t size) -{ - return malloc(size); -} - -void JS_DLL_CALLBACK -js_free_table_space(void *priv, void *item) -{ - free(item); -} - -JS_STATIC_DLL_CALLBACK(JSHashEntry *) -js_alloc_atom(void *priv, const void *key) -{ - JSAtom *atom; - - atom = (JSAtom *) malloc(sizeof(JSAtom)); - if (!atom) - return NULL; - ((JSAtomState *)priv)->tablegen++; - atom->entry.key = key; - atom->entry.value = NULL; - atom->flags = 0; - return &atom->entry; -} - -JS_STATIC_DLL_CALLBACK(void) -js_free_atom(void *priv, JSHashEntry *he, uintN flag) -{ - if (flag != HT_FREE_ENTRY) - return; - ((JSAtomState *)priv)->tablegen++; - free(he); -} - -static JSHashAllocOps atom_alloc_ops = { - js_alloc_table_space, js_free_table_space, - js_alloc_atom, js_free_atom +static const JSDHashTableOps DoubleHashOps = { + JS_DHashAllocTable, + JS_DHashFreeTable, + HashDouble, + MatchDouble, + JS_DHashMoveEntryStub, + JS_DHashClearEntryStub, + JS_DHashFinalizeStub, + NULL }; -#define JS_ATOM_HASH_SIZE 1024 +static const JSDHashTableOps StringHashOps = { + JS_DHashAllocTable, + JS_DHashFreeTable, + HashString, + MatchString, + JS_DHashMoveEntryStub, + JS_DHashClearEntryStub, + JS_DHashFinalizeStub, + NULL +}; + +/* + * For a browser build from 2007-08-09 after the browser starts up there are + * just 55 double atoms, but over 15000 string atoms. Not to penalize more + * economical embeddings allocating too much memory initially we initialize + * atomized strings with just 1K entries. + */ +#define JS_STRING_HASH_COUNT 1024 +#define JS_DOUBLE_HASH_COUNT 64 JSBool js_InitAtomState(JSRuntime *rt) @@ -313,32 +353,50 @@ js_InitAtomState(JSRuntime *rt) /* * The caller must zero the state before calling this function. */ - JS_ASSERT(!state->table); + JS_ASSERT(!state->stringAtoms.ops); + JS_ASSERT(!state->doubleAtoms.ops); JS_ASSERT(state->tablegen == 0); - state->table = JS_NewHashTable(JS_ATOM_HASH_SIZE, js_hash_atom_key, - js_compare_atom_keys, js_compare_stub, - &atom_alloc_ops, state); - if (!state->table) + if (!JS_DHashTableInit(&state->stringAtoms, &StringHashOps, + NULL, sizeof(JSAtomHashEntry), + JS_DHASH_DEFAULT_CAPACITY(JS_STRING_HASH_COUNT))) { + state->stringAtoms.ops = NULL; return JS_FALSE; + } + JS_ASSERT(IS_STRING_TABLE(&state->stringAtoms)); + + if (!JS_DHashTableInit(&state->doubleAtoms, &DoubleHashOps, + NULL, sizeof(JSAtomHashEntry), + JS_DHASH_DEFAULT_CAPACITY(JS_DOUBLE_HASH_COUNT))) { + state->doubleAtoms.ops = NULL; + JS_DHashTableFinish(&state->stringAtoms); + state->stringAtoms.ops = NULL; + return JS_FALSE; + } + JS_ASSERT(IS_DOUBLE_TABLE(&state->doubleAtoms)); #ifdef JS_THREADSAFE js_InitLock(&state->lock); #endif + JS_ASSERT(IS_INITIALIZED_STATE(state)); return JS_TRUE; } -JS_STATIC_DLL_CALLBACK(intN) -js_atom_uninterner(JSHashEntry *he, intN i, void *arg) +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +js_string_uninterner(JSDHashTable *table, JSDHashEntryHdr *hdr, + uint32 number, void *arg) { - JSAtom *atom; - JSRuntime *rt; + JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); + JSRuntime *rt = (JSRuntime *)arg; - atom = (JSAtom *)he; - rt = (JSRuntime *)arg; - if (ATOM_IS_STRING(atom)) - js_FinalizeStringRT(rt, ATOM_TO_STRING(atom)); - return HT_ENUMERATE_NEXT; + /* + * Any string entry that remains at this point must be initialized, as the + * last GC should clean any uninitialized ones. + */ + JS_ASSERT(IS_STRING_TABLE(table)); + JS_ASSERT(entry->keyAndFlags != 0); + js_FinalizeStringRT(rt, (JSString *)ATOM_ENTRY_KEY(entry)); + return JS_DHASH_NEXT; } void @@ -346,16 +404,18 @@ js_FinishAtomState(JSRuntime *rt) { JSAtomState *state = &rt->atomState; - if (!state->table) { + if (!IS_INITIALIZED_STATE(state)) { /* - * state->table is null when JS_NewRuntime fails and calls - * JS_DestroyRuntime on a partially initialized runtime. + * We are called with uninitialized state when JS_NewRuntime fails and + * calls JS_DestroyRuntime on a partially initialized runtime. */ return; } - JS_HashTableEnumerateEntries(state->table, js_atom_uninterner, rt); - JS_HashTableDestroy(state->table); + JS_DHashTableEnumerate(&state->stringAtoms, js_string_uninterner, rt); + JS_DHashTableFinish(&state->stringAtoms); + JS_DHashTableFinish(&state->doubleAtoms); + #ifdef JS_THREADSAFE js_FinishLock(&state->lock); #endif @@ -371,7 +431,7 @@ js_InitCommonAtoms(JSContext *cx) uintN i; JSAtom **atoms; - atoms = (JSAtom **)((uint8 *)state + ATOM_OFFSET_START); + atoms = COMMON_ATOMS_START(state); for (i = 0; i < JS_ARRAY_LENGTH(js_common_atom_names); i++, atoms++) { *atoms = js_Atomize(cx, js_common_atom_names[i], strlen(js_common_atom_names[i]), ATOM_PINNED); @@ -384,14 +444,13 @@ js_InitCommonAtoms(JSContext *cx) return JS_TRUE; } -JS_STATIC_DLL_CALLBACK(intN) -js_atom_unpinner(JSHashEntry *he, intN i, void *arg) +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +js_atom_unpinner(JSDHashTable *table, JSDHashEntryHdr *hdr, + uint32 number, void *arg) { - JSAtom *atom; - - atom = (JSAtom *)he; - atom->flags &= ~ATOM_PINNED; - return HT_ENUMERATE_NEXT; + JS_ASSERT(IS_STRING_TABLE(table)); + CLEAR_ATOM_ENTRY_FLAGS(TO_ATOM_ENTRY(hdr), ATOM_PINNED); + return JS_DHASH_NEXT; } void @@ -399,78 +458,83 @@ js_FinishCommonAtoms(JSContext *cx) { JSAtomState *state = &cx->runtime->atomState; - JS_HashTableEnumerateEntries(state->table, js_atom_unpinner, NULL); + JS_DHashTableEnumerate(&state->stringAtoms, js_atom_unpinner, NULL); #ifdef DEBUG - memset((uint8 *)state + ATOM_OFFSET_START, JS_FREE_PATTERN, + memset(COMMON_ATOMS_START(state), JS_FREE_PATTERN, ATOM_OFFSET_LIMIT - ATOM_OFFSET_START); #endif } -void -js_TraceAtom(JSTracer *trc, JSAtom *atom) +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +js_locked_atom_tracer(JSDHashTable *table, JSDHashEntryHdr *hdr, + uint32 number, void *arg) { - jsval key; + JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); + JSTracer *trc = (JSTracer *)arg; - key = ATOM_KEY(atom); - JS_CALL_VALUE_TRACER(trc, key, "key"); - if (atom->flags & ATOM_HIDDEN) - JS_CALL_TRACER(trc, atom->entry.value, JSTRACE_ATOM, "hidden"); -} - -typedef struct TraceArgs { - JSBool allAtoms; - JSTracer *trc; -} TraceArgs; - -JS_STATIC_DLL_CALLBACK(intN) -js_locked_atom_tracer(JSHashEntry *he, intN i, void *arg) -{ - JSAtom *atom; - TraceArgs *args; - - atom = (JSAtom *)he; - args = (TraceArgs *)arg; - if ((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) || args->allAtoms) { - JS_SET_TRACING_INDEX(args->trc, - (atom->flags & ATOM_PINNED) - ? "pinned_atom" - : (atom->flags & ATOM_INTERNED) - ? "interned_atom" - : "locked_atom", - (size_t)i); - JS_CallTracer(args->trc, atom, JSTRACE_ATOM); + if (entry->keyAndFlags == 0) { + /* Ignore uninitialized entries during tracing. */ + return JS_DHASH_NEXT; } - return HT_ENUMERATE_NEXT; + JS_SET_TRACING_INDEX(trc, "locked_atom", (size_t)number); + JS_CallTracer(trc, ATOM_ENTRY_KEY(entry), + IS_STRING_TABLE(table) ? JSTRACE_STRING : JSTRACE_DOUBLE); + return JS_DHASH_NEXT; +} + +JS_STATIC_DLL_CALLBACK(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); + + JS_ASSERT(IS_STRING_TABLE(table)); + if (flags & (ATOM_PINNED | ATOM_INTERNED)) { + JS_SET_TRACING_INDEX(trc, + flags & ATOM_PINNED + ? "pinned_atom" + : "interned_atom", + (size_t)number); + JS_CallTracer(trc, ATOM_ENTRY_KEY(entry), JSTRACE_STRING); + } + return JS_DHASH_NEXT; } void -js_TraceLockedAtoms(JSTracer *trc, JSBool allAtoms) +js_TraceAtomState(JSTracer *trc, JSBool allAtoms) { JSAtomState *state; - TraceArgs args; state = &trc->context->runtime->atomState; - if (!state->table) - return; - args.allAtoms = allAtoms; - args.trc = trc; - JS_HashTableEnumerateEntries(state->table, js_locked_atom_tracer, &args); + if (allAtoms) { + JS_DHashTableEnumerate(&state->doubleAtoms, js_locked_atom_tracer, trc); + JS_DHashTableEnumerate(&state->stringAtoms, js_locked_atom_tracer, trc); + } else { + JS_DHashTableEnumerate(&state->stringAtoms, js_pinned_atom_tracer, trc); + } } -JS_STATIC_DLL_CALLBACK(intN) -js_atom_sweeper(JSHashEntry *he, intN i, void *arg) +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +js_atom_sweeper(JSDHashTable *table, JSDHashEntryHdr *hdr, + uint32 number, void *arg) { - JSAtom *atom; + JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); + JSContext *cx = (JSContext *)arg; - atom = (JSAtom *)he; - if (atom->flags & ATOM_MARK) { - atom->flags &= ~ATOM_MARK; - return HT_ENUMERATE_NEXT; + /* Remove uninitialized entries. */ + if (entry->keyAndFlags == 0) + return JS_DHASH_REMOVE; + + if (ATOM_ENTRY_FLAGS(entry) & (ATOM_PINNED | ATOM_INTERNED)) { + /* Pinned or interned key cannot be finalized. */ + JS_ASSERT(!js_IsAboutToBeFinalized(cx, ATOM_ENTRY_KEY(entry))); + } else if (js_IsAboutToBeFinalized(cx, ATOM_ENTRY_KEY(entry))) { + /* Remove entries with things about to be GC'ed. */ + return JS_DHASH_REMOVE; } - JS_ASSERT((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) == 0); - atom->entry.key = atom->entry.value = NULL; - atom->flags = 0; - return HT_ENUMERATE_REMOVE; + return JS_DHASH_NEXT; } void @@ -478,170 +542,148 @@ js_SweepAtomState(JSContext *cx) { JSAtomState *state = &cx->runtime->atomState; - JS_HashTableEnumerateEntries(state->table, js_atom_sweeper, NULL); + JS_DHashTableEnumerate(&state->doubleAtoms, js_atom_sweeper, cx); + JS_DHashTableEnumerate(&state->stringAtoms, js_atom_sweeper, cx); + + /* + * Optimize for simplicity and mutate tablegen even if the sweeper has not + * removed any entries. + */ + state->tablegen++; } -static JSAtom * -AtomizeHashedKey(JSContext *cx, jsval key, JSHashNumber keyHash) -{ - JSAtomState *state; - JSHashTable *table; - JSHashEntry *he, **hep; - JSAtom *atom; - - state = &cx->runtime->atomState; - JS_LOCK(&state->lock, cx); - table = state->table; - hep = JS_HashTableRawLookup(table, keyHash, (void *)key); - if ((he = *hep) == NULL) { - he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); - if (!he) { - JS_UNLOCK(&state->lock,cx); - JS_ReportOutOfMemory(cx); - return NULL; - } - } - - atom = (JSAtom *)he; - cx->weakRoots.lastAtom = atom; - JS_UNLOCK(&state->lock,cx); - return atom; -} - -/* Worst-case alignment grain and aligning macro for 2x-sized buffer. */ -#define ALIGNMENT(t) JS_MAX(JSVAL_ALIGN, sizeof(t)) -#define ALIGN(b,t) ((t*) &(b)[ALIGNMENT(t) - (jsuword)(b) % ALIGNMENT(t)]) - JSAtom * js_AtomizeDouble(JSContext *cx, jsdouble d) { - char buf[2 * ALIGNMENT(double)]; - jsdouble *dp; - JSHashNumber keyHash; - jsval key; JSAtomState *state; - JSHashTable *table; - JSHashEntry *he, **hep; + JSDHashTable *table; + JSAtomHashEntry *entry; uint32 gen; - JSAtom *atom; + jsdouble *key; + jsval v; - dp = ALIGN(buf, double); - *dp = d; - keyHash = HASH_DOUBLE(dp); - key = DOUBLE_TO_JSVAL(dp); state = &cx->runtime->atomState; - table = state->table; + table = &state->doubleAtoms; JS_LOCK(&state->lock, cx); - hep = JS_HashTableRawLookup(table, keyHash, (void *)key); - if ((he = *hep) == NULL) { - gen = state->tablegen; + entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, &d, JS_DHASH_ADD)); + if (!entry) + goto failed_hash_add; + if (entry->keyAndFlags == 0) { + gen = ++state->tablegen; JS_UNLOCK(&state->lock, cx); - if (!js_NewDoubleValue(cx, d, &key)) + key = js_NewDouble(cx, d, 0); + if (!key) return NULL; JS_LOCK(&state->lock, cx); - if (state->tablegen != gen) { - hep = JS_HashTableRawLookup(table, keyHash, (void *)key); - if ((he = *hep) != NULL) + if (state->tablegen == 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; + ++state->tablegen; } - he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); - if (!he) { - JS_UNLOCK(&state->lock, cx); - JS_ReportOutOfMemory(cx); - return NULL; - } + INIT_ATOM_ENTRY(entry, key); } finish: - atom = (JSAtom *)he; - cx->weakRoots.lastAtom = atom; + v = DOUBLE_TO_JSVAL((jsdouble *)ATOM_ENTRY_KEY(entry)); + cx->weakRoots.lastAtom = v; JS_UNLOCK(&state->lock,cx); - return atom; -} -/* - * To put an atom into the hidden subspace. XOR its keyHash with this value, - * which is (sqrt(2)-1) in 32-bit fixed point. - */ -#define HIDDEN_ATOM_SUBSPACE_KEYHASH 0x6A09E667 + return (JSAtom *)v; + + failed_hash_add: + JS_UNLOCK(&state->lock,cx); + JS_ReportOutOfMemory(cx); + return NULL; +} JSAtom * js_AtomizeString(JSContext *cx, JSString *str, uintN flags) { - JSHashNumber keyHash; - void *key; JSAtomState *state; - JSHashTable *table; - JSHashEntry *he, **hep; + JSDHashTable *table; + JSAtomHashEntry *entry; + JSString *key; uint32 gen; - JSString *hashed; - JSAtom *atom; + jsval v; - keyHash = js_HashString(str); - if (flags & ATOM_HIDDEN) - keyHash ^= HIDDEN_ATOM_SUBSPACE_KEYHASH; - key = (void *)STRING_TO_JSVAL(str); + JS_ASSERT((flags & + ~(ATOM_PINNED | ATOM_INTERNED | ATOM_TMPSTR | ATOM_NOCOPY)) + == 0); state = &cx->runtime->atomState; - table = state->table; + table = &state->stringAtoms; JS_LOCK(&state->lock, cx); - hep = JS_HashTableRawLookup(table, keyHash, key); - if ((he = *hep) == NULL) { + entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, str, JS_DHASH_ADD)); + if (!entry) + goto failed_hash_add; + if (entry->keyAndFlags == 0) { + ++state->tablegen; gen = state->tablegen; JS_UNLOCK(&state->lock, cx); if (flags & ATOM_TMPSTR) { if (flags & ATOM_NOCOPY) { - hashed = js_NewString(cx, str->chars, str->length, 0); - if (!hashed) + key = js_NewString(cx, str->chars, str->length, 0); + if (!key) return NULL; /* Transfer ownership of str->chars to GC-controlled string. */ str->chars = NULL; } else { - hashed = js_NewStringCopyN(cx, str->chars, str->length); - if (!hashed) + key = js_NewStringCopyN(cx, str->chars, str->length); + if (!key) return NULL; } - key = (void *)STRING_TO_JSVAL(hashed); } else { JS_ASSERT((flags & ATOM_NOCOPY) == 0); if (!JS_MakeStringImmutable(cx, str)) return NULL; + key = str; } JS_LOCK(&state->lock, cx); - if (state->tablegen != gen) { - hep = JS_HashTableRawLookup(table, keyHash, key); - if ((he = *hep) != NULL) + if (state->tablegen == 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; + ++state->tablegen; } - he = JS_HashTableRawAdd(table, hep, keyHash, key, NULL); - if (!he) { - JS_UNLOCK(&state->lock, cx); - JS_ReportOutOfMemory(cx); - return NULL; - } + INIT_ATOM_ENTRY(entry, key); } finish: - atom = (JSAtom *)he; - atom->flags |= flags & (ATOM_PINNED | ATOM_INTERNED | ATOM_HIDDEN); - cx->weakRoots.lastAtom = atom; + ADD_ATOM_ENTRY_FLAGS(entry, flags & (ATOM_PINNED | ATOM_INTERNED)); + v = STRING_TO_JSVAL((JSString *)ATOM_ENTRY_KEY(entry)); + cx->weakRoots.lastAtom = v; JS_UNLOCK(&state->lock, cx); - return atom; + return (JSAtom *)v; + + failed_hash_add: + JS_UNLOCK(&state->lock,cx); + JS_ReportOutOfMemory(cx); + return NULL; } JS_FRIEND_API(JSAtom *) js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags) { jschar *chars; - JSString *str; + JSString str; JSAtom *atom; - char buf[2 * ALIGNMENT(JSString)]; /* * Avoiding the malloc in js_InflateString on shorter strings saves us @@ -666,12 +708,10 @@ js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags) flags |= ATOM_NOCOPY; } - str = ALIGN(buf, JSString); - - str->chars = chars; - str->length = inflatedLength; - atom = js_AtomizeString(cx, str, ATOM_TMPSTR | flags); - if (chars != inflated && str->chars) + str.chars = chars; + str.length = inflatedLength; + atom = js_AtomizeString(cx, &str, ATOM_TMPSTR | flags); + if (chars != inflated && str.chars) JS_free(cx, chars); return atom; } @@ -679,49 +719,54 @@ js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags) JS_FRIEND_API(JSAtom *) js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags) { - JSString *str; - char buf[2 * ALIGNMENT(JSString)]; + JSString str; - str = ALIGN(buf, JSString); - str->chars = (jschar *)chars; - str->length = length; - return js_AtomizeString(cx, str, ATOM_TMPSTR | flags); + str.chars = (jschar *)chars; + str.length = length; + return js_AtomizeString(cx, &str, ATOM_TMPSTR | flags); } JSAtom * js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length) { - JSString *str; - char buf[2 * ALIGNMENT(JSString)]; - JSHashNumber keyHash; - jsval key; + JSString str, *str2; JSAtomState *state; - JSHashTable *table; - JSHashEntry **hep; + JSDHashEntryHdr *hdr; - str = ALIGN(buf, JSString); - str->chars = (jschar *)chars; - str->length = length; - keyHash = js_HashString(str); - key = STRING_TO_JSVAL(str); + str.chars = (jschar *)chars; + str.length = length; state = &cx->runtime->atomState; + JS_LOCK(&state->lock, cx); - table = state->table; - hep = JS_HashTableRawLookup(table, keyHash, (void *)key); + 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(&state->lock, cx); - return (hep) ? (JSAtom *)*hep : NULL; + + return str2 ? (JSAtom *)STRING_TO_JSVAL(str2) : NULL; } -JSAtom * -js_AtomizePrimitiveValue(JSContext *cx, jsval v) +JSBool +js_AtomizePrimitiveValue(JSContext *cx, jsval v, JSAtom **atomp) { - if (JSVAL_IS_STRING(v)) - return js_AtomizeString(cx, JSVAL_TO_STRING(v), 0); - if (JSVAL_IS_DOUBLE(v)) - return js_AtomizeDouble(cx, *JSVAL_TO_DOUBLE(v)); - JS_ASSERT(JSVAL_IS_INT(v) || v == JSVAL_TRUE || v == JSVAL_FALSE || - v == JSVAL_NULL || v == JSVAL_VOID); - return AtomizeHashedKey(cx, v, (JSHashNumber)v); + JSAtom *atom; + + if (JSVAL_IS_STRING(v)) { + atom = js_AtomizeString(cx, JSVAL_TO_STRING(v), 0); + if (!atom) + return JS_FALSE; + } else if (JSVAL_IS_DOUBLE(v)) { + atom = js_AtomizeDouble(cx, *JSVAL_TO_DOUBLE(v)); + if (!atom) + return JS_FALSE; + } else { + JS_ASSERT(JSVAL_IS_INT(v) || v == JSVAL_TRUE || v == JSVAL_FALSE || + v == JSVAL_NULL || v == JSVAL_VOID); + atom = (JSAtom *)v; + } + *atomp = atom; + return JS_TRUE; } JSAtom * @@ -737,21 +782,36 @@ js_ValueToStringAtom(JSContext *cx, jsval v) #ifdef DEBUG -JS_STATIC_DLL_CALLBACK(int) -atom_dumper(JSHashEntry *he, int i, void *arg) +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +atom_dumper(JSDHashTable *table, JSDHashEntryHdr *hdr, + uint32 number, void *arg) { + JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); FILE *fp = (FILE *)arg; - JSAtom *atom = (JSAtom *)he; + void *key; + uintN flags; - fprintf(fp, "%3u %08x ", (uintN)i, (uintN)he->keyHash); - if (ATOM_IS_STRING(atom)) - js_FileEscapedString(fp, ATOM_TO_STRING(atom), '"'); - else if (ATOM_IS_INT(atom)) - fprintf(fp, "%ld", (long)ATOM_TO_INT(atom)); - else - fprintf(fp, "%.16g", *ATOM_TO_DOUBLE(atom)); + fprintf(fp, "%3u %08x ", number, (uintN)entry->hdr.keyHash); + if (entry->keyAndFlags == 0) { + fputs("", fp); + } else { + key = ATOM_ENTRY_KEY(entry); + if (IS_DOUBLE_TABLE(table)) { + fprintf(fp, "%.16g", *(jsdouble *)key); + } else { + JS_ASSERT(IS_STRING_TABLE(table)); + js_FileEscapedString(fp, (JSString *)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 HT_ENUMERATE_NEXT; + return JS_DHASH_NEXT; } JS_FRIEND_API(void) @@ -759,11 +819,19 @@ js_DumpAtoms(JSContext *cx, FILE *fp) { JSAtomState *state = &cx->runtime->atomState; - fprintf(fp, "\natom table contents:\n"); - JS_HashTableEnumerateEntries(state->table, atom_dumper, fp); -#ifdef HASHMETER - JS_HashTableDumpMeter(state->table, atom_dumper, fp); + fprintf(fp, "stringAtoms table contents:\n"); + JS_DHashTableEnumerate(&state->stringAtoms, atom_dumper, fp); +#ifdef JS_DHASHMETER + JS_DHashTableDumpMeter(&state->stringAtoms, atom_dumper, fp); #endif + putc('\n', fp); + + fprintf(fp, "doubleAtoms table contents:\n"); + JS_DHashTableEnumerate(&state->doubleAtoms, atom_dumper, fp); +#ifdef JS_DHASHMETER + JS_DHashTableDumpMeter(&state->doubleAtoms, atom_dumper, fp); +#endif + putc('\n', fp); } #endif diff --git a/js/src/jsatom.h b/js/src/jsatom.h index a4819df38d90..8cdbc063f2b2 100644 --- a/js/src/jsatom.h +++ b/js/src/jsatom.h @@ -46,6 +46,7 @@ #include "jsconfig.h" #include "jstypes.h" #include "jshash.h" /* Added by JSIFY */ +#include "jsdhash.h" #include "jsapi.h" #include "jsprvtd.h" #include "jspubtd.h" @@ -56,28 +57,16 @@ JS_BEGIN_EXTERN_C -#define ATOM_PINNED 0x01 /* atom is pinned against GC */ -#define ATOM_INTERNED 0x02 /* pinned variant for JS_Intern* API */ -#define ATOM_MARK 0x04 /* atom is reachable via GC */ -#define ATOM_HIDDEN 0x08 /* atom is in special hidden subspace */ -#define ATOM_NOCOPY 0x40 /* don't copy atom string bytes */ -#define ATOM_TMPSTR 0x80 /* internal, to avoid extra string */ +#define ATOM_PINNED 0x1 /* atom is pinned against GC */ +#define ATOM_INTERNED 0x2 /* pinned variant for JS_Intern* API */ +#define ATOM_NOCOPY 0x4 /* don't copy atom string bytes */ +#define ATOM_TMPSTR 0x8 /* internal, to avoid extra string */ -struct JSAtom { - JSHashEntry entry; /* key is jsval or unhidden atom - if ATOM_HIDDEN */ - uint32 flags; /* pinned, interned, and mark flags */ -}; - -#define ATOM_KEY(atom) ((jsval)(atom)->entry.key) -#define ATOM_IS_INT(atom) JSVAL_IS_INT(ATOM_KEY(atom)) -#define ATOM_TO_INT(atom) JSVAL_TO_INT(ATOM_KEY(atom)) +#define ATOM_KEY(atom) ((jsval)(atom)) #define ATOM_IS_DOUBLE(atom) JSVAL_IS_DOUBLE(ATOM_KEY(atom)) #define ATOM_TO_DOUBLE(atom) JSVAL_TO_DOUBLE(ATOM_KEY(atom)) #define ATOM_IS_STRING(atom) JSVAL_IS_STRING(ATOM_KEY(atom)) #define ATOM_TO_STRING(atom) JSVAL_TO_STRING(ATOM_KEY(atom)) -#define ATOM_IS_BOOLEAN(atom) JSVAL_IS_BOOLEAN(ATOM_KEY(atom)) -#define ATOM_TO_BOOLEAN(atom) JSVAL_TO_BOOLEAN(ATOM_KEY(atom)) JS_STATIC_ASSERT(sizeof(JSHashNumber) == 4); JS_STATIC_ASSERT(sizeof(JSAtom *) == JS_BYTES_PER_WORD); @@ -156,8 +145,8 @@ struct JSAtomMap { }; struct JSAtomState { - JSHashTable *table; /* hash table containing all atoms */ - + JSDHashTable stringAtoms; /* hash table with shared strings */ + JSDHashTable doubleAtoms; /* hash table with shared doubles */ uint32 tablegen; /* number of atoms mutations to optimize hashing */ #ifdef JS_THREADSAFE @@ -274,6 +263,9 @@ struct JSAtomState { #define LAZY_ATOM_OFFSET_START offsetof(JSAtomState, lazy) #define ATOM_OFFSET_LIMIT (sizeof(JSAtomState)) +#define COMMON_ATOMS_START(state) \ + (JSAtom **)((uint8 *)(state) + ATOM_OFFSET_START) + /* Start and limit offsets should correspond to atoms. */ JS_STATIC_ASSERT(ATOM_OFFSET_START % sizeof(JSAtom *) == 0); JS_STATIC_ASSERT(ATOM_OFFSET_LIMIT % sizeof(JSAtom *) == 0); @@ -379,10 +371,7 @@ js_FinishAtomState(JSRuntime *rt); */ extern void -js_TraceAtom(JSTracer *trc, JSAtom *atom); - -extern void -js_TraceLockedAtoms(JSTracer *trc, JSBool allAtoms); +js_TraceAtomState(JSTracer *trc, JSBool allAtoms); extern void js_SweepAtomState(JSContext *cx); @@ -423,8 +412,8 @@ js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length); /* * This variant handles all primitive values. */ -extern JSAtom * -js_AtomizePrimitiveValue(JSContext *cx, jsval v); +JSBool +js_AtomizePrimitiveValue(JSContext *cx, jsval v, JSAtom **atomp); /* * Convert v to an atomized string. diff --git a/js/src/jsdbgapi.c b/js/src/jsdbgapi.c index 742d4729cb56..d473030316c0 100644 --- a/js/src/jsdbgapi.c +++ b/js/src/jsdbgapi.c @@ -1472,7 +1472,7 @@ GetAtomTotalSize(JSContext *cx, JSAtom *atom) { size_t nbytes; - nbytes = sizeof *atom; + nbytes = sizeof(JSAtom *) + sizeof(JSDHashEntryStub); if (ATOM_IS_STRING(atom)) { nbytes += sizeof(JSString); nbytes += (ATOM_TO_STRING(atom)->length + 1) * sizeof(jschar); diff --git a/js/src/jsemit.c b/js/src/jsemit.c index ba99fbce8a51..e8de036e144e 100644 --- a/js/src/jsemit.c +++ b/js/src/jsemit.c @@ -3145,8 +3145,7 @@ EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { if (pn3->pn_type == TOK_DEFAULT) continue; - atom = js_AtomizePrimitiveValue(cx, pn3->pn_val); - if (!atom) + if (!js_AtomizePrimitiveValue(cx, pn3->pn_val, &atom)) goto bad; ale = js_IndexAtom(cx, atom, &cg->atomList); if (!ale) diff --git a/js/src/jsfun.c b/js/src/jsfun.c index 791dc5822bf7..bfd8a567eeb2 100644 --- a/js/src/jsfun.c +++ b/js/src/jsfun.c @@ -754,7 +754,7 @@ call_enumerate(JSContext *cx, JSObject *obj) JSScopeProperty *sprop, *cprop; JSPropertyOp getter; jsval *vec; - JSAtom *atom; + jsid id; JSProperty *prop; fp = (JSStackFrame *) JS_GetPrivate(cx, obj); @@ -793,12 +793,8 @@ call_enumerate(JSContext *cx, JSObject *obj) continue; /* Trigger reflection by looking up the unhidden atom for sprop->id. */ - JS_ASSERT(JSID_IS_ATOM(sprop->id)); - atom = JSID_TO_ATOM(sprop->id); - JS_ASSERT(atom->flags & ATOM_HIDDEN); - atom = (JSAtom *) atom->entry.value; - - if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) + id = JSID_UNHIDE_NAME(sprop->id); + if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) return JS_FALSE; /* @@ -1248,24 +1244,6 @@ fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) } } -static void -fun_finalize(JSContext *cx, JSObject *obj) -{ - JSFunction *fun; - - /* No valid function object should lack private data, but check anyway. */ - fun = (JSFunction *) JS_GetPrivate(cx, obj); - if (!fun) - return; - - /* - * This works because obj is finalized before JSFunction. See - * comments in js_GC before the finalization loop. - */ - if (fun->object == obj) - fun->object = NULL; -} - #if JS_HAS_XDR #include "jsxdrapi.h" @@ -1380,7 +1358,14 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp) ? JSXDR_FUNCONST : JSXDR_FUNVAR; userid = INT_TO_JSVAL(sprop->shortid); - propAtom = JSID_TO_ATOM(sprop->id); + + /* + * sprop->id here represents hidden names so we unhide it and + * encode as an atom. During decoding we read the atom and use + * js_AddHiddenProperty to reconstruct sprop with the hidden + * id. + */ + propAtom = JSID_TO_ATOM(JSID_UNHIDE_NAME(sprop->id)); if (!JS_XDRUint32(xdr, &type) || !JS_XDRUint32(xdr, &userid) || !js_XDRCStringAtom(xdr, &propAtom)) { @@ -1503,7 +1488,7 @@ fun_trace(JSTracer *trc, JSObject *obj) if (fun->object != obj) JS_CALL_TRACER(trc, fun->object, JSTRACE_OBJECT, "object"); if (fun->atom) - JS_CALL_TRACER(trc, fun->atom, JSTRACE_ATOM, "atom"); + JS_CALL_STRING_TRACER(trc, ATOM_TO_STRING(fun->atom), "atom"); if (FUN_INTERPRETED(fun) && fun->u.i.script) js_TraceScript(trc, fun->u.i.script); } @@ -1533,7 +1518,7 @@ JS_FRIEND_DATA(JSClass) js_FunctionClass = { JS_PropertyStub, JS_PropertyStub, fun_getProperty, JS_PropertyStub, fun_enumerate, (JSResolveOp)fun_resolve, - fun_convert, fun_finalize, + fun_convert, JS_FinalizeStub, NULL, NULL, NULL, NULL, fun_xdrObject, fun_hasInstance, diff --git a/js/src/jsgc.c b/js/src/jsgc.c index 45d8e8bb4cec..39a4f0a45422 100644 --- a/js/src/jsgc.c +++ b/js/src/jsgc.c @@ -263,11 +263,13 @@ static uint8 GCTypeToTraceKindMap[GCX_NTYPES] = { }; /* - * Ensure that GC-allocated JSFunction and JSObject would go to different - * lists so we can easily finalize JSObject before JSFunction. See comments - * in js_GC. + * Ensure that JSObject is allocated from a different GC-list rather than + * jsdouble and JSString so we can easily finalize JSObject before these 2 + * types of GC things. See comments in js_GC. */ -JS_STATIC_ASSERT(GC_FREELIST_INDEX(sizeof(JSFunction)) != +JS_STATIC_ASSERT(GC_FREELIST_INDEX(sizeof(JSString)) != + GC_FREELIST_INDEX(sizeof(JSObject))); +JS_STATIC_ASSERT(GC_FREELIST_INDEX(sizeof(jsdouble)) != GC_FREELIST_INDEX(sizeof(JSObject))); /* @@ -1460,10 +1462,6 @@ JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind) */ break; - case JSTRACE_ATOM: - js_TraceAtom(trc, (JSAtom *)thing); - break; - #if JS_HAS_XML_SUPPORT case JSTRACE_NAMESPACE: js_TraceXMLNamespace(trc, (JSXMLNamespace *)thing); @@ -1716,7 +1714,6 @@ JS_CallTracer(JSTracer *trc, void *thing, uint32 kind) { JSContext *cx; JSRuntime *rt; - JSAtom *atom; uint8 *flagp; JS_ASSERT(thing); @@ -1733,36 +1730,6 @@ JS_CallTracer(JSTracer *trc, void *thing, uint32 kind) JS_ASSERT(rt->gcMarkingTracer == trc); JS_ASSERT(rt->gcLevel > 0); - if (kind == JSTRACE_ATOM) { - atom = (JSAtom *)thing; - - /* - * Here we should workaround gcThingCallback deficiency of being able - * to handle only GC things, not atoms. Because of this we must call - * the callback on all GC things referenced by atoms. For unmarked - * atoms we call when tracing things reached directly from each such - * atom, but for already-marked atoms we have to call the callback - * explicitly. - * - * We do not do it currently for compatibility with XPCOM cycle - * collector which ignores JSString * and jsdouble * GC things that - * the atom can refer to. - * - * FIXME bug 386265 will remove the need to trace atoms and bug 379718 - * may remove gcThingCallback altogether. - */ - if (!(atom->flags & ATOM_MARK)) { - atom->flags |= ATOM_MARK; - - /* - * Call js_TraceAtom directly to avoid an extra dispatch in - * JS_TraceChildren. - */ - js_TraceAtom(trc, (JSAtom *)thing); - } - goto out; - } - flagp = js_GetGCThingFlags(thing); JS_ASSERT(*flagp != GCF_FINAL); JS_ASSERT(GCTypeToTraceKindMap[*flagp & GCF_TYPEMASK] == kind); @@ -2029,8 +1996,7 @@ TraceWeakRoots(JSTracer *trc, JSWeakRoots *wr) gc_typenames[i]); } } - if (wr->lastAtom) - JS_CALL_TRACER(trc, wr->lastAtom, JSTRACE_ATOM, "lastAtom"); + JS_CALL_VALUE_TRACER(trc, wr->lastAtom, "lastAtom"); JS_SET_TRACING_NAME(trc, "lastInternalResult"); js_CallValueTracerIfGCThing(trc, wr->lastInternalResult); } @@ -2127,7 +2093,7 @@ js_TraceRuntime(JSTracer *trc, JSBool allAtoms) JS_DHashTableEnumerate(&rt->gcRootsHash, gc_root_traversal, trc); if (rt->gcLocksHash) JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_traversal, trc); - js_TraceLockedAtoms(trc, allAtoms); + js_TraceAtomState(trc, allAtoms); js_TraceWatchPoints(trc); js_TraceNativeIteratorStates(trc); @@ -2397,12 +2363,22 @@ restart: * so that any attempt to allocate a GC-thing from a finalizer will fail, * rather than nest badly and leave the unmarked newborn to be swept. * + * We first sweep atom state so we can use js_IsAboutToBeFinalized on + * JSString or jsdouble held in a hashtable to check if the hashtable + * entry can be freed. Note that even after the entry is freed, JSObject + * finalizers can continue to access the corresponding jsdouble* and + * JSString* assuming that they are unique. This works since the + * atomization API must not be called during GC. + */ + js_SweepAtomState(cx); + + /* * Here we need to ensure that JSObject instances are finalized before GC- - * allocated JSFunction instances so fun_finalize from jsfun.c can clear - * the weak pointer from the JSFunction back to the JSObject. For that we - * simply finalize the list containing JSObject first since the static - * assert at the beginning of the file guarantees that JSFunction instances - * are allocated from a different list. + * allocated JSString and jsdouble instances so object's finalizer can + * access them even if they will be freed. For that we simply finalize the + * list containing JSObject first since the static assert at the beginning + * of the file guarantees that JSString and jsdouble instances are + * allocated from a different list. */ for (i = 0; i < GC_NUM_FREELISTS; i++) { arenaList = &rt->gcArenaList[i == 0 @@ -2451,11 +2427,9 @@ restart: /* * Sweep the runtime's property tree after finalizing objects, in case any - * had watchpoints referencing tree nodes. Then sweep atoms, which may be - * referenced from dead property ids. + * had watchpoints referencing tree nodes. */ js_SweepScopeProperties(cx); - js_SweepAtomState(cx); /* * Sweep script filenames after sweeping functions in the generic loop diff --git a/js/src/jsgc.h b/js/src/jsgc.h index af976ca24faa..1e9ad1a87d74 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -175,10 +175,9 @@ js_IsAboutToBeFinalized(JSContext *cx, void *thing); JS_STATIC_ASSERT(JSTRACE_STRING == 2); #define JSTRACE_FUNCTION 3 -#define JSTRACE_ATOM 4 -#define JSTRACE_NAMESPACE 5 -#define JSTRACE_QNAME 6 -#define JSTRACE_XML 7 +#define JSTRACE_NAMESPACE 4 +#define JSTRACE_QNAME 5 +#define JSTRACE_XML 6 #if JS_HAS_XML_SUPPORT # define JS_IS_VALID_TRACE_KIND(kind) ((uint32)(kind) <= JSTRACE_XML) @@ -311,7 +310,7 @@ struct JSWeakRoots { JSGCThing *newborn[GCX_NTYPES]; /* Atom root for the last-looked-up atom on this context. */ - JSAtom *lastAtom; + jsval lastAtom; /* Root for the result of the most recent js_InternalInvoke call. */ jsval lastInternalResult; diff --git a/js/src/jsiter.c b/js/src/jsiter.c index 6d5e204b60b9..43dbfe6d1d00 100644 --- a/js/src/jsiter.c +++ b/js/src/jsiter.c @@ -565,23 +565,10 @@ CallEnumeratorNext(JSContext *cx, JSObject *iterobj, uintN flags, jsval *rval) } } else { /* Make rval a string for uniformity and compatibility. */ - if (JSID_IS_ATOM(id)) { - *rval = ATOM_KEY(JSID_TO_ATOM(id)); - } -#if JS_HAS_XML_SUPPORT - else if (JSID_IS_OBJECT(id)) { - str = js_ValueToString(cx, OBJECT_JSID_TO_JSVAL(id)); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - } -#endif - else { - str = js_NumberToString(cx, (jsdouble)JSID_TO_INT(id)); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - } + str = js_ValueToString(cx, ID_TO_VALUE(id)); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); } return JS_TRUE; diff --git a/js/src/jsobj.c b/js/src/jsobj.c index 7f5c9a27dc39..76c5ffb04a50 100644 --- a/js/src/jsobj.c +++ b/js/src/jsobj.c @@ -724,7 +724,6 @@ obj_toSource(JSContext *cx, uintN argc, jsval *vp) jsval *val; JSString *gsopold[2]; JSString *gsop[2]; - JSAtom *atom; JSString *idstr, *valstr, *str; int stackDummy; @@ -855,7 +854,6 @@ obj_toSource(JSContext *cx, uintN argc, jsval *vp) * Convert id to a jsval and then to a string. Decide early whether we * prefer get/set or old getter/setter syntax. */ - atom = JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL; idstr = js_ValueToString(cx, ID_TO_VALUE(id)); if (!idstr) { ok = JS_FALSE; @@ -929,9 +927,9 @@ obj_toSource(JSContext *cx, uintN argc, jsval *vp) * If id is a string that's not an identifier, then it needs to be * quoted. Also, negative integer ids must be quoted. */ - if (atom + if (JSID_IS_ATOM(id) ? !idIsLexicalIdentifier - : (JSID_IS_OBJECT(id) || JSID_TO_INT(id) < 0)) { + : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) { idstr = js_QuoteString(cx, idstr, (jschar)'\''); if (!idstr) { ok = JS_FALSE; @@ -2884,41 +2882,12 @@ CheckForStringIndex(jsid id, const jschar *cp, const jschar *end, return id; } -static JSBool -HidePropertyName(JSContext *cx, jsid *idp) -{ - jsid id; - JSAtom *atom, *hidden; - - id = *idp; - JS_ASSERT(JSID_IS_ATOM(id)); - - atom = JSID_TO_ATOM(id); - JS_ASSERT(!(atom->flags & ATOM_HIDDEN)); - JS_ASSERT(ATOM_IS_STRING(atom)); - - hidden = js_AtomizeString(cx, ATOM_TO_STRING(atom), ATOM_HIDDEN); - if (!hidden) - return JS_FALSE; - - /* - * Link hidden to unhidden atom to optimize call_enumerate -- this means - * the GC must mark a hidden atom's unhidden counterpart (see js_MarkAtom - * in jsgc.c). It uses the atom's entry.value member for this linkage. - */ - hidden->entry.value = atom; - *idp = ATOM_TO_JSID(hidden); - return JS_TRUE; -} - JSScopeProperty * js_AddHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSPropertyOp getter, JSPropertyOp setter, uint32 slot, uintN attrs, uintN flags, intN shortid) { - if (!HidePropertyName(cx, &id)) - return NULL; - + id = JSID_HIDE_NAME(id); flags |= SPROP_IS_HIDDEN; return js_AddNativeProperty(cx, obj, id, getter, setter, slot, attrs, flags, shortid); @@ -2928,8 +2897,8 @@ JSBool js_LookupHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp) { - return HidePropertyName(cx, &id) && - js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_HIDDEN, + id = JSID_HIDE_NAME(id); + return js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_HIDDEN, objp, propp); } @@ -4461,7 +4430,7 @@ CheckCtorGetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp) uintN attrs; atom = cx->runtime->atomState.constructorAtom; - JS_ASSERT(id == ATOM_KEY(atom)); + JS_ASSERT(id == ATOM_TO_JSID(atom)); return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_READ, vp, &attrs); } @@ -4473,7 +4442,7 @@ CheckCtorSetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp) uintN attrs; atom = cx->runtime->atomState.constructorAtom; - JS_ASSERT(id == ATOM_KEY(atom)); + JS_ASSERT(id == ATOM_TO_JSID(atom)); return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_WRITE, vp, &attrs); } diff --git a/js/src/jsopcode.c b/js/src/jsopcode.c index fb1699fbe66a..2669c2ada1a4 100644 --- a/js/src/jsopcode.c +++ b/js/src/jsopcode.c @@ -1218,9 +1218,9 @@ GetSlotAtom(JSPrinter *jp, JSPropertyOp getter, uintN slot) if (sprop->getter != getter) continue; JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); - JS_ASSERT(JSID_IS_ATOM(sprop->id)); + JS_ASSERT(JSID_IS_HIDDEN(sprop->id)); if ((uintN) sprop->shortid == slot) - return JSID_TO_ATOM(sprop->id); + return JSID_TO_ATOM(JSID_UNHIDE_NAME(sprop->id)); } obj = OBJ_GET_PROTO(jp->sprinter.context, obj); } @@ -4776,8 +4776,9 @@ js_DecompileFunction(JSPrinter *jp, JSFunction *fun) continue; JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); JS_ASSERT((uint16) sprop->shortid < nargs); - JS_ASSERT(JSID_IS_ATOM(sprop->id)); - params[(uint16) sprop->shortid] = JSID_TO_ATOM(sprop->id); + JS_ASSERT(JSID_IS_HIDDEN(sprop->id)); + params[(uint16) sprop->shortid] = + JSID_TO_ATOM(JSID_UNHIDE_NAME(sprop->id)); } pc = fun->u.i.script->main; diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index 6d39251393c4..fe03dc02cd9d 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -57,30 +57,37 @@ #include "jspubtd.h" /* Internal identifier (jsid) macros. */ -#define JSID_ATOM 0x0 -#define JSID_INT 0x1 -#define JSID_OBJECT 0x2 -#define JSID_TAGMASK 0x3 -#define JSID_TAG(id) ((id) & JSID_TAGMASK) -#define JSID_SETTAG(id,t) ((id) | (t)) -#define JSID_CLRTAG(id) ((id) & ~(jsid)JSID_TAGMASK) -#define JSID_IS_ATOM(id) (JSID_TAG(id) == JSID_ATOM) +#define JSID_IS_ATOM(id) JSVAL_IS_STRING((jsval)(id)) #define JSID_TO_ATOM(id) ((JSAtom *)(id)) -#define ATOM_TO_JSID(atom) ((jsid)(atom)) -#define ATOM_JSID_TO_JSVAL(id) ATOM_KEY(JSID_TO_ATOM(id)) +#define ATOM_TO_JSID(atom) (JS_ASSERT(ATOM_IS_STRING(atom)), \ + (jsid)(atom)) -#define JSID_IS_INT(id) ((id) & JSID_INT) -#define JSID_TO_INT(id) ((jsint)(id) >> 1) -#define INT_TO_JSID(i) (((jsint)(i) << 1) | JSID_INT) -#define INT_JSID_TO_JSVAL(id) (id) -#define INT_JSVAL_TO_JSID(v) (v) +#define JSID_IS_INT(id) JSVAL_IS_INT((jsval)(id)) +#define JSID_TO_INT(id) JSVAL_TO_INT((jsval)(id)) +#define INT_TO_JSID(i) ((jsid)INT_TO_JSVAL(i)) +#define INT_JSVAL_TO_JSID(v) ((jsid)(v)) +#define INT_JSID_TO_JSVAL(id) ((jsval)(id)) -#define JSID_IS_OBJECT(id) (JSID_TAG(id) == JSID_OBJECT) -#define JSID_TO_OBJECT(id) ((JSObject *) JSID_CLRTAG(id)) -#define OBJECT_TO_JSID(obj) ((jsid)(obj) | JSID_OBJECT) -#define OBJECT_JSID_TO_JSVAL(id) OBJECT_TO_JSVAL(JSID_CLRTAG(id)) -#define OBJECT_JSVAL_TO_JSID(v) OBJECT_TO_JSID(JSVAL_TO_OBJECT(v)) +#define JSID_IS_OBJECT(id) JSVAL_IS_OBJECT((jsval)(id)) +#define JSID_TO_OBJECT(id) JSVAL_TO_OBJECT((jsval)(id)) +#define OBJECT_TO_JSID(obj) ((jsid)OBJECT_TO_JSVAL(obj)) +#define OBJECT_JSVAL_TO_JSID(v) ((jsid)v) + +/* + * To put a property into the hidden subspace we re-tag JSString * behind + * property's atom as JSVAL_BOOLEAN to get a different id. js_TraceId must + * properly trace such pseudo-booleans to ensure GC safety. + */ +#define JSID_IS_HIDDEN(id) (JSVAL_TAG((jsval)(id)) == JSVAL_BOOLEAN) + +#define JSID_HIDE_NAME(id) \ + (JS_ASSERT(JSID_IS_ATOM(id)), \ + (jsid)((jsval)(id) ^ (JSVAL_STRING ^ JSVAL_BOOLEAN))) + +#define JSID_UNHIDE_NAME(id) \ + (JS_ASSERT(JSID_IS_HIDDEN(id)), \ + (jsid)((jsval)(id) ^ (JSVAL_BOOLEAN ^ JSVAL_STRING))) /* Scalar typedefs. */ typedef uint8 jsbytecode; diff --git a/js/src/jsscope.c b/js/src/jsscope.c index 30908854d2d7..e6fe5747fffb 100644 --- a/js/src/jsscope.c +++ b/js/src/jsscope.c @@ -1510,16 +1510,10 @@ js_ClearScope(JSContext *cx, JSScope *scope) void js_TraceId(JSTracer *trc, jsid id) { - JSObject *obj; + jsval v; - if (JSID_IS_ATOM(id)) { - JS_CALL_TRACER(trc, JSID_TO_ATOM(id), JSTRACE_ATOM, "id"); - } else if (!JSID_IS_INT(id)) { - JS_ASSERT(JSID_IS_OBJECT(id)); - obj = JSID_TO_OBJECT(id); - if (obj) - JS_CALL_OBJECT_TRACER(trc, obj, "id"); - } + v = ID_TO_VALUE(id); + JS_CALL_VALUE_TRACER(trc, v, "id"); } #if defined DEBUG || defined DUMP_SCOPE_STATS @@ -1531,27 +1525,28 @@ static void PrintPropertyGetterOrSetter(JSTracer *trc, char *buf, size_t bufsize) { JSScopeProperty *sprop; + jsid id; size_t n; - const char *name; + const char *name, *prefix; JS_ASSERT(trc->debugPrinter == PrintPropertyGetterOrSetter); sprop = (JSScopeProperty *)trc->debugPrintArg; + id = sprop->id; name = trc->debugPrintIndex ? js_setter_str : js_getter_str; - n = strlen(name); - if (JSID_IS_ATOM(sprop->id)) { - JSAtom *atom = JSID_TO_ATOM(sprop->id); - if (atom && ATOM_IS_STRING(atom)) { - n = js_PutEscapedString(buf, bufsize - 1, - ATOM_TO_STRING(atom), 0); - buf[n++] = ' '; - strncpy(buf + n, name, bufsize - n); - buf[bufsize - 1] = '\0'; + if (JSID_IS_ATOM(id) || JSID_IS_HIDDEN(id)) { + if (JSID_IS_HIDDEN(id)) { + id = JSID_UNHIDE_NAME(id); + prefix = "hidden "; } else { - JS_snprintf(buf, bufsize, "uknown %s", name); + prefix = ""; } + n = js_PutEscapedString(buf, bufsize - 1, + ATOM_TO_STRING(JSID_TO_ATOM(id)), 0); + if (n < bufsize - 1) + JS_snprintf(buf + n, bufsize - n, " %s%s", prefix, name); } else if (JSID_IS_INT(sprop->id)) { - JS_snprintf(buf, bufsize, "%d %s", JSID_TO_INT(sprop->id), name); + JS_snprintf(buf, bufsize, "%d %s", JSID_TO_INT(id), name); } else { JS_snprintf(buf, bufsize, " %s", name); } @@ -1647,22 +1642,32 @@ js_MeterPropertyTree(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, static void DumpSubtree(JSContext *cx, JSScopeProperty *sprop, int level, FILE *fp) { + jsval v; JSString *str; JSScopeProperty *kids, *kid; PropTreeKidsChunk *chunk; uintN i; fprintf(fp, "%*sid ", level, ""); - if (JSID_IS_ATOM(sprop->id)) { - str = ATOM_TO_STRING(JSID_TO_ATOM(sprop->id)); - } else if (JSID_IS_OBJECT(sprop->id)) { - str = js_ValueToString(cx, OBJECT_JSID_TO_JSVAL(sprop->id)); + v = ID_TO_VALUE(sprop->id); + if (JSID_IS_INT(sprop->id)) { + fprintf(fp, "%d", JSVAL_TO_INT(v)); } else { - fprintf(fp, "%d", JSVAL_TO_INT(sprop->id)); - str = NULL; + if (JSID_IS_ATOM(sprop->id)) { + str = JSVAL_TO_STRING(v); + } else if (JSID_IS_HIDDEN(sprop->id)) { + str = JSVAL_TO_STRING(v); + fputs("hidden ", fp); + } else { + JSASSERT(JSID_IS_OBJECT(sprop->id)); + str = js_ValueToString(cx, v); + fputs("object ", fp); + } + if (!str) + fputs("", fp); + else + js_FileEscapedString(fp, str, '"'); } - if (str) - js_FileEscapedString(fp, str, 0); fprintf(fp, " g/s %p/%p slot %lu attrs %x flags %x shortid %d\n", (void *) sprop->getter, (void *) sprop->setter, diff --git a/js/src/jsscope.h b/js/src/jsscope.h index 20b917309db3..5ee217914e09 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -348,9 +348,8 @@ js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, extern void js_DestroyScope(JSContext *cx, JSScope *scope); -#define ID_TO_VALUE(id) (JSID_IS_ATOM(id) ? ATOM_JSID_TO_JSVAL(id) : \ - JSID_IS_OBJECT(id) ? OBJECT_JSID_TO_JSVAL(id) : \ - (jsval)(id)) +#define ID_TO_VALUE(id) \ + (JSID_IS_HIDDEN(id) ? (jsval)JSID_UNHIDE_NAME(id) : (jsval)(id)) extern JS_FRIEND_API(JSScopeProperty **) js_SearchScope(JSScope *scope, jsid id, JSBool adding); diff --git a/js/src/jsscript.c b/js/src/jsscript.c index d3dd4c2ed328..ab8387500836 100644 --- a/js/src/jsscript.c +++ b/js/src/jsscript.c @@ -933,13 +933,6 @@ js_compare_strings(const void *k1, const void *k2) return strcmp((const char *) k1, (const char *) k2) == 0; } -/* Shared with jsatom.c to save code space. */ -extern void * JS_DLL_CALLBACK -js_alloc_table_space(void *priv, size_t size); - -extern void JS_DLL_CALLBACK -js_free_table_space(void *priv, void *item); - /* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */ typedef struct ScriptFilenameEntry { JSHashEntry *next; /* hash chain linkage */ @@ -950,6 +943,18 @@ typedef struct ScriptFilenameEntry { char filename[3]; /* two or more bytes, NUL-terminated */ } ScriptFilenameEntry; +JS_STATIC_DLL_CALLBACK(void *) +js_alloc_table_space(void *priv, size_t size) +{ + return malloc(size); +} + +JS_STATIC_DLL_CALLBACK(void) +js_free_table_space(void *priv, void *item) +{ + free(item); +} + JS_STATIC_DLL_CALLBACK(JSHashEntry *) js_alloc_sftbl_entry(void *priv, const void *key) { @@ -1497,14 +1502,18 @@ js_TraceScript(JSTracer *trc, JSScript *script) JSAtomMap *map; uintN i, length; JSAtom **vector; + jsval v; JSObjectArray *objarray; map = &script->atomMap; length = map->length; vector = map->vector; for (i = 0; i < length; i++) { - JS_SET_TRACING_INDEX(trc, "atomMap", i); - JS_CallTracer(trc, vector[i], JSTRACE_ATOM); + v = ATOM_KEY(vector[i]); + if (JSVAL_IS_TRACEABLE(v)) { + JS_SET_TRACING_INDEX(trc, "atomMap", i); + JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v)); + } } if (script->objectsOffset != 0) { diff --git a/js/src/jsstr.c b/js/src/jsstr.c index 18e9843fadf9..28506c5806aa 100644 --- a/js/src/jsstr.c +++ b/js/src/jsstr.c @@ -2760,10 +2760,13 @@ js_ValueToSource(JSContext *cx, jsval v) return str; } -JSHashNumber +/* + * str is not necessarily a GC thing here. + */ +uint32 js_HashString(JSString *str) { - JSHashNumber h; + uint32 h; const jschar *s; size_t n; @@ -2773,31 +2776,9 @@ js_HashString(JSString *str) return h; } -intN -js_CompareStrings(JSString *str1, JSString *str2) -{ - size_t l1, l2, n, i; - const jschar *s1, *s2; - intN cmp; - - JS_ASSERT(str1); - JS_ASSERT(str2); - - /* Fast case: pointer equality could be a quick win. */ - if (str1 == str2) - return 0; - - l1 = JSSTRING_LENGTH(str1), l2 = JSSTRING_LENGTH(str2); - s1 = JSSTRING_CHARS(str1), s2 = JSSTRING_CHARS(str2); - n = JS_MIN(l1, l2); - for (i = 0; i < n; i++) { - cmp = s1[i] - s2[i]; - if (cmp != 0) - return cmp; - } - return (intN)(l1 - l2); -} - +/* + * str is not necessarily a GC thing here. + */ JSBool js_EqualStrings(JSString *str1, JSString *str2) { @@ -2828,6 +2809,31 @@ js_EqualStrings(JSString *str1, JSString *str2) return JS_TRUE; } +intN +js_CompareStrings(JSString *str1, JSString *str2) +{ + size_t l1, l2, n, i; + const jschar *s1, *s2; + intN cmp; + + JS_ASSERT(str1); + JS_ASSERT(str2); + + /* Fast case: pointer equality could be a quick win. */ + if (str1 == str2) + return 0; + + l1 = JSSTRING_LENGTH(str1), l2 = JSSTRING_LENGTH(str2); + s1 = JSSTRING_CHARS(str1), s2 = JSSTRING_CHARS(str2); + n = JS_MIN(l1, l2); + for (i = 0; i < n; i++) { + cmp = s1[i] - s2[i]; + if (cmp != 0) + return cmp; + } + return (intN)(l1 - l2); +} + size_t js_strlen(const jschar *s) { diff --git a/js/src/jsstr.h b/js/src/jsstr.h index 29f4ef80814a..87a35b0d6ebb 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -51,7 +51,6 @@ #include #include "jspubtd.h" #include "jsprvtd.h" -#include "jshash.h" JS_BEGIN_EXTERN_C @@ -384,13 +383,19 @@ js_ValueToString(JSContext *cx, jsval v); extern JS_FRIEND_API(JSString *) js_ValueToSource(JSContext *cx, jsval v); -#ifdef HT_ENUMERATE_NEXT /* XXX don't require jshash.h */ /* - * Compute a hash function from str. + * Compute a hash function from str. The caller can call this function even if + * str is not a GC-allocated thing. */ -extern JSHashNumber +extern uint32 js_HashString(JSString *str); -#endif + +/* + * Test if strings are equal. The caller can call the function even if str1 + * or str2 are not GC-allocated things. + */ +extern JSBool +js_EqualStrings(JSString *str1, JSString *str2); /* * Return less than, equal to, or greater than zero depending on whether @@ -399,12 +404,6 @@ js_HashString(JSString *str); extern intN js_CompareStrings(JSString *str1, JSString *str2); -/* - * Test if strings are equal. - */ -extern JSBool -js_EqualStrings(JSString *str1, JSString *str2); - /* * Boyer-Moore-Horspool superlinear search for pat:patlen in text:textlen. * The patlen argument must be positive and no greater than BMH_PATLEN_MAX. diff --git a/js/src/jsxdrapi.c b/js/src/jsxdrapi.c index c5c3038227b2..0eb45eba76b0 100644 --- a/js/src/jsxdrapi.c +++ b/js/src/jsxdrapi.c @@ -611,7 +611,6 @@ js_XDRAtom(JSXDRState *xdr, JSAtom **atomp) jsval v; uint32 type; jsdouble d; - JSAtom *atom; if (xdr->mode == JSXDR_ENCODE) { v = ATOM_KEY(*atomp); @@ -630,17 +629,12 @@ js_XDRAtom(JSXDRState *xdr, JSAtom **atomp) if (type == JSVAL_DOUBLE) { if (!XDRDoubleValue(xdr, &d)) return JS_FALSE; - atom = js_AtomizeDouble(xdr->cx, d); - } else { - if (!XDRValueBody(xdr, type, &v)) - return JS_FALSE; - atom = js_AtomizePrimitiveValue(xdr->cx, v); + *atomp = js_AtomizeDouble(xdr->cx, d); + return *atomp != NULL; } - if (!atom) - return JS_FALSE; - *atomp = atom; - return JS_TRUE; + return XDRValueBody(xdr, type, &v) && + js_AtomizePrimitiveValue(xdr->cx, v, atomp); } extern JSBool diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index f9f499706933..f65c6cd1b1cd 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -649,17 +649,6 @@ struct ContextCallbackItem : public JSTracer void NoteJSChild(JSTracer *trc, void *thing, uint32 kind) { - if(kind == JSTRACE_ATOM) - { - JSAtom *atom = (JSAtom *)thing; - jsval v = ATOM_KEY(atom); - if(!JSVAL_IS_PRIMITIVE(v)) - { - thing = JSVAL_TO_GCTHING(v); - kind = JSTRACE_OBJECT; - } - } - if(kind == JSTRACE_OBJECT || kind == JSTRACE_NAMESPACE || kind == JSTRACE_QNAME || kind == JSTRACE_XML) {