Back out Igor's patch from bug 412340 due to consistent orange on fxdbug-win32-tb.
authorreed@reedloden.com
Sat, 19 Jan 2008 00:06:45 -0800
changeset 10437 decaf4a24b05692220789e7ccecd29cd8ddd20b4
parent 10436 0c49c59c744fec71cdb2e6cd736023ce12b3531f
child 10438 8c42b0b4f72281742b73f2e1af87ad35b95d474c
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherderautoland@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs412340
milestone1.9b3pre
Back out Igor's patch from bug 412340 due to consistent orange on fxdbug-win32-tb.
js/src/js.c
js/src/jsapi.c
js/src/jsatom.c
js/src/jsatom.h
js/src/jsdbgapi.c
js/src/jsfun.c
js/src/jsinterp.c
js/src/jsobj.c
js/src/jsparse.c
js/src/jsstr.c
js/src/jsstr.h
js/src/jsxml.c
--- a/js/src/js.c
+++ b/js/src/js.c
@@ -1689,41 +1689,42 @@ DumpHeap(JSContext *cx, uintN argc, jsva
 }
 
 #endif /* DEBUG */
 
 #ifdef TEST_EXPORT
 static JSBool
 DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
-    jsid id;
+    JSAtom *atom;
     JSObject *obj2;
     JSProperty *prop;
     JSBool ok;
     uintN attrs;
 
     if (argc != 2) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_DOEXP_USAGE);
         return JS_FALSE;
     }
     if (!JS_ValueToObject(cx, argv[0], &obj))
         return JS_FALSE;
     argv[0] = OBJECT_TO_JSVAL(obj);
-    if (!js_ValueToStringId(cx, argv[1], &id))
+    atom = js_ValueToStringAtom(cx, argv[1]);
+    if (!atom)
         return JS_FALSE;
-    if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
+    if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop))
         return JS_FALSE;
     if (!prop) {
         ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
                                  JSPROP_EXPORTED, NULL);
     } else {
-        ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
+        ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs);
         if (ok) {
             attrs |= JSPROP_EXPORTED;
-            ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs);
+            ok = OBJ_SET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs);
         }
         OBJ_DROP_PROPERTY(cx, obj2, prop);
     }
     return ok;
 }
 #endif
 
 #ifdef TEST_CVTARGS
--- a/js/src/jsapi.c
+++ b/js/src/jsapi.c
@@ -2560,25 +2560,33 @@ JS_PUBLIC_API(void)
 JS_DestroyIdArray(JSContext *cx, JSIdArray *ida)
 {
     JS_free(cx, ida);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ValueToId(JSContext *cx, jsval v, jsid *idp)
 {
+    JSAtom *atom;
+
     CHECK_REQUEST(cx);
-    if (JSVAL_IS_INT(v))
+    if (JSVAL_IS_INT(v)) {
         *idp = INT_JSVAL_TO_JSID(v);
+    } else {
 #if JS_HAS_XML_SUPPORT
-    else if (!JSVAL_IS_PRIMITIVE(v))
-        *idp = OBJECT_JSVAL_TO_JSID(v);
+        if (JSVAL_IS_OBJECT(v)) {
+            *idp = OBJECT_JSVAL_TO_JSID(v);
+            return JS_TRUE;
+        }
 #endif
-    else
-        return js_ValueToStringId(cx, v, idp);
+        atom = js_ValueToStringAtom(cx, v);
+        if (!atom)
+            return JS_FALSE;
+        *idp = ATOM_TO_JSID(atom);
+    }
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_IdToValue(JSContext *cx, jsid id, jsval *vp)
 {
     CHECK_REQUEST(cx);
     *vp = ID_TO_VALUE(id);
@@ -5227,17 +5235,17 @@ JS_GetStringChars(JSString *str)
             memcpy(s, JSSTRDEP_CHARS(str), n * sizeof *s);
             s[n] = 0;
             JSSTRING_INIT(str, s, n);
         } else {
             s = JSSTRDEP_CHARS(str);
         }
     } else {
         JSSTRING_CLEAR_MUTABLE(str);
-        s = JSFLATSTR_CHARS(str);
+        s = str->u.chars;
     }
     return s;
 }
 
 JS_PUBLIC_API(size_t)
 JS_GetStringLength(JSString *str)
 {
     return JSSTRING_LENGTH(str);
--- a/js/src/jsatom.c
+++ b/js/src/jsatom.c
@@ -616,94 +616,76 @@ js_AtomizeDouble(JSContext *cx, jsdouble
     JS_UNLOCK(&state->lock,cx);
     JS_ReportOutOfMemory(cx);
     return NULL;
 }
 
 JSAtom *
 js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
 {
-    jsval v;
     JSAtomState *state;
     JSDHashTable *table;
     JSAtomHashEntry *entry;
     JSString *key;
     uint32 gen;
+    jsval v;
 
-    JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY)));
-    JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR);
-
+    JS_ASSERT((flags &
+               ~(ATOM_PINNED | ATOM_INTERNED | ATOM_TMPSTR | ATOM_NOCOPY))
+              == 0);
     state = &cx->runtime->atomState;
     table = &state->stringAtoms;
 
     JS_LOCK(&state->lock, cx);
     entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, str, JS_DHASH_ADD));
     if (!entry)
         goto failed_hash_add;
