Bug 387481, bug 389063: multithreading atom fixes and cleanups. r=brendan

git-svn-id: svn://10.0.0.236/trunk@231579 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
igor%mir2.org 2007-08-07 07:29:32 +00:00
parent 9a22d5458f
commit 8c7384c4bc
12 changed files with 352 additions and 378 deletions

View File

@ -1019,7 +1019,7 @@ SrcNotes(JSContext *cx, JSScript *script)
const char *bytes;
atomIndex = (jsatomid) js_GetSrcNoteOffset(sn, 0);
atom = js_GetAtom(cx, &script->atomMap, atomIndex);
JS_GET_SCRIPT_ATOM(script, atomIndex, atom);
bytes = js_AtomToPrintableString(cx, atom);
fprintf(gOutFile, " atom %u (%s)", (uintN)atomIndex, bytes);
break;
@ -1267,28 +1267,6 @@ Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
return JS_TRUE;
}
typedef struct DumpAtomArgs {
JSContext *cx;
FILE *fp;
} DumpAtomArgs;
static int
DumpAtom(JSHashEntry *he, int i, void *arg)
{
DumpAtomArgs *args = (DumpAtomArgs *)arg;
FILE *fp = args->fp;
JSAtom *atom = (JSAtom *)he;
fprintf(fp, "%3d %08x ", i, (uintN)he->keyHash);
if (ATOM_IS_STRING(atom))
fprintf(fp, "\"%s\"\n", js_AtomToPrintableString(args->cx, atom));
else if (ATOM_IS_INT(atom))
fprintf(fp, "%ld\n", (long)ATOM_TO_INT(atom));
else
fprintf(fp, "%.16g\n", *ATOM_TO_DOUBLE(atom));
return HT_ENUMERATE_NEXT;
}
static void
DumpScope(JSContext *cx, JSObject *obj, FILE *fp)
{
@ -1347,19 +1325,7 @@ DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JS_DumpArenaStats(stdout);
#endif
} else if (strcmp(bytes, "atom") == 0) {
DumpAtomArgs args;
fprintf(gOutFile, "\natom table contents:\n");
args.cx = cx;
args.fp = stdout;
JS_HashTableEnumerateEntries(cx->runtime->atomState.table,
DumpAtom,
&args);
#ifdef HASHMETER
JS_HashTableDumpMeter(cx->runtime->atomState.table,
DumpAtom,
stdout);
#endif
js_DumpAtoms(cx, gOutFile);
} else if (strcmp(bytes, "global") == 0) {
DumpScope(cx, cx->globalObject, stdout);
} else {

View File

@ -648,7 +648,7 @@ JS_GetTypeName(JSContext *cx, JSType type)
{
if ((uintN)type >= (uintN)JSTYPE_LIMIT)
return NULL;
return js_type_strs[type];
return JS_TYPE_STR(type);
}
/************************************************************************/
@ -703,6 +703,8 @@ JS_NewRuntime(uint32 maxbytes)
if (!js_InitGC(rt, maxbytes))
goto bad;
if (!js_InitAtomState(rt))
goto bad;
#ifdef JS_THREADSAFE
if (!js_InitThreadPrivateIndex(js_ThreadDestructorCB))
goto bad;
@ -764,7 +766,7 @@ JS_DestroyRuntime(JSRuntime *rt)
#endif
js_FreeRuntimeScriptState(rt);
js_FinishAtomState(&rt->atomState);
js_FinishAtomState(rt);
js_FinishGC(rt);
#ifdef JS_THREADSAFE
if (rt->gcLock)

View File

@ -63,38 +63,94 @@ js_AtomToPrintableString(JSContext *cx, JSAtom *atom)
return js_ValueToPrintableString(cx, ATOM_KEY(atom));
}
/*
* Keep this in sync with jspubtd.h -- an assertion below will insist that
* its length match the JSType enum's JSTYPE_LIMIT limit value.
*/
const char *js_type_strs[] = {
"undefined",
js_object_str,
"function",
"string",
"number",
"boolean",
"null",
"xml",
};
JS_STATIC_ASSERT(JSTYPE_LIMIT ==
sizeof js_type_strs / sizeof js_type_strs[0]);
const char *js_boolean_strs[] = {
js_false_str,
js_true_str
};
#define JS_PROTO(name,code,init) const char js_##name##_str[] = #name;
#include "jsproto.tbl"
#undef JS_PROTO
const char *js_proto_strs[JSProto_LIMIT] = {
/*
* Names for common atoms defined in JSAtomState starting from
* JSAtomState.emptyAtom until JSAtomState.lazy.
*
* The elements of the array after the first empty string define strings
* corresponding to JSType enumerators from jspubtd.h and to two boolean
* literals, false and true. The following assert insists that JSType defines
* exactly 8 types.
*/
JS_STATIC_ASSERT(JSTYPE_LIMIT == 8);
const char *const js_common_atom_names[] = {
"", /* emptyAtom */
js_undefined_str, /* typeAtoms[JSTYPE_VOID] */
js_object_str, /* typeAtoms[JSTYPE_OBJECT] */
js_function_str, /* typeAtoms[JSTYPE_FUNCTION] */
"string", /* typeAtoms[JSTYPE_STRING] */
"number", /* typeAtoms[JSTYPE_NUMBER] */
"boolean", /* typeAtoms[JSTYPE_BOOLEAN] */
js_null_str, /* typeAtoms[JSTYPE_NULL] */
"xml", /* typeAtoms[JSTYPE_XML] */
js_false_str, /* booleanAtoms[0] */
js_true_str, /* booleanAtoms[1] */
js_null_str, /* nullAtom */
#define JS_PROTO(name,code,init) js_##name##_str,
#include "jsproto.tbl"
#undef JS_PROTO
js_anonymous_str, /* anonymousAtom */
js_arguments_str, /* argumentsAtom */
js_arity_str, /* arityAtom */
js_callee_str, /* calleeAtom */
js_caller_str, /* callerAtom */
js_class_prototype_str, /* classPrototypeAtom */
js_constructor_str, /* constructorAtom */
js_count_str, /* countAtom */
js_each_str, /* eachAtom */
js_eval_str, /* evalAtom */
js_fileName_str, /* fileNameAtom */
js_get_str, /* getAtom */
js_getter_str, /* getterAtom */
js_index_str, /* indexAtom */
js_input_str, /* inputAtom */
js_iterator_str, /* iteratorAtom */
js_length_str, /* lengthAtom */
js_lineNumber_str, /* lineNumberAtom */
js_message_str, /* messageAtom */
js_name_str, /* nameAtom */
js_next_str, /* nextAtom */
js_noSuchMethod_str, /* noSuchMethodAtom */
js_parent_str, /* parentAtom */
js_proto_str, /* protoAtom */
js_set_str, /* setAtom */
js_setter_str, /* setterAtom */
js_stack_str, /* stackAtom */
js_toLocaleString_str, /* toLocaleStringAtom */
js_toSource_str, /* toSourceAtom */
js_toString_str, /* toStringAtom */
js_valueOf_str, /* valueOfAtom */
"(void 0)", /* void0Atom */
#if JS_HAS_XML_SUPPORT
js_etago_str, /* etagoAtom */
js_namespace_str, /* namespaceAtom */
js_ptagc_str, /* ptagcAtom */
js_qualifier_str, /* qualifierAtom */
js_space_str, /* spaceAtom */
js_stago_str, /* stagoAtom */
js_star_str, /* starAtom */
js_starQualifier_str, /* starQualifierAtom */
js_tagc_str, /* tagcAtom */
js_xml_str, /* xmlAtom */
#endif
#ifdef NARCISSUS
js_call_str, /* callAtom */
js_construct_str, /* constructAtom */
js_hasInstance_str, /* hasInstanceAtom */
js_ExecutionContext_str, /* ExecutionContextAtom */
js_current_str, /* currentAtom */
#endif
};
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) * sizeof(JSAtom *) ==
LAZY_ATOM_OFFSET_START - ATOM_OFFSET_START);
const char js_anonymous_str[] = "anonymous";
const char js_arguments_str[] = "arguments";
@ -127,6 +183,7 @@ const char js_stack_str[] = "stack";
const char js_toSource_str[] = "toSource";
const char js_toString_str[] = "toString";
const char js_toLocaleString_str[] = "toLocaleString";
const char js_undefined_str[] = "undefined";
const char js_valueOf_str[] = "valueOf";
#if JS_HAS_XML_SUPPORT
@ -221,16 +278,11 @@ JS_STATIC_DLL_CALLBACK(JSHashEntry *)
js_alloc_atom(void *priv, const void *key)
{
JSAtom *atom;
#ifdef JS_THREADSAFE
JSAtomState *state = (JSAtomState *) priv;
#endif
atom = (JSAtom *) malloc(sizeof(JSAtom));
if (!atom)
return NULL;
#ifdef JS_THREADSAFE
state->tablegen++;
#endif
((JSAtomState *)priv)->tablegen++;
atom->entry.key = key;
atom->entry.value = NULL;
atom->flags = 0;
@ -242,9 +294,7 @@ js_free_atom(void *priv, JSHashEntry *he, uintN flag)
{
if (flag != HT_FREE_ENTRY)
return;
#ifdef JS_THREADSAFE
((JSAtomState *)priv)->tablegen++;
#endif
free(he);
}
@ -256,133 +306,33 @@ static JSHashAllocOps atom_alloc_ops = {
#define JS_ATOM_HASH_SIZE 1024
JSBool
js_InitAtomState(JSContext *cx, JSAtomState *state)
js_InitAtomState(JSRuntime *rt)
{
JSAtomState *state = &rt->atomState;
/*
* The caller must zero the state before calling this function.
*/
JS_ASSERT(!state->table);
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) {
JS_ReportOutOfMemory(cx);
if (!state->table)
return JS_FALSE;
}
state->runtime = cx->runtime;
#ifdef JS_THREADSAFE
js_InitLock(&state->lock);
state->tablegen = 0;
#endif
if (!js_InitPinnedAtoms(cx, state)) {
js_FreeAtomState(cx, state);
return JS_FALSE;
}
return JS_TRUE;
}
JSBool
js_InitPinnedAtoms(JSContext *cx, JSAtomState *state)
{
uintN i;
#define FROB(lval,str) \
JS_BEGIN_MACRO \
if (!(state->lval = js_Atomize(cx, str, strlen(str), ATOM_PINNED))) \
return JS_FALSE; \
JS_END_MACRO
FROB(emptyAtom, "");
for (i = 0; i < JSTYPE_LIMIT; i++)
FROB(typeAtoms[i], js_type_strs[i]);
for (i = 0; i < JSProto_LIMIT; i++)
FROB(classAtoms[i], js_proto_strs[i]);
FROB(booleanAtoms[0], js_false_str);
FROB(booleanAtoms[1], js_true_str);
FROB(nullAtom, js_null_str);
FROB(anonymousAtom, js_anonymous_str);
FROB(argumentsAtom, js_arguments_str);
FROB(arityAtom, js_arity_str);
FROB(calleeAtom, js_callee_str);
FROB(callerAtom, js_caller_str);
FROB(classPrototypeAtom, js_class_prototype_str);
FROB(constructorAtom, js_constructor_str);
FROB(countAtom, js_count_str);
FROB(eachAtom, js_each_str);
FROB(evalAtom, js_eval_str);
FROB(fileNameAtom, js_fileName_str);
FROB(getAtom, js_get_str);
FROB(getterAtom, js_getter_str);
FROB(indexAtom, js_index_str);
FROB(inputAtom, js_input_str);
FROB(iteratorAtom, js_iterator_str);
FROB(lengthAtom, js_length_str);
FROB(lineNumberAtom, js_lineNumber_str);
FROB(messageAtom, js_message_str);
FROB(nameAtom, js_name_str);
FROB(nextAtom, js_next_str);
FROB(noSuchMethodAtom, js_noSuchMethod_str);
FROB(parentAtom, js_parent_str);
FROB(protoAtom, js_proto_str);
FROB(setAtom, js_set_str);
FROB(setterAtom, js_setter_str);
FROB(stackAtom, js_stack_str);
FROB(toSourceAtom, js_toSource_str);
FROB(toStringAtom, js_toString_str);
FROB(toLocaleStringAtom, js_toLocaleString_str);
FROB(valueOfAtom, js_valueOf_str);
FROB(void0Atom, "(void 0)");
#if JS_HAS_XML_SUPPORT
FROB(etagoAtom, js_etago_str);
FROB(namespaceAtom, js_namespace_str);
FROB(ptagcAtom, js_ptagc_str);
FROB(qualifierAtom, js_qualifier_str);
FROB(spaceAtom, js_space_str);
FROB(stagoAtom, js_stago_str);
FROB(starAtom, js_star_str);
FROB(starQualifierAtom, js_starQualifier_str);
FROB(tagcAtom, js_tagc_str);
FROB(xmlAtom, js_xml_str);
#endif
#if JS_HAS_GENERATORS
FROB(closeAtom, js_close_str);
#endif
#ifdef NARCISSUS
FROB(callAtom, js_call_str);
FROB(constructAtom, js_construct_str);
FROB(hasInstanceAtom, js_hasInstance_str);
FROB(ExecutionContextAtom, js_ExecutionContext_str);
FROB(currentAtom, js_current_str);
#endif
#undef FROB
memset(&state->lazy, 0, sizeof state->lazy);
return JS_TRUE;
}
/* NB: cx unused; js_FinishAtomState calls us with null cx. */
void
js_FreeAtomState(JSContext *cx, JSAtomState *state)
{
if (state->table)
JS_HashTableDestroy(state->table);
#ifdef JS_THREADSAFE
js_FinishLock(&state->lock);
#endif
memset(state, 0, sizeof *state);
}
JS_STATIC_DLL_CALLBACK(intN)
js_atom_uninterner(JSHashEntry *he, intN i, void *arg)
{
JSAtom *atom;
JSRuntime *rt;
JSRuntime *rt;
atom = (JSAtom *)he;
rt = (JSRuntime *)arg;
@ -392,13 +342,68 @@ js_atom_uninterner(JSHashEntry *he, intN i, void *arg)
}
void
js_FinishAtomState(JSAtomState *state)
js_FinishAtomState(JSRuntime *rt)
{
if (!state->table)
JSAtomState *state = &rt->atomState;
if (!state->table) {
/*
* state->table is null when JS_NewRuntime fails and calls
* JS_DestroyRuntime on a partially initialized runtime.
*/
return;
JS_HashTableEnumerateEntries(state->table, js_atom_uninterner,
state->runtime);
js_FreeAtomState(NULL, state);
}
JS_HashTableEnumerateEntries(state->table, js_atom_uninterner, rt);
JS_HashTableDestroy(state->table);
#ifdef JS_THREADSAFE
js_FinishLock(&state->lock);
#endif
#ifdef DEBUG
memset(state, JS_FREE_PATTERN, sizeof *state);
#endif
}
JSBool
js_InitCommonAtoms(JSContext *cx)
{
JSAtomState *state = &cx->runtime->atomState;
uintN i;
JSAtom **atoms;
atoms = (JSAtom **)((uint8 *)state + ATOM_OFFSET_START);
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);
if (!*atoms)
return JS_FALSE;
}
JS_ASSERT((uint8 *)atoms - (uint8 *)state == LAZY_ATOM_OFFSET_START);
memset(atoms, 0, ATOM_OFFSET_LIMIT - LAZY_ATOM_OFFSET_START);
return JS_TRUE;
}
JS_STATIC_DLL_CALLBACK(intN)
js_atom_unpinner(JSHashEntry *he, intN i, void *arg)
{
JSAtom *atom;
atom = (JSAtom *)he;
atom->flags &= ~ATOM_PINNED;
return HT_ENUMERATE_NEXT;
}
void
js_FinishCommonAtoms(JSContext *cx)
{
JSAtomState *state = &cx->runtime->atomState;
JS_HashTableEnumerateEntries(state->table, js_atom_unpinner, NULL);
#ifdef DEBUG
memset((uint8 *)state + ATOM_OFFSET_START, JS_FREE_PATTERN,
ATOM_OFFSET_LIMIT - ATOM_OFFSET_START);
#endif
}
void
@ -456,13 +461,10 @@ JS_STATIC_DLL_CALLBACK(intN)
js_atom_sweeper(JSHashEntry *he, intN i, void *arg)
{
JSAtom *atom;
JSAtomState *state;
atom = (JSAtom *)he;
if (atom->flags & ATOM_MARK) {
atom->flags &= ~ATOM_MARK;
state = (JSAtomState *)arg;
state->liveAtoms++;
return HT_ENUMERATE_NEXT;
}
JS_ASSERT((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) == 0);
@ -472,28 +474,11 @@ js_atom_sweeper(JSHashEntry *he, intN i, void *arg)
}
void
js_SweepAtomState(JSAtomState *state)
js_SweepAtomState(JSContext *cx)
{
state->liveAtoms = 0;
if (state->table)
JS_HashTableEnumerateEntries(state->table, js_atom_sweeper, state);
}
JSAtomState *state = &cx->runtime->atomState;
JS_STATIC_DLL_CALLBACK(intN)
js_atom_unpinner(JSHashEntry *he, intN i, void *arg)
{
JSAtom *atom;
atom = (JSAtom *)he;
atom->flags &= ~ATOM_PINNED;
return HT_ENUMERATE_NEXT;
}
void
js_UnpinPinnedAtoms(JSAtomState *state)
{
if (state->table)
JS_HashTableEnumerateEntries(state->table, js_atom_unpinner, NULL);
JS_HashTableEnumerateEntries(state->table, js_atom_sweeper, NULL);
}
static JSAtom *
@ -511,15 +496,14 @@ AtomizeHashedKey(JSContext *cx, jsval key, JSHashNumber keyHash)
if ((he = *hep) == NULL) {
he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL);
if (!he) {
JS_UNLOCK(&state->lock,cx);
JS_ReportOutOfMemory(cx);
atom = NULL;
goto out;
return NULL;
}
}
atom = (JSAtom *)he;
cx->weakRoots.lastAtom = atom;
out:
JS_UNLOCK(&state->lock,cx);
return atom;
}
@ -531,51 +515,49 @@ out:
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;
uint32 gen;
JSAtom *atom;
char buf[2 * ALIGNMENT(double)];
dp = ALIGN(buf, double);
*dp = d;
keyHash = HASH_DOUBLE(dp);
key = DOUBLE_TO_JSVAL(dp);
state = &cx->runtime->atomState;
JS_LOCK(&state->lock, cx);
table = state->table;
JS_LOCK(&state->lock, cx);
hep = JS_HashTableRawLookup(table, keyHash, (void *)key);
if ((he = *hep) == NULL) {
#ifdef JS_THREADSAFE
uint32 gen = state->tablegen;
#endif
JS_UNLOCK(&state->lock,cx);
gen = state->tablegen;
JS_UNLOCK(&state->lock, cx);
if (!js_NewDoubleValue(cx, d, &key))
return NULL;
JS_LOCK(&state->lock, cx);
#ifdef JS_THREADSAFE
if (state->tablegen != gen) {
hep = JS_HashTableRawLookup(table, keyHash, (void *)key);
if ((he = *hep) != NULL) {
atom = (JSAtom *)he;
goto out;
}
if ((he = *hep) != NULL)
goto finish;
}
#endif
he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL);
if (!he) {
JS_UNLOCK(&state->lock, cx);
JS_ReportOutOfMemory(cx);
atom = NULL;
goto out;
return NULL;
}
}
finish:
atom = (JSAtom *)he;
cx->weakRoots.lastAtom = atom;
out:
JS_UNLOCK(&state->lock,cx);
return atom;
}
@ -590,66 +572,66 @@ JSAtom *
js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
{
JSHashNumber keyHash;
jsval key;
void *key;
JSAtomState *state;
JSHashTable *table;
JSHashEntry *he, **hep;
uint32 gen;
JSString *hashed;
JSAtom *atom;
keyHash = js_HashString(str);
if (flags & ATOM_HIDDEN)
keyHash ^= HIDDEN_ATOM_SUBSPACE_KEYHASH;
key = STRING_TO_JSVAL(str);
key = (void *)STRING_TO_JSVAL(str);
state = &cx->runtime->atomState;
JS_LOCK(&state->lock, cx);
table = state->table;
hep = JS_HashTableRawLookup(table, keyHash, (void *)key);
JS_LOCK(&state->lock, cx);
hep = JS_HashTableRawLookup(table, keyHash, key);
if ((he = *hep) == NULL) {
#ifdef JS_THREADSAFE
uint32 gen = state->tablegen;
gen = state->tablegen;
JS_UNLOCK(&state->lock, cx);
#endif
if (flags & ATOM_TMPSTR) {
str = (flags & ATOM_NOCOPY)
? js_NewString(cx, str->chars, str->length, 0)
: js_NewStringCopyN(cx, str->chars, str->length);
if (!str)
return NULL;
key = STRING_TO_JSVAL(str);
if (flags & ATOM_NOCOPY) {
hashed = js_NewString(cx, str->chars, str->length, 0);
if (!hashed)
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)
return NULL;
}
key = (void *)STRING_TO_JSVAL(hashed);
} else {
JS_ASSERT((flags & ATOM_NOCOPY) == 0);
if (!JS_MakeStringImmutable(cx, str))
return NULL;
}
#ifdef JS_THREADSAFE
JS_LOCK(&state->lock, cx);
if (state->tablegen != gen) {
hep = JS_HashTableRawLookup(table, keyHash, (void *)key);
if ((he = *hep) != NULL) {
atom = (JSAtom *)he;
if (flags & ATOM_NOCOPY)
str->chars = NULL;
goto out;
}
hep = JS_HashTableRawLookup(table, keyHash, key);
if ((he = *hep) != NULL)
goto finish;
}
#endif
he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL);
he = JS_HashTableRawAdd(table, hep, keyHash, key, NULL);
if (!he) {
if (flags & ATOM_NOCOPY)
str->chars = NULL;
JS_UNLOCK(&state->lock, cx);
JS_ReportOutOfMemory(cx);
atom = NULL;
goto out;
return NULL;
}
}
finish:
atom = (JSAtom *)he;
atom->flags |= flags & (ATOM_PINNED | ATOM_INTERNED | ATOM_HIDDEN);
cx->weakRoots.lastAtom = atom;
out:
JS_UNLOCK(&state->lock,cx);
JS_UNLOCK(&state->lock, cx);
return atom;
}
@ -689,7 +671,7 @@ js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags)
str->chars = chars;
str->length = inflatedLength;
atom = js_AtomizeString(cx, str, ATOM_TMPSTR | flags);
if (chars != inflated && (!atom || ATOM_TO_STRING(atom)->chars != chars))
if (chars != inflated && str->chars)
JS_free(cx, chars);
return atom;
}
@ -753,6 +735,39 @@ js_ValueToStringAtom(JSContext *cx, jsval v)
return js_AtomizeString(cx, str, 0);
}
#ifdef DEBUG
JS_STATIC_DLL_CALLBACK(int)
atom_dumper(JSHashEntry *he, int i, void *arg)
{
FILE *fp = (FILE *)arg;
JSAtom *atom = (JSAtom *)he;
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));
putc('\n', fp);
return HT_ENUMERATE_NEXT;
}
JS_FRIEND_API(void)
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);
#endif
}
#endif
JS_STATIC_DLL_CALLBACK(JSHashNumber)
js_hash_atom_ptr(const void *key)
{
@ -863,25 +878,6 @@ js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al)
return ale;
}
JS_FRIEND_API(JSAtom *)
js_GetAtom(JSContext *cx, JSAtomMap *map, jsatomid i)
{
JSAtom *atom;
static JSAtom dummy;
JS_ASSERT(map->vector && i < map->length);
if (!map->vector || i >= map->length) {
char numBuf[12];
JS_snprintf(numBuf, sizeof numBuf, "%lu", (unsigned long)i);
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_ATOMIC_NUMBER, numBuf);
return &dummy;
}
atom = map->vector[i];
JS_ASSERT(atom);
return atom;
}
JS_STATIC_DLL_CALLBACK(intN)
js_map_atom(JSHashEntry *he, intN i, void *arg)
{

View File

@ -43,6 +43,7 @@
* JS atom table.
*/
#include <stddef.h>
#include "jsconfig.h"
#include "jstypes.h"
#include "jshash.h" /* Added by JSIFY */
#include "jsapi.h"
@ -155,9 +156,23 @@ struct JSAtomMap {
};
struct JSAtomState {
JSRuntime *runtime; /* runtime that owns us */
JSHashTable *table; /* hash table containing all atoms */
jsatomid liveAtoms; /* number of live atoms after last GC */
uint32 tablegen; /* number of atoms mutations to
optimize hashing */
#ifdef JS_THREADSAFE
JSThinLock lock;
#endif
/*
* From this point until the end of struct definition the struct must
* contain only JSAtom fields. We use this to access the storage occupied
* by the common atoms in js_FinishCommonAtoms.
*
* js_common_atom_names defined in jsatom.c contains C strings for atoms
* in the order of atom fields here. Therefore you must update that array
* if you change member order here.
*/
/* The rt->emptyString atom, see jsstr.c's js_InitRuntimeStringState. */
JSAtom *emptyAtom;
@ -177,11 +192,9 @@ struct JSAtomState {
JSAtom *calleeAtom;
JSAtom *callerAtom;
JSAtom *classPrototypeAtom;
JSAtom *closeAtom;
JSAtom *constructorAtom;
JSAtom *countAtom;
JSAtom *eachAtom;
JSAtom *etagoAtom;
JSAtom *evalAtom;
JSAtom *fileNameAtom;
JSAtom *getAtom;
@ -193,27 +206,39 @@ struct JSAtomState {
JSAtom *lineNumberAtom;
JSAtom *messageAtom;
JSAtom *nameAtom;
JSAtom *namespaceAtom;
JSAtom *nextAtom;
JSAtom *noSuchMethodAtom;
JSAtom *parentAtom;
JSAtom *protoAtom;
JSAtom *ptagcAtom;
JSAtom *qualifierAtom;
JSAtom *setAtom;
JSAtom *setterAtom;
JSAtom *spaceAtom;
JSAtom *stackAtom;
JSAtom *stagoAtom;
JSAtom *starAtom;
JSAtom *starQualifierAtom;
JSAtom *tagcAtom;
JSAtom *toLocaleStringAtom;
JSAtom *toSourceAtom;
JSAtom *toStringAtom;
JSAtom *valueOfAtom;
JSAtom *void0Atom;
#if JS_HAS_XML_SUPPORT
JSAtom *etagoAtom;
JSAtom *namespaceAtom;
JSAtom *ptagcAtom;
JSAtom *qualifierAtom;
JSAtom *spaceAtom;
JSAtom *stagoAtom;
JSAtom *starAtom;
JSAtom *starQualifierAtom;
JSAtom *tagcAtom;
JSAtom *xmlAtom;
#endif
#ifdef NARCISSUS
JSAtom *callAtom;
JSAtom *constructAtom;
JSAtom *hasInstanceAtom;
JSAtom *ExecutionContextAtom;
JSAtom *currentAtom;
#endif
/* Less frequently used atoms, pinned lazily by JS_ResolveStandardClass. */
struct {
@ -243,20 +268,16 @@ struct JSAtomState {
JSAtom *unwatchAtom;
JSAtom *watchAtom;
} lazy;
#ifdef JS_THREADSAFE
JSThinLock lock;
volatile uint32 tablegen;
#endif
#ifdef NARCISSUS
JSAtom *callAtom;
JSAtom *constructAtom;
JSAtom *hasInstanceAtom;
JSAtom *ExecutionContextAtom;
JSAtom *currentAtom;
#endif
};
#define ATOM_OFFSET_START offsetof(JSAtomState, emptyAtom)
#define LAZY_ATOM_OFFSET_START offsetof(JSAtomState, lazy)
#define ATOM_OFFSET_LIMIT (sizeof(JSAtomState))
/* 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);
#define ATOM_OFFSET(name) offsetof(JSAtomState, name##Atom)
#define OFFSET_TO_ATOM(rt,off) (*(JSAtom **)((char*)&(rt)->atomState + (off)))
#define CLASS_ATOM_OFFSET(name) offsetof(JSAtomState,classAtoms[JSProto_##name])
@ -264,11 +285,22 @@ struct JSAtomState {
#define CLASS_ATOM(cx,name) \
((cx)->runtime->atomState.classAtoms[JSProto_##name])
/* Well-known predefined strings and their atoms. */
extern const char *js_type_strs[];
extern const char *js_boolean_strs[];
extern const char *js_proto_strs[];
extern const char *const js_common_atom_names[];
/*
* Macros to access C strings for JSType and boolean literals together with
* checks that type names and booleans starts from index 1 and 1+JSTYPE_LIMIT
* correspondingly.
*/
#define JS_TYPE_STR(type) (js_common_atom_names[1 + (type)])
#define JS_BOOLEAN_STR(type) (js_common_atom_names[1 + JSTYPE_LIMIT + (type)])
JS_STATIC_ASSERT(1 * sizeof(JSAtom *) ==
offsetof(JSAtomState, typeAtoms) - ATOM_OFFSET_START);
JS_STATIC_ASSERT((1 + JSTYPE_LIMIT) * sizeof(JSAtom *) ==
offsetof(JSAtomState, booleanAtoms) - ATOM_OFFSET_START);
/* Well-known predefined C strings. */
#define JS_PROTO(name,code,init) extern const char js_##name##_str[];
#include "jsproto.tbl"
#undef JS_PROTO
@ -315,6 +347,7 @@ extern const char js_tagc_str[];
extern const char js_toSource_str[];
extern const char js_toString_str[];
extern const char js_toLocaleString_str[];
extern const char js_undefined_str[];
extern const char js_valueOf_str[];
extern const char js_xml_str[];
@ -327,33 +360,19 @@ extern const char js_current_str[];
#endif
/*
* Initialize atom state. Return true on success, false with an out of
* memory error report on failure.
* Initialize atom state. Return true on success, false on failure to allocate
* memory. The caller must zero rt->atomState before calling this function and
* only call it after js_InitGC successfully returns.
*/
extern JSBool
js_InitAtomState(JSContext *cx, JSAtomState *state);
js_InitAtomState(JSRuntime *rt);
/*
* Free and clear atom state (except for any interned string atoms).
* Free and clear atom state including any interned string atoms. This
* function must be called before js_FinishGC.
*/
extern void
js_FreeAtomState(JSContext *cx, JSAtomState *state);
/*
* Interned strings are atoms that live until state's runtime is destroyed.
* This function frees all interned string atoms, and then frees and clears
* state's members (just as js_FreeAtomState does), unless there aren't any
* interned strings in state -- in which case state must be "free" already.
*
* NB: js_FreeAtomState is called for each "last" context being destroyed in
* a runtime, where there may yet be another context created in the runtime;
* whereas js_FinishAtomState is called from JS_DestroyRuntime, when we know
* that no more contexts will be created. Thus we minimize garbage during
* context-free episodes on a runtime, while preserving atoms created by the
* JS_Intern*String APIs for the life of the runtime.
*/
extern void
js_FinishAtomState(JSAtomState *state);
js_FinishAtomState(JSRuntime *rt);
/*
* Atom tracing and garbage collection hooks.
@ -366,13 +385,13 @@ extern void
js_TraceLockedAtoms(JSTracer *trc, JSBool allAtoms);
extern void
js_SweepAtomState(JSAtomState *state);
js_SweepAtomState(JSContext *cx);
extern JSBool
js_InitPinnedAtoms(JSContext *cx, JSAtomState *state);
js_InitCommonAtoms(JSContext *cx);
extern void
js_UnpinPinnedAtoms(JSAtomState *state);
js_FinishCommonAtoms(JSContext *cx);
/*
* Find or create the atom for a double value. Return null on failure to
@ -413,18 +432,19 @@ js_AtomizePrimitiveValue(JSContext *cx, jsval v);
extern JSAtom *
js_ValueToStringAtom(JSContext *cx, jsval v);
#ifdef DEBUG
extern JS_FRIEND_API(void)
js_DumpAtoms(JSContext *cx, FILE *fp);
#endif
/*
* Assign atom an index and insert it on al.
*/
extern JSAtomListElement *
js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al);
/*
* Get the atom with index i from map.
*/
extern JS_FRIEND_API(JSAtom *)
js_GetAtom(JSContext *cx, JSAtomMap *map, jsatomid i);
/*
* For all unmapped atoms recorded in al, add a mapping from the atom's index
* to its address. map->length must already be set to the number of atoms in

View File

@ -77,7 +77,7 @@ bool_toSource(JSContext *cx, uintN argc, jsval *vp)
JS_ASSERT(JSVAL_IS_BOOLEAN(v));
JS_snprintf(buf, sizeof buf, "(new %s(%s))",
js_BooleanClass.name,
js_boolean_strs[JSVAL_TO_BOOLEAN(v) ? 1 : 0]);
JS_BOOLEAN_STR(JSVAL_TO_BOOLEAN(v)));
str = JS_NewStringCopyZ(cx, buf);
if (!str)
return JS_FALSE;

View File

@ -279,16 +279,13 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
#ifdef JS_THREADSAFE
JS_BeginRequest(cx);
#endif
ok = js_InitCommonAtoms(cx);
/*
* Both atomState and the scriptFilenameTable may be left over from a
* previous episode of non-zero contexts alive in rt, so don't re-init
* either table if it's not necessary. Just repopulate atomState with
* well-known internal atoms, and with the reserved identifiers added
* by the scanner.
* scriptFilenameTable may be left over from a previous episode of
* non-zero contexts alive in rt, so don't re-init the table if it's
* not necessary.
*/
ok = (rt->atomState.liveAtoms == 0)
? js_InitAtomState(cx, &rt->atomState)
: js_InitPinnedAtoms(cx, &rt->atomState);
if (ok && !rt->scriptFilenameTable)
ok = js_InitRuntimeScriptState(rt);
if (ok)
@ -373,8 +370,8 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
js_FinishRuntimeNumberState(cx);
js_FinishRuntimeStringState(cx);
/* Unpin all pinned atoms before final GC. */
js_UnpinPinnedAtoms(&rt->atomState);
/* Unpin all common atoms before final GC. */
js_FinishCommonAtoms(cx);
/* Clear debugging state to remove GC roots. */
JS_ClearAllTraps(cx);
@ -408,11 +405,7 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
if (last) {
js_GC(cx, GC_LAST_CONTEXT);
/* Try to free atom state, now that no unrooted scripts survive. */
if (rt->atomState.liveAtoms == 0)
js_FreeAtomState(cx, &rt->atomState);
/* Also free the script filename table if it exists and is empty. */
/* Free the script filename table if it exists and is empty. */
if (rt->scriptFilenameTable && rt->scriptFilenameTable->nentries == 0)
js_FinishRuntimeScriptState(rt);

View File

@ -224,9 +224,6 @@ struct JSRuntime {
JSTraceDataOp gcExtraRootsTraceOp;
void *gcExtraRootsData;
/* Literal table maintained by jsatom.c functions. */
JSAtomState atomState;
/* Random number generator state, used by jsmath.c. */
JSBool rngInitialized;
int64 rngMultiplier;
@ -386,6 +383,9 @@ struct JSRuntime {
#define JS_GSN_CACHE(cx) ((cx)->runtime->gsnCache)
#endif
/* Literal table maintained by jsatom.c functions. */
JSAtomState atomState;
#ifdef DEBUG
/* Function invocation metering. */
jsrefcount inlineCalls;

View File

@ -2455,7 +2455,7 @@ restart:
* referenced from dead property ids.
*/
js_SweepScopeProperties(cx);
js_SweepAtomState(&rt->atomState);
js_SweepAtomState(cx);
/*
* Sweep script filenames after sweeping functions in the generic loop

View File

@ -697,7 +697,7 @@ typedef struct CallKey {
/* Compensate for typeof null == "object" brain damage. */
#define JSTYPE_NULL JSTYPE_LIMIT
#define TYPEOF(cx,v) (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v))
#define TYPENAME(t) (((t) == JSTYPE_NULL) ? js_null_str : js_type_strs[t])
#define TYPENAME(t) (((t) == JSTYPE_NULL) ? js_null_str : JS_TYPE_STR(t))
#define NTYPEHIST (JSTYPE_LIMIT + 1)
typedef struct CallValue {
@ -970,13 +970,13 @@ LogCall(JSContext *cx, jsval callee, uintN argc, jsval *argv)
cstr = "";
switch (TYPEOF(cx, argval)) {
case JSTYPE_VOID:
cstr = js_type_strs[JSTYPE_VOID];
cstr = js_undefined_str;
break;
case JSTYPE_NULL:
cstr = js_null_str;
break;
case JSTYPE_BOOLEAN:
cstr = js_boolean_strs[JSVAL_TO_BOOLEAN(argval)];
cstr = JS_BOOLEAN_STR(JSVAL_TO_BOOLEAN(argval));
break;
case JSTYPE_NUMBER:
if (JSVAL_IS_INT(argval)) {

View File

@ -3962,7 +3962,7 @@ js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
JSDVG_SEARCH_STACK, save, str,
(hint == JSTYPE_VOID)
? "primitive type"
: js_type_strs[hint]);
: JS_TYPE_STR(hint));
return JS_FALSE;
}
out:

View File

@ -1760,6 +1760,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
#define LOAD_REGEXP(PCOFF) \
GET_REGEXP_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
#define GET_SOURCE_NOTE_ATOM(sn, atom) \
JS_BEGIN_MACRO \
jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0); \
\
LOCAL_ASSERT(atomIndex_ < jp->script->atomMap.length); \
(atom) = jp->script->atomMap.vector[atomIndex_]; \
JS_END_MACRO
/*
* Get atom from jp->script's atom map, quote/escape its string appropriately
* into rval, and select fmt from the quoted and unquoted alternatives.
@ -2047,8 +2055,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
break;
case SRC_LABEL:
atom = js_GetAtom(cx, &jp->script->atomMap,
(jsatomid) js_GetSrcNoteOffset(sn, 0));
GET_SOURCE_NOTE_ATOM(sn, atom);
jp->indent -= 4;
rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
if (!rval)
@ -2059,8 +2066,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
break;
case SRC_LABELBRACE:
atom = js_GetAtom(cx, &jp->script->atomMap,
(jsatomid) js_GetSrcNoteOffset(sn, 0));
GET_SOURCE_NOTE_ATOM(sn, atom);
rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
if (!rval)
return NULL;
@ -2903,8 +2909,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
break;
case SRC_CONT2LABEL:
atom = js_GetAtom(cx, &jp->script->atomMap,
(jsatomid) js_GetSrcNoteOffset(sn, 0));
GET_SOURCE_NOTE_ATOM(sn, atom);
rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
if (!rval)
return NULL;
@ -2917,8 +2922,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
break;
case SRC_BREAK2LABEL:
atom = js_GetAtom(cx, &jp->script->atomMap,
(jsatomid) js_GetSrcNoteOffset(sn, 0));
GET_SOURCE_NOTE_ATOM(sn, atom);
rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
if (!rval)
return NULL;
@ -4000,10 +4004,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
sn = js_GetSrcNote(jp->script, pc2);
if (sn) {
LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL);
table[j].label =
js_GetAtom(cx, &jp->script->atomMap,
(jsatomid)
js_GetSrcNoteOffset(sn, 0));
GET_SOURCE_NOTE_ATOM(sn, table[j].label);
}
table[j].key = INT_TO_JSVAL(low + i);
table[j].offset = off2;
@ -4060,9 +4061,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
sn = js_GetSrcNote(jp->script, pc2);
if (sn) {
LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL);
table[k].label =
js_GetAtom(cx, &jp->script->atomMap, (jsatomid)
js_GetSrcNoteOffset(sn, 0));
GET_SOURCE_NOTE_ATOM(sn, table[k].label);
} else {
table[k].label = NULL;
}

View File

@ -2483,7 +2483,7 @@ GetNamespace(JSContext *cx, JSXMLQName *qn, const JSXMLArray *inScopeNSes)
qn->prefix
? js_ValueToPrintableString(cx,
STRING_TO_JSVAL(qn->prefix))
: js_type_strs[JSTYPE_VOID]);
: js_undefined_str);
return NULL;
}
@ -3047,9 +3047,7 @@ ToXMLString(JSContext *cx, jsval v)
if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_XML_CONVERSION,
js_type_strs[JSVAL_IS_NULL(v)
? JSTYPE_NULL
: JSTYPE_VOID]);
JSVAL_IS_NULL(v) ? js_null_str : js_undefined_str);
return NULL;
}