-    if (entry->keyAndFlags != 0) {
-        key = (JSString *)ATOM_ENTRY_KEY(entry);
-    } else {
-        /*
-         * We created a new hashtable entry. Unless str is already allocated
-         * from the GC heap and flat, we have to release state->lock as
-         * string construction is a complex operation. For example, it can
-         * trigger GC which may rehash the table and make the entry invalid.
-         */
+    if (entry->keyAndFlags == 0) {
         ++state->tablegen;
+        gen = state->tablegen;
+        JS_UNLOCK(&state->lock, cx);
 
-        if (!(flags & ATOM_TMPSTR) && !JSSTRING_IS_DEPENDENT(str)) {
-            JSSTRING_CLEAR_MUTABLE(str);
-            key = str;
-        } else {
-            gen = state->tablegen;
-            JS_UNLOCK(&state->lock, cx);
-
-            if (flags & ATOM_TMPSTR) {
-                if (flags & ATOM_NOCOPY) {
-                    key = js_NewString(cx, JSFLATSTR_CHARS(str),
-                                       JSFLATSTR_LENGTH(str));
-                    if (!key)
-                        return NULL;
+        if (flags & ATOM_TMPSTR) {
+            if (flags & ATOM_NOCOPY) {
+                key = js_NewString(cx, str->u.chars, str->length);
+                if (!key)
+                    return NULL;
 
-                    /* Finish handing off chars to the GC'ed key string. */
-                    str->u.chars = NULL;
-                } else {
-                    key = js_NewStringCopyN(cx, JSFLATSTR_CHARS(str),
-                                            JSFLATSTR_LENGTH(str));
-                    if (!key)
-                        return NULL;
-                }
+                /* Transfer ownership of str->chars to GC-controlled string. */
+                str->u.chars = NULL;
             } else {
-                JS_ASSERT(JSSTRING_IS_DEPENDENT(str));
-                if (!js_UndependString(cx, str))
+                key = js_NewStringCopyN(cx, str->u.chars, str->length);
+                if (!key)
                     return NULL;
-                key = str;
             }
+        } 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) {
-                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;
-            }
+        JS_LOCK(&state->lock, cx);
+        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;
         }
         INIT_ATOM_ENTRY(entry, key);
-        JSSTRING_SET_ATOMIZED(key);
     }
 
   finish:
     ADD_ATOM_ENTRY_FLAGS(entry, flags & (ATOM_PINNED | ATOM_INTERNED));
-    JS_ASSERT(JSSTRING_IS_ATOMIZED(key));
-    v = STRING_TO_JSVAL(key);
+    v = STRING_TO_JSVAL((JSString *)ATOM_ENTRY_KEY(entry));
     cx->weakRoots.lastAtom = v;
     JS_UNLOCK(&state->lock, cx);
     return (JSAtom *)v;
 
   failed_hash_add:
     JS_UNLOCK(&state->lock,cx);
     JS_ReportOutOfMemory(cx);
     return NULL;
@@ -792,46 +774,25 @@ js_AtomizePrimitiveValue(JSContext *cx, 
         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;
 }
 
-JSBool
-js_ValueToStringId(JSContext *cx, jsval v, jsid *idp)
+JSAtom *
+js_ValueToStringAtom(JSContext *cx, jsval v)
 {
     JSString *str;
-    JSAtom *atom;
 
-    /*
-     * Optimize for the common case where v is an already-atomized string. The
-     * comment in jsstr.h before the JSSTRING_SET_ATOMIZED macro's definition
-     * explains why this is thread-safe. The extra rooting via lastAtom (which
-     * would otherwise be done in js_js_AtomizeString) ensures the caller that
-     * the resulting id at least weakly rooted.
-     */
-    if (JSVAL_IS_STRING(v)) {
-        str = JSVAL_TO_STRING(v);
-        if (JSSTRING_IS_ATOMIZED(str)) {
-            cx->weakRoots.lastAtom = v;
-            *idp = ATOM_TO_JSID((JSAtom *) v);
-            return JS_TRUE;
-        }
-    } else {
-        str = js_ValueToString(cx, v);
-        if (!str)
-            return JS_FALSE;
-    }
-    atom = js_AtomizeString(cx, str, 0);
-    if (!atom)
-        return JS_FALSE;
-    *idp = ATOM_TO_JSID(atom);
-    return JS_TRUE;
+    str = js_ValueToString(cx, v);
+    if (!str)
+        return NULL;
+    return js_AtomizeString(cx, str, 0);
 }
 
 #ifdef DEBUG
 
 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
 atom_dumper(JSDHashTable *table, JSDHashEntryHdr *hdr,
             uint32 number, void *arg)
 {
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -411,20 +411,20 @@ js_GetExistingStringAtom(JSContext *cx, 
 
 /*
  * This variant handles all primitive values.
  */
 JSBool
 js_AtomizePrimitiveValue(JSContext *cx, jsval v, JSAtom **atomp);
 
 /*
- * Convert v to an atomized string and wrap it as an id.
+ * Convert v to an atomized string.
  */
-extern JSBool
-js_ValueToStringId(JSContext *cx, jsval v, jsid *idp);
+extern JSAtom *
+js_ValueToStringAtom(JSContext *cx, jsval v);
 
 #ifdef DEBUG
 
 extern JS_FRIEND_API(void)
 js_DumpAtoms(JSContext *cx, FILE *fp);
 
 #endif
 
--- a/js/src/jsdbgapi.c
+++ b/js/src/jsdbgapi.c
@@ -670,53 +670,59 @@ js_WrapWatchedSetter(JSContext *cx, jsid
     JSFunction *wrapper;
 
     if (!(attrs & JSPROP_SETTER))
         return &js_watch_set;   /* & to silence schoolmarmish MSVC */
 
     if (JSID_IS_ATOM(id)) {
         atom = JSID_TO_ATOM(id);
     } else if (JSID_IS_INT(id)) {
-        if (!js_ValueToStringId(cx, INT_JSID_TO_JSVAL(id), &id))
+        atom = js_ValueToStringAtom(cx, INT_JSID_TO_JSVAL(id));
+        if (!atom)
             return NULL;
-        atom = JSID_TO_ATOM(id);
     } else {
         atom = NULL;
     }
     wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0,
                              OBJ_GET_PARENT(cx, (JSObject *)setter),
                              atom);
     if (!wrapper)
         return NULL;
     return (JSPropertyOp) wrapper->object;
 }
 
 JS_PUBLIC_API(JSBool)
-JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval idval,
+JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id,
                  JSWatchPointHandler handler, void *closure)
 {
+    JSAtom *atom;
     jsid propid;
     JSObject *pobj;
     JSProperty *prop;
     JSScopeProperty *sprop;
     JSRuntime *rt;
     JSBool ok;
     JSWatchPoint *wp;
     JSPropertyOp watcher;
 
     if (!OBJ_IS_NATIVE(obj)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH,
                              OBJ_GET_CLASS(cx, obj)->name);
         return JS_FALSE;
     }
 
-    if (JSVAL_IS_INT(idval))
-        propid = INT_JSVAL_TO_JSID(idval);
-    else if (!js_ValueToStringId(cx, idval, &propid))
-        return JS_FALSE;
+    if (JSVAL_IS_INT(id)) {
+        propid = INT_JSVAL_TO_JSID(id);
+        atom = NULL;
+    } else {
+        atom = js_ValueToStringAtom(cx, id);
+        if (!atom)
+            return JS_FALSE;
+        propid = ATOM_TO_JSID(atom);
+    }
 
     if (!js_LookupProperty(cx, obj, propid, &pobj, &prop))
         return JS_FALSE;
     sprop = (JSScopeProperty *) prop;
     rt = cx->runtime;
     if (!sprop) {
         /* Check for a deleted symbol watchpoint, which holds its property. */
         sprop = js_FindWatchPoint(rt, OBJ_SCOPE(obj), propid);
@@ -741,18 +747,18 @@ JS_SetWatchPoint(JSContext *cx, JSObject
                     ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
                     : JSVAL_VOID;
             getter = sprop->getter;
             setter = sprop->setter;
             attrs = sprop->attrs;
             flags = sprop->flags;
             shortid = sprop->shortid;
         } else {
-            if (!OBJ_GET_PROPERTY(cx, pobj, propid, &value) ||
-                !OBJ_GET_ATTRIBUTES(cx, pobj, propid, prop, &attrs)) {
+            if (!OBJ_GET_PROPERTY(cx, pobj, id, &value) ||
+                !OBJ_GET_ATTRIBUTES(cx, pobj, id, prop, &attrs)) {
                 OBJ_DROP_PROPERTY(cx, pobj, prop);
                 return JS_FALSE;
             }
             getter = setter = NULL;
             flags = 0;
             shortid = 0;
         }
         OBJ_DROP_PROPERTY(cx, pobj, prop);
@@ -1511,18 +1517,17 @@ JS_GetObjectTotalSize(JSContext *cx, JSO
 static size_t
 GetAtomTotalSize(JSContext *cx, JSAtom *atom)
 {
     size_t nbytes;
 
     nbytes = sizeof(JSAtom *) + sizeof(JSDHashEntryStub);
     if (ATOM_IS_STRING(atom)) {
         nbytes += sizeof(JSString);
-        nbytes += (JSFLATSTR_LENGTH(ATOM_TO_STRING(atom)) + 1)
-                  * sizeof(jschar);
+        nbytes += (ATOM_TO_STRING(atom)->length + 1) * sizeof(jschar);
     } else if (ATOM_IS_DOUBLE(atom)) {
         nbytes += sizeof(jsdouble);
     }
     return nbytes;
 }
 
 JS_PUBLIC_API(size_t)
 JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun)
--- a/js/src/jsfun.c
+++ b/js/src/jsfun.c
@@ -802,39 +802,42 @@ call_enumerate(JSContext *cx, JSObject *
     }
 
   out:
     JS_ARENA_RELEASE(&cx->tempPool, mark);
     return names != NULL;
 }
 
 static JSBool
-call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags,
+call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
              JSObject **objp)
 {
     JSStackFrame *fp;
-    jsid id;
+    JSString *str;
+    JSAtom *atom;
     JSLocalKind localKind;
     JSPropertyOp getter, setter;
     uintN slot, attrs;
     jsval *vp;
 
     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
     if (!fp)
         return JS_TRUE;
     JS_ASSERT(fp->fun);
     JS_ASSERT(GET_FUNCTION_PRIVATE(cx, fp->callee) == fp->fun);
 
-    if (!JSVAL_IS_STRING(idval))
+    if (!JSVAL_IS_STRING(id))
         return JS_TRUE;
 
-    if (!js_ValueToStringId(cx, idval, &id))
+    str = JSVAL_TO_STRING(id);
+    atom = js_AtomizeString(cx, str, 0);
+    if (!atom)
         return JS_FALSE;
 
-    localKind = js_LookupLocal(cx, fp->fun, JSID_TO_ATOM(id), &slot);
+    localKind = js_LookupLocal(cx, fp->fun, atom, &slot);
     if (localKind != JSLOCAL_NONE) {
         if (localKind == JSLOCAL_ARG) {
             JS_ASSERT(slot < fp->fun->nargs);
             vp = fp->argv;
             getter = setter = NULL;
             attrs = JSPROP_PERMANENT;
         } else {
             JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
@@ -842,31 +845,33 @@ call_resolve(JSContext *cx, JSObject *ob
             JS_ASSERT(slot < fp->nvars);
             vp = fp->vars;
             getter = js_GetCallVariable;
             setter = js_SetCallVariable;
             attrs = (localKind == JSLOCAL_CONST)
                     ? JSPROP_PERMANENT | JSPROP_READONLY
                     : JSPROP_PERMANENT;
         }
-        if (!js_DefineNativeProperty(cx, obj, id, vp[slot], getter, setter,
-                                     attrs, SPROP_HAS_SHORTID, (int) slot,
-                                     NULL)) {
+        if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), vp[slot],
+                                     getter, setter, attrs,
+                                     SPROP_HAS_SHORTID, (int) slot, NULL)) {
             return JS_FALSE;
         }
         *objp = obj;
         return JS_TRUE;
     }
 
     /*
      * Resolve arguments so that we never store a particular Call object's
      * arguments object reference in a Call prototype's |arguments| slot.
      */
-    if (id == ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom)) {
-        if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID,
+    atom = cx->runtime->atomState.argumentsAtom;
+    if (id == ATOM_KEY(atom)) {
+        if (!js_DefineNativeProperty(cx, obj,
+                                     ATOM_TO_JSID(atom), JSVAL_VOID,
                                      NULL, NULL, JSPROP_PERMANENT,
                                      SPROP_HAS_SHORTID, CALL_ARGUMENTS,
                                      NULL)) {
             return JS_FALSE;
         }
         *objp = obj;
         return JS_TRUE;
     }
--- a/js/src/jsinterp.c
+++ b/js/src/jsinterp.c
@@ -1627,32 +1627,38 @@ js_InvokeConstructor(JSContext *cx, jsva
 
     JS_RUNTIME_METER(cx->runtime, constructs);
     return JS_TRUE;
 }
 
 static JSBool
 InternNonIntElementId(JSContext *cx, JSObject *obj, jsval idval, jsid *idp)
 {
+    JSAtom *atom;
+
     JS_ASSERT(!JSVAL_IS_INT(idval));
 
 #if JS_HAS_XML_SUPPORT
     if (!JSVAL_IS_PRIMITIVE(idval)) {
         if (OBJECT_IS_XML(cx, obj)) {
             *idp = OBJECT_JSVAL_TO_JSID(idval);
             return JS_TRUE;
         }
         if (!js_IsFunctionQName(cx, JSVAL_TO_OBJECT(idval), idp))
             return JS_FALSE;
         if (*idp != 0)
             return JS_TRUE;
     }
 #endif
 
-    return js_ValueToStringId(cx, idval, idp);
+    atom = js_ValueToStringAtom(cx, idval);
+    if (!atom)
+        return JS_FALSE;
+    *idp = ATOM_TO_JSID(atom);
+    return JS_TRUE;
 }
 
 /*
  * Threaded interpretation via computed goto appears to be well-supported by
  * GCC 3 and higher.  IBM's C compiler when run with the right options (e.g.,
  * -qlanglvl=extended) also supports threading.  Ditto the SunPro C compiler.
  * Currently it's broken for JS_VERSION < 160, though this isn't worth fixing.
  * Add your compiler support macros here.
--- a/js/src/jsobj.c
+++ b/js/src/jsobj.c
@@ -2932,21 +2932,21 @@ js_FreeSlot(JSContext *cx, JSObject *obj
 /* JSVAL_INT_MAX as a string */
 #define JSVAL_INT_MAX_STRING "1073741823"
 
 #define CHECK_FOR_STRING_INDEX(id)                                            \
     JS_BEGIN_MACRO                                                            \
         if (JSID_IS_ATOM(id)) {                                               \
             JSAtom *atom_ = JSID_TO_ATOM(id);                                 \
             JSString *str_ = ATOM_TO_STRING(atom_);                           \
-            const jschar *cp_ = JSFLATSTR_CHARS(str_);                        \
+            const jschar *cp_ = str_->u.chars;                                \
             JSBool negative_ = (*cp_ == '-');                                 \
             if (negative_) cp_++;                                             \
             if (JS7_ISDEC(*cp_)) {                                            \
-                size_t n_ = JSFLATSTR_LENGTH(str_) - negative_;               \
+                size_t n_ = str_->length - negative_;                         \
                 if (n_ <= sizeof(JSVAL_INT_MAX_STRING) - 1)                   \
                     id = CheckForStringIndex(id, cp_, cp_ + n_, negative_);   \
             }                                                                 \
         }                                                                     \
     JS_END_MACRO
 
 static jsid
 CheckForStringIndex(jsid id, const jschar *cp, const jschar *end,
--- a/js/src/jsparse.c
+++ b/js/src/jsparse.c
@@ -6401,34 +6401,34 @@ js_FoldConstants(JSContext *cx, JSParseN
             /* Ok, we're concatenating: convert non-string constant operands. */
             length = 0;
             for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
                 if (!FoldType(cx, pn2, TOK_STRING))
                     return JS_FALSE;
                 /* XXX fold only if all operands convert to string */
                 if (pn2->pn_type != TOK_STRING)
                     return JS_TRUE;
-                length += JSFLATSTR_LENGTH(ATOM_TO_STRING(pn2->pn_atom));
+                length += ATOM_TO_STRING(pn2->pn_atom)->length;
             }
 
             /* Allocate a new buffer and string descriptor for the result. */
             chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
             if (!chars)
                 return JS_FALSE;
             str = js_NewString(cx, chars, length);
             if (!str) {
                 JS_free(cx, chars);
                 return JS_FALSE;
             }
 
             /* Fill the buffer, advancing chars and recycling kids as we go. */
             for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
                 str2 = ATOM_TO_STRING(pn2->pn_atom);
-                length2 = JSFLATSTR_LENGTH(str2);
-                js_strncpy(chars, JSFLATSTR_CHARS(str2), length2);
+                length2 = str2->length;
+                js_strncpy(chars, str2->u.chars, length2);
                 chars += length2;
             }
             *chars = 0;
 
             /* Atomize the result string and mutate pn to refer to it. */
             pn->pn_atom = js_AtomizeString(cx, str, 0);
             if (!pn->pn_atom)
                 return JS_FALSE;
--- a/js/src/jsstr.c
+++ b/js/src/jsstr.c
@@ -105,27 +105,27 @@ js_MinimizeDependentStrings(JSString *st
 jschar *
 js_GetDependentStringChars(JSString *str)
 {
     size_t start;
     JSString *base;
 
     start = js_MinimizeDependentStrings(str, 0, &base);
     JS_ASSERT(!JSSTRING_IS_DEPENDENT(base));
-    JS_ASSERT(start < JSFLATSTR_LENGTH(base));
-    return JSFLATSTR_CHARS(base) + start;
+    JS_ASSERT(start < (base->length & ~JSSTRFLAG_MUTABLE));
+    return base->u.chars + start;
 }
 
 const jschar *
 js_GetStringChars(JSContext *cx, JSString *str)
 {
     if (!js_MakeStringImmutable(cx, str))
         return NULL;
     JS_ASSERT(!JSSTRING_IS_DEPENDENT(str));
-    return JSFLATSTR_CHARS(str);
+    return str->u.chars;
 }
 
 JSString *
 js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
 {
     size_t rn, ln, lrdist, n;
     jschar *rs, *ls, *s;
     JSString *ldep;             /* non-null if left should become dependent */
@@ -220,17 +220,17 @@ js_UndependString(JSContext *cx, JSStrin
             JS_RUNTIME_UNMETER(rt, totalDependentStrings);
             JS_LOCK_RUNTIME_VOID(rt,
                 (rt->strdepLengthSum -= (double)n,
                  rt->strdepLengthSquaredSum -= (double)n * (double)n));
         }
 #endif
     }
 
-    return JSFLATSTR_CHARS(str);
+    return str->u.chars;
 }
 
 JSBool
 js_MakeStringImmutable(JSContext *cx, JSString *str)
 {
     if (JSSTRING_IS_DEPENDENT(str) && !js_UndependString(cx, str)) {
         JS_RUNTIME_METER(cx->runtime, badUndependStrings);
         return JS_FALSE;
@@ -2640,38 +2640,36 @@ js_ChangeExternalStringFinalizer(JSStrin
 
 /*
  * cx is NULL when we are called from js_FinishAtomState to force the
  * finalization of the permanently interned strings.
  */
 void
 js_FinalizeStringRT(JSRuntime *rt, JSString *str, intN type, JSContext *cx)
 {
-    jschar *chars;
     JSBool valid;
     JSStringFinalizeOp finalizer;
 
     JS_RUNTIME_UNMETER(rt, liveStrings);
     if (JSSTRING_IS_DEPENDENT(str)) {
         /* A dependent string can not be external and must be valid. */
         JS_ASSERT(type < 0);
         JS_ASSERT(JSSTRDEP_BASE(str));
         JS_RUNTIME_UNMETER(rt, liveDependentStrings);
         valid = JS_TRUE;
     } else {
         /* A stillborn string has null chars, so is not valid. */
-        chars = JSFLATSTR_CHARS(str);
-        valid = (chars != NULL);
+        valid = (str->u.chars != NULL);
         if (valid) {
-            if (IN_UNIT_STRING_SPACE_RT(rt, chars)) {
-                JS_ASSERT(rt->unitStrings[*chars] == str);
+            if (IN_UNIT_STRING_SPACE_RT(rt, str->u.chars)) {
+                JS_ASSERT(rt->unitStrings[*str->u.chars] == str);
                 JS_ASSERT(type < 0);
-                rt->unitStrings[*chars] = NULL;
+                rt->unitStrings[*str->u.chars] = NULL;
             } else if (type < 0) {
-                free(chars);
+                free(str->u.chars);
             } else {
                 JS_ASSERT((uintN) type < JS_ARRAY_LENGTH(str_finalizers));
                 finalizer = str_finalizers[type];
                 if (finalizer) {
                     /*
                      * Assume that the finalizer for the permanently interned
                      * string knows how to deal with null context.
                      */
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -65,20 +65,16 @@ JS_BEGIN_EXTERN_C
  * requires \u0000 termination.
  *
  * A flat string with JSSTRFLAG_MUTABLE set means the string is accessible
  * only from one thread and it is possible to turn it into a dependent string
  * of the same length to optimize js_ConcatStrings. It also possible to grow
  * such string but extreme care must be taken to ensure that no other code
  * relies on the original length of the string.
  *
- * A flat string with JSSTRFLAG_ATOMIZED set means that the string is hashed
- * as an atom. This flag is used to avoid re-hashing of the already-atomized
- * string.
- *
  * When JSSTRFLAG_DEPENDENT is set, the string depends on characters of
  * another string strongly referenced by the u.base field. The base member may
  * point to another dependent string if JSSTRING_CHARS has not been called
  * yet.
  *
  * JSSTRFLAG_PREFIX determines the kind of the dependent string. When the flag
  * is unset, the length field encodes both starting position relative to the
  * base string and the number of characters in the dependent string, see
@@ -98,112 +94,69 @@ struct JSString {
         JSString    *base;
     } u;
 };
 
 /*
  * Definitions for flags stored in the high order bits of JSString.length.
  * JSSTRFLAG_PREFIX and JSSTRFLAG_MUTABLE are two aliases for the same value.
  * JSSTRFLAG_PREFIX should be used only if JSSTRFLAG_DEPENDENT is set and
- * JSSTRFLAG_MUTABLE should be used only if the string is flat and
- * JSSTRFLAG_DEPENDENT is unset. JSSTRFLAG_ATOMIZED is used only with the
- * flat immutable strings.
+ * JSSTRFLAG_MUTABLE should be used only if JSSTRFLAG_DEPENDENT is unset.
  */
-#define JSSTRFLAG_DEPENDENT         JSSTRING_BIT(JS_BITS_PER_WORD - 1)
-#define JSSTRFLAG_PREFIX            JSSTRING_BIT(JS_BITS_PER_WORD - 2)
-#define JSSTRFLAG_MUTABLE           JSSTRFLAG_PREFIX
-#define JSSTRFLAG_ATOMIZED          JSSTRING_BIT(JS_BITS_PER_WORD - 3)
-
-#define JSSTRING_LENGTH_BITS        (JS_BITS_PER_WORD - 3)
-#define JSSTRING_LENGTH_MASK        JSSTRING_BITMASK(JSSTRING_LENGTH_BITS)
+#define JSSTRFLAG_BITS              2
+#define JSSTRFLAG_SHIFT(flg)        ((size_t)(flg) << JSSTRING_LENGTH_BITS)
+#define JSSTRFLAG_MASK              JSSTRFLAG_SHIFT(JS_BITMASK(JSSTRFLAG_BITS))
+#define JSSTRFLAG_DEPENDENT         JSSTRFLAG_SHIFT(1)
+#define JSSTRFLAG_PREFIX            JSSTRFLAG_SHIFT(2)
+#define JSSTRFLAG_MUTABLE           JSSTRFLAG_SHIFT(2)
 
 /* Universal JSString type inquiry and accessor macros. */
 #define JSSTRING_BIT(n)             ((size_t)1 << (n))
 #define JSSTRING_BITMASK(n)         (JSSTRING_BIT(n) - 1)
 #define JSSTRING_HAS_FLAG(str,flg)  ((str)->length & (flg))
 #define JSSTRING_IS_DEPENDENT(str)  JSSTRING_HAS_FLAG(str, JSSTRFLAG_DEPENDENT)
-#define JSSTRING_IS_MUTABLE(str)    (((str)->length & (JSSTRFLAG_DEPENDENT |  \
-                                                       JSSTRFLAG_MUTABLE)) == \
+#define JSSTRING_IS_MUTABLE(str)    (((str)->length & JSSTRFLAG_MASK) ==      \
                                      JSSTRFLAG_MUTABLE)
-#define JSSTRING_IS_ATOMIZED(str)   (((str)->length & (JSSTRFLAG_DEPENDENT |  \
-                                                       JSSTRFLAG_ATOMIZED)) ==\
-                                     JSSTRFLAG_ATOMIZED)
 
 #define JSSTRING_CHARS(str)         (JSSTRING_IS_DEPENDENT(str)               \
                                      ? JSSTRDEP_CHARS(str)                    \
-                                     : JSFLATSTR_CHARS(str))
+                                     : (str)->u.chars)
 #define JSSTRING_LENGTH(str)        (JSSTRING_IS_DEPENDENT(str)               \
                                      ? JSSTRDEP_LENGTH(str)                   \
-                                     : JSFLATSTR_LENGTH(str))
+                                     : ((str)->length &  ~JSSTRFLAG_MUTABLE))
 
 #define JSSTRING_CHARS_AND_LENGTH(str, chars_, length_)                       \
     ((void)(JSSTRING_IS_DEPENDENT(str)                                        \
             ? ((length_) = JSSTRDEP_LENGTH(str),                              \
                (chars_) = JSSTRDEP_CHARS(str))                                \
-            : ((length_) = JSFLATSTR_LENGTH(str),                             \
-               (chars_) = JSFLATSTR_CHARS(str))))
+            : ((length_) = (str)->length & ~JSSTRFLAG_MUTABLE,                \
+               (chars_) = (str)->u.chars)))
 
 #define JSSTRING_CHARS_AND_END(str, chars_, end)                              \
     ((void)((end) = JSSTRING_IS_DEPENDENT(str)                                \
                   ? JSSTRDEP_LENGTH(str) + ((chars_) = JSSTRDEP_CHARS(str))   \
-                  : JSFLATSTR_LENGTH(str) + ((chars_) = JSFLATSTR_CHARS(str))))
+                  : ((str)->length & ~JSSTRFLAG_MUTABLE) +                    \
+                    ((chars_) = (str)->u.chars)))
+
+#define JSSTRING_LENGTH_BITS        (sizeof(size_t) * JS_BITS_PER_BYTE        \
+                                     - JSSTRFLAG_BITS)
+#define JSSTRING_LENGTH_MASK        JSSTRING_BITMASK(JSSTRING_LENGTH_BITS)
 
 #define JSSTRING_INIT(str, chars_, length_)                                   \
-    ((void)(JS_ASSERT(((length_) & ~JSSTRING_LENGTH_MASK) == 0),              \
+    ((void)(JS_ASSERT(((length_) & JSSTRFLAG_MASK) == 0),                     \
             (str)->length = (length_), (str)->u.chars = (chars_)))
 
-/*
- * Specific macro to get the length and characters of a flat string.
- */
-#define JSFLATSTR_LENGTH(str)                                                 \
-    (JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)),                                  \
-     (str)->length & JSSTRING_LENGTH_MASK)
-
-#define JSFLATSTR_CHARS(str)                                                  \
-    (JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)), (str)->u.chars)
-
-/*
- * Specific macros to manipulate atomized and mutable flags. It is safe to use
- * these without extra locking due to the following properties:
- *
- *   * We do not have a macro like JSSTRING_CLEAR_ATOMIZED as a string remains
- *     atomized until the GC collects it.
- *
- *   * A thread may call JSSTRING_SET_MUTABLE only when it is the only thread
- *     accessing the string until a later call to JSSTRING_CLEAR_MUTABLE.
- *
- *   * Multiple threads can call JSSTRING_CLEAR_MUTABLE but the macro actually
- *     clears the mutable flag only when the flag is set -- in which case only
- *     one thread can access the string (see previous property).
- *
- *  Thus, when multiple threads access the string, JSSTRING_SET_ATOMIZED is
- *  the only macro that can update the length field of the string by changing
- *  the mutable bit from 0 to 1. We call the macro only after the string has
- *  been hashed. When some threads in js_ValueToStringId see that the flag is
- *  set, it knows that the string was atomized.
- *
- *  On the other hand, if the thread sees that the flag is unset, it could be
- *  seeing a stale value when another thread has just atomized the string and
- *  set the flag. But this can lead only to an extra call to js_AtomizeString.
- *  This function would find that the string was already hashed and return it
- *  with the atomized bit set.
- */
-#define JSSTRING_SET_ATOMIZED(str)                                            \
-    ((void)(JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)),                           \
-            JS_ASSERT(!JSSTRING_IS_MUTABLE(str)),                             \
-            (str)->length |= JSSTRFLAG_ATOMIZED))
-
+/* Specific mutable string manipulation macros. */
 #define JSSTRING_SET_MUTABLE(str)                                             \
     ((void)(JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)),                           \
             (str)->length |= JSSTRFLAG_MUTABLE))
 
 #define JSSTRING_CLEAR_MUTABLE(str)                                           \
     ((void)(JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)),                           \
-            JSSTRING_HAS_FLAG(str, JSSTRFLAG_MUTABLE) &&                      \
-            ((str)->length &= ~JSSTRFLAG_MUTABLE)))
+            (str)->length &= ~JSSTRFLAG_MUTABLE))
 
 /* Specific dependent string shift/mask accessor and mutator macros. */
 #define JSSTRDEP_START_BITS         (JSSTRING_LENGTH_BITS-JSSTRDEP_LENGTH_BITS)
 #define JSSTRDEP_START_SHIFT        JSSTRDEP_LENGTH_BITS
 #define JSSTRDEP_START_MASK         JSSTRING_BITMASK(JSSTRDEP_START_BITS)
 #define JSSTRDEP_LENGTH_BITS        (JSSTRING_LENGTH_BITS / 2)
 #define JSSTRDEP_LENGTH_MASK        JSSTRING_BITMASK(JSSTRDEP_LENGTH_BITS)
 
@@ -230,17 +183,17 @@ struct JSString {
 
 #define JSSTRDEP_BASE(str)          ((str)->u.base)
 #define JSPREFIX_BASE(str)          JSSTRDEP_BASE(str)
 #define JSPREFIX_SET_BASE(str,bstr) ((str)->u.base = (bstr))
 
 #define JSSTRDEP_CHARS(str)                                                   \
     (JSSTRING_IS_DEPENDENT(JSSTRDEP_BASE(str))                                \
      ? js_GetDependentStringChars(str)                                        \
-     : JSFLATSTR_CHARS(JSSTRDEP_BASE(str)) + JSSTRDEP_START(str))
+     : JSSTRDEP_BASE(str)->u.chars + JSSTRDEP_START(str))
 
 extern size_t
 js_MinimizeDependentStrings(JSString *str, int level, JSString **basep);
 
 extern jschar *
 js_GetDependentStringChars(JSString *str);
 
 extern const jschar *
--- a/js/src/jsxml.c
+++ b/js/src/jsxml.c
@@ -7891,17 +7891,17 @@ js_AddAttributePart(JSContext *cx, JSBoo
     size_t len, len2, newlen;
     jschar *chars, *chars2;
 
     JSSTRING_CHARS_AND_LENGTH(str, chars, len);
     if (!JSSTRING_IS_MUTABLE(str)) {
         str = js_NewStringCopyN(cx, chars, len);
         if (!str)
             return NULL;
-        chars = JSFLATSTR_CHARS(str);
+        chars = str->u.chars;
     } else {
         /*
          * Reallocating str (because we know it has no other references)
          * requires purging any deflated string cached for it.
          */
         js_PurgeDeflatedStringCache(cx->runtime, str);
     }