Bug 675806 - Make static strings be GC things (r=luke)
authorBill McCloskey <wmccloskey@mozilla.com>
Tue, 20 Sep 2011 14:47:14 -0700
changeset 77224 3c429287dfbe623a21a9ca2382873a10a63ed781
parent 77223 c207eea5477770ac791123fb1e937d1952b3b661
child 77225 a84273cf364468ced0be05f8ea860c70a95c53c5
push id2019
push userwmccloskey@mozilla.com
push dateTue, 20 Sep 2011 21:48:30 +0000
treeherdermozilla-inbound@3c429287dfbe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs675806
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 675806 - Make static strings be GC things (r=luke)
dom/indexedDB/IDBObjectStore.cpp
js/src/jsapi-tests/testGCOutOfMemory.cpp
js/src/jsapi-tests/testIndexToString.cpp
js/src/jsapi-tests/testIntString.cpp
js/src/jsatom.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jsgc.cpp
js/src/jsgcinlines.h
js/src/jsgcmark.cpp
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsiter.cpp
js/src/jsnum.cpp
js/src/jsstr.cpp
js/src/jstracer.cpp
js/src/jstracer.h
js/src/methodjit/FastBuiltins.cpp
js/src/methodjit/StubCalls.cpp
js/src/vm/String-inl.h
js/src/vm/String.cpp
js/src/vm/String.h
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -2202,17 +2202,17 @@ OpenCursorHelper::GetSuccessResult(JSCon
 
 class ThreadLocalJSRuntime
 {
   JSRuntime* mRuntime;
   JSContext* mContext;
   JSObject* mGlobal;
 
   static JSClass sGlobalClass;
-  static const unsigned sRuntimeHeapSize = 64 * 1024;  // should be enough for anyone
+  static const unsigned sRuntimeHeapSize = 256 * 1024;  // should be enough for anyone
 
   ThreadLocalJSRuntime()
   : mRuntime(NULL), mContext(NULL), mGlobal(NULL)
   {
       MOZ_COUNT_CTOR(ThreadLocalJSRuntime);
   }
 
   nsresult Init()
--- a/js/src/jsapi-tests/testGCOutOfMemory.cpp
+++ b/js/src/jsapi-tests/testGCOutOfMemory.cpp
@@ -53,16 +53,16 @@ BEGIN_TEST(testGCOutOfMemory)
          "        array.push({});"
          "    }"
          "})();", root.addr());
     CHECK_EQUAL(errorCount, 1);
     return true;
 }
 
 virtual JSRuntime * createRuntime() {
-    return JS_NewRuntime(256 * 1024);
+    return JS_NewRuntime(512 * 1024);
 }
 
 virtual void destroyRuntime() {
     JS_DestroyRuntime(rt);
 }
 
 END_TEST(testGCOutOfMemory)
--- a/js/src/jsapi-tests/testIndexToString.cpp
+++ b/js/src/jsapi-tests/testIndexToString.cpp
@@ -51,17 +51,17 @@ static const struct TestPair {
 
 BEGIN_TEST(testIndexToString)
 {
     for (size_t i = 0, sz = JS_ARRAY_LENGTH(tests); i < sz; i++) {
         uint32 u = tests[i].num;
         JSString *str = js::IndexToString(cx, u);
         CHECK(str);
 
-        if (!JSAtom::hasUintStatic(u))
+        if (!js::StaticStrings::hasUint(u))
             CHECK(cx->compartment->dtoaCache.lookup(10, u) == str);
 
         JSBool match = JS_FALSE;
         CHECK(JS_StringEqualsAscii(cx, str, tests[i].expected, &match));
         CHECK(match);
     }
 
     return true;
--- a/js/src/jsapi-tests/testIntString.cpp
+++ b/js/src/jsapi-tests/testIntString.cpp
@@ -6,35 +6,35 @@
 #include "vm/String.h"
 
 BEGIN_TEST(testIntString_bug515273)
 {
     jsvalRoot v(cx);
 
     EVAL("'1';", v.addr());
     JSString *str = JSVAL_TO_STRING(v.value());
-    CHECK(str->isStaticAtom());
+    CHECK(JS_StringHasBeenInterned(cx, str));
     CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "1"));
 
     EVAL("'42';", v.addr());
     str = JSVAL_TO_STRING(v.value());
-    CHECK(str->isStaticAtom());
+    CHECK(JS_StringHasBeenInterned(cx, str));
     CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "42"));
 
     EVAL("'111';", v.addr());
     str = JSVAL_TO_STRING(v.value());
-    CHECK(str->isStaticAtom());
+    CHECK(JS_StringHasBeenInterned(cx, str));
     CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "111"));
 
     /* Test other types of static strings. */
     EVAL("'a';", v.addr());
     str = JSVAL_TO_STRING(v.value());
-    CHECK(str->isStaticAtom());
+    CHECK(JS_StringHasBeenInterned(cx, str));
     CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "a"));
 
     EVAL("'bc';", v.addr());
     str = JSVAL_TO_STRING(v.value());
-    CHECK(str->isStaticAtom());
+    CHECK(JS_StringHasBeenInterned(cx, str));
     CHECK(JS_FlatStringEqualsAscii(JS_ASSERT_STRING_IS_FLAT(str), "bc"));
 
     return true;
 }
 END_TEST(testIntString_bug515273)
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -418,26 +418,27 @@ js_SweepAtomState(JSContext *cx)
     for (AtomSet::Enum e(state->atoms); !e.empty(); e.popFront()) {
         AtomStateEntry entry = e.front();
 
         if (entry.isTagged()) {
             /* Pinned or interned key cannot be finalized. */
             JS_ASSERT(!IsAboutToBeFinalized(cx, entry.asPtr()));
             continue;
         }
-        
+
         if (IsAboutToBeFinalized(cx, entry.asPtr()))
             e.removeFront();
     }
 }
 
 bool
 AtomIsInterned(JSContext *cx, JSAtom *atom)
 {
-    if (atom->isStaticAtom())
+    /* We treat static strings as interned because they're never collected. */
+    if (StaticStrings::isStatic(atom))
         return true;
 
     AutoLockAtomsCompartment lock(cx);
     AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(atom);
     if (!p)
         return false;
 
     return p->isTagged();
@@ -456,17 +457,17 @@ enum OwnCharsBehavior
  */
 JS_ALWAYS_INLINE
 static JSAtom *
 AtomizeInline(JSContext *cx, const jschar **pchars, size_t length,
               InternBehavior ib, OwnCharsBehavior ocb = CopyChars)
 {
     const jschar *chars = *pchars;
 
-    if (JSAtom *s = JSAtom::lookupStatic(chars, length))
+    if (JSAtom *s = cx->runtime->staticStrings.lookup(chars, length))
         return s;
 
     AutoLockAtomsCompartment lock(cx);
 
     AtomSet &atoms = cx->runtime->atomState.atoms;
     AtomSet::AddPtr p = atoms.lookupForAdd(AtomHasher::Lookup(chars, length));
 
     if (p) {
@@ -516,17 +517,17 @@ Atomize(JSContext *cx, const jschar **pc
 }
 
 JSAtom *
 js_AtomizeString(JSContext *cx, JSString *str, InternBehavior ib)
 {
     if (str->isAtom()) {
         JSAtom &atom = str->asAtom();
         /* N.B. static atoms are effectively always interned. */
-        if (ib != InternAtom || atom.isStaticAtom())
+        if (ib != InternAtom || js::StaticStrings::isStatic(&atom))
             return &atom;
 
         /* Here we have to check whether the atom is already interned. */
         AutoLockAtomsCompartment lock(cx);
 
         AtomSet &atoms = cx->runtime->atomState.atoms;
         AtomSet::Ptr p = atoms.lookup(AtomHasher::Lookup(&atom));
         JS_ASSERT(p); /* Non-static atom must exist in atom state set. */
@@ -599,17 +600,17 @@ js_AtomizeChars(JSContext *cx, const jsc
         return NULL;
 
     return AtomizeInline(cx, &chars, length, ib);
 }
 
 JSAtom *
 js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length)
 {
-    if (JSAtom *atom = JSAtom::lookupStatic(chars, length))
+    if (JSAtom *atom = cx->runtime->staticStrings.lookup(chars, length))
         return atom;
     AutoLockAtomsCompartment lock(cx);
     AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(AtomHasher::Lookup(chars, length));
     return p ? p->asPtr() : NULL;
 }
 
 #ifdef DEBUG
 JS_FRIEND_API(void)
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -401,17 +401,19 @@ js_NewContext(JSRuntime *rt, size_t stac
      * nulls stored in the default-initialized remainder of the struct.  We'll
      * clean the runtime up under js_DestroyContext, because cx will be "last"
      * as well as "first".
      */
     if (first) {
 #ifdef JS_THREADSAFE
         JS_BeginRequest(cx);
 #endif
-        JSBool ok = js_InitCommonAtoms(cx);
+        bool ok = rt->staticStrings.init(cx);
+        if (ok)
+            ok = js_InitCommonAtoms(cx);
 
 #ifdef JS_THREADSAFE
         JS_EndRequest(cx);
 #endif
         if (!ok) {
             js_DestroyContext(cx, JSDCM_NEW_FAILED);
             return NULL;
         }
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -60,16 +60,17 @@
 #include "jspropertycache.h"
 #include "jspropertytree.h"
 #include "jsstaticcheck.h"
 #include "jsutil.h"
 #include "jsvector.h"
 #include "prmjtime.h"
 
 #include "vm/Stack.h"
+#include "vm/String.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #pragma warning(push)
 #pragma warning(disable:4355) /* Silence warning about "this" used in base member initializer list */
 #endif
 
@@ -650,16 +651,19 @@ struct JSRuntime {
      * disabled until a triggered GC at some later moment compresses live
      * types, minimizing rt->shapeGen in the process.
      */
     volatile uint32     shapeGen;
 
     /* Literal table maintained by jsatom.c functions. */
     JSAtomState         atomState;
 
+    /* Tables of strings that are pre-allocated in the atomsCompartment. */
+    js::StaticStrings   staticStrings;
+
     JSWrapObjectCallback wrapObjectCallback;
     JSPreWrapCallback    preWrapObjectCallback;
 
     /*
      * To ensure that cx->malloc does not cause a GC, we set this flag during
      * OOM reporting (in js_ReportOutOfMemory). If a GC is requested while
      * reporting the OOM, we ignore it.
      */
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -202,20 +202,16 @@ JSCompartment::wrap(JSContext *cx, Value
 
     /* Only GC things have to be wrapped or copied. */
     if (!vp->isMarkable())
         return true;
 
     if (vp->isString()) {
         JSString *str = vp->toString();
 
-        /* Static atoms do not have to be wrapped. */
-        if (str->isStaticAtom())
-            return true;
-
         /* If the string is already in this compartment, we are done. */
         if (str->compartment() == this)
             return true;
 
         /* If the string is an atom, we don't have to copy. */
         if (str->isAtom()) {
             JS_ASSERT(str->compartment() == cx->runtime->atomsCompartment);
             return true;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -616,35 +616,32 @@ ExpireGCChunks(JSRuntime *rt, JSGCInvoca
             chunkp = &chunk->info.next;
         }
     }
 }
 
 JS_FRIEND_API(bool)
 IsAboutToBeFinalized(JSContext *cx, const void *thing)
 {
-    if (JSAtom::isStatic(thing))
-        return false;
     JS_ASSERT(cx);
 
     JSCompartment *thingCompartment = reinterpret_cast<const Cell *>(thing)->compartment();
     JSRuntime *rt = cx->runtime;
     JS_ASSERT(rt == thingCompartment->rt);
     if (rt->gcCurrentCompartment != NULL && rt->gcCurrentCompartment != thingCompartment)
         return false;
 
     return !reinterpret_cast<const Cell *>(thing)->isMarked();
 }
 
 JS_FRIEND_API(bool)
 js_GCThingIsMarked(void *thing, uintN color = BLACK)
 {
     JS_ASSERT(thing);
     AssertValidColor(thing, color);
-    JS_ASSERT(!JSAtom::isStatic(thing));
     return reinterpret_cast<Cell *>(thing)->isMarked(color);
 }
 
 /*
  * 1/8 life for JIT code. After this number of microseconds have passed, 1/8 of all
  * JIT code is discarded in inactive compartments, regardless of how often that
  * code runs.
  */
@@ -1635,34 +1632,32 @@ gc_root_traversal(JSTracer *trc, const R
     if (entry.value.type == JS_GC_ROOT_GCTHING_PTR) {
         ptr = *reinterpret_cast<void **>(entry.key);
     } else {
         Value *vp = reinterpret_cast<Value *>(entry.key);
         ptr = vp->isGCThing() ? vp->toGCThing() : NULL;
     }
 
     if (ptr && !trc->context->runtime->gcCurrentCompartment) {
-        if (!JSAtom::isStatic(ptr)) {
-            /*
-             * Use conservative machinery to find if ptr is a valid GC thing.
-             * We only do this during global GCs, to preserve the invariant
-             * that mark callbacks are not in place during compartment GCs.
-             */
-            JSTracer checker;
-            JS_TRACER_INIT(&checker, trc->context, EmptyMarkCallback);
-            ConservativeGCTest test = MarkIfGCThingWord(&checker, reinterpret_cast<jsuword>(ptr));
-            if (test != CGCT_VALID && entry.value.name) {
-                fprintf(stderr,
+        /*
+         * Use conservative machinery to find if ptr is a valid GC thing.
+         * We only do this during global GCs, to preserve the invariant
+         * that mark callbacks are not in place during compartment GCs.
+         */
+        JSTracer checker;
+        JS_TRACER_INIT(&checker, trc->context, EmptyMarkCallback);
+        ConservativeGCTest test = MarkIfGCThingWord(&checker, reinterpret_cast<jsuword>(ptr));
+        if (test != CGCT_VALID && entry.value.name) {
+            fprintf(stderr,
 "JS API usage error: the address passed to JS_AddNamedRoot currently holds an\n"
 "invalid gcthing.  This is usually caused by a missing call to JS_RemoveRoot.\n"
 "The root's name is \"%s\".\n",
-                        entry.value.name);
-            }
-            JS_ASSERT(test == CGCT_VALID);
+                    entry.value.name);
         }
+        JS_ASSERT(test == CGCT_VALID);
     }
 #endif
     JS_SET_TRACING_NAME(trc, entry.value.name ? entry.value.name : "root");
     if (entry.value.type == JS_GC_ROOT_GCTHING_PTR)
         MarkGCThing(trc, *reinterpret_cast<void **>(entry.key));
     else
         MarkValueRaw(trc, *reinterpret_cast<Value *>(entry.key));
 }
@@ -1851,16 +1846,17 @@ MarkRuntime(JSTracer *trc)
 
     for (RootRange r = rt->gcRootsHash.all(); !r.empty(); r.popFront())
         gc_root_traversal(trc, r.front());
 
     for (GCLocks::Range r = rt->gcLocksHash.all(); !r.empty(); r.popFront())
         gc_lock_traversal(r.front(), trc);
 
     js_TraceAtomState(trc);
+    rt->staticStrings.trace(trc);
 
     JSContext *iter = NULL;
     while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter))
         MarkContext(trc, acx);
 
     PER_COMPARTMENT_OP(rt, if (c->activeAnalysis) c->markTypes(trc));
 
 #ifdef JS_TRACER
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -44,85 +44,26 @@
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsscope.h"
 #include "jsxml.h"
 
 #include "jslock.h"
 #include "jstl.h"
 
-inline bool
-JSAtom::isUnitString(const void *ptr)
-{
-#ifdef JS_HAS_STATIC_STRINGS
-    jsuword delta = reinterpret_cast<jsuword>(ptr) -
-                    reinterpret_cast<jsuword>(unitStaticTable);
-    if (delta >= UNIT_STATIC_LIMIT * sizeof(JSString))
-        return false;
-
-    /* If ptr points inside the static array, it must be well-aligned. */
-    JS_ASSERT(delta % sizeof(JSString) == 0);
-    return true;
-#else
-    return false;
-#endif
-}
-
-inline bool
-JSAtom::isLength2String(const void *ptr)
-{
-#ifdef JS_HAS_STATIC_STRINGS
-    jsuword delta = reinterpret_cast<jsuword>(ptr) -
-                    reinterpret_cast<jsuword>(length2StaticTable);
-    if (delta >= NUM_SMALL_CHARS * NUM_SMALL_CHARS * sizeof(JSString))
-        return false;
-
-    /* If ptr points inside the static array, it must be well-aligned. */
-    JS_ASSERT(delta % sizeof(JSString) == 0);
-    return true;
-#else
-    return false;
-#endif
-}
-
-inline bool
-JSAtom::isHundredString(const void *ptr)
-{
-#ifdef JS_HAS_STATIC_STRINGS
-    jsuword delta = reinterpret_cast<jsuword>(ptr) -
-                    reinterpret_cast<jsuword>(hundredStaticTable);
-    if (delta >= NUM_HUNDRED_STATICS * sizeof(JSString))
-        return false;
-
-    /* If ptr points inside the static array, it must be well-aligned. */
-    JS_ASSERT(delta % sizeof(JSString) == 0);
-    return true;
-#else
-    return false;
-#endif
-}
-
-inline bool
-JSAtom::isStatic(const void *ptr)
-{
-    return isUnitString(ptr) || isLength2String(ptr) || isHundredString(ptr);
-}
-
 namespace js {
 
 struct Shape;
 
 namespace gc {
 
 inline JSGCTraceKind
 GetGCThingTraceKind(const void *thing)
 {
     JS_ASSERT(thing);
-    if (JSAtom::isStatic(thing))
-        return JSTRACE_STRING;
     const Cell *cell = reinterpret_cast<const Cell *>(thing);
     return MapAllocToTraceKind(cell->getAllocKind());
 }
 
 /* Capacity for slotsToThingKind */
 const size_t SLOTS_TO_THING_KIND_LIMIT = 17;
 
 /* Get the best kind to use when making an object with the given slot count. */
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -110,17 +110,16 @@ PushMarkStack(GCMarker *gcmarker, types:
 template<typename T>
 static inline void
 CheckMarkedThing(JSTracer *trc, T *thing)
 {
     JS_ASSERT(thing);
     JS_ASSERT(trc->debugPrinter || trc->debugPrintArg);
     JS_ASSERT_IF(trc->context->runtime->gcCurrentCompartment, IS_GC_MARKING_TRACER(trc));
 
-    JS_ASSERT(!JSAtom::isStatic(thing));
     JS_ASSERT(thing->isAligned());
 
     JS_ASSERT(thing->compartment());
     JS_ASSERT(thing->compartment()->rt == trc->context->runtime);
 }
 
 template<typename T>
 void
@@ -150,18 +149,16 @@ Mark(JSTracer *trc, T *thing)
     trc->debugPrintArg = NULL;
 #endif
 }
 
 void
 MarkString(JSTracer *trc, JSString *str)
 {
     JS_ASSERT(str);
-    if (str->isStaticAtom())
-        return;
     Mark(trc, str);
 }
 
 void
 MarkString(JSTracer *trc, JSString *str, const char *name)
 {
     JS_ASSERT(str);
     JS_SET_TRACING_NAME(trc, name);
@@ -329,18 +326,17 @@ PushMarkStack(GCMarker *gcmarker, const 
 }
 
 static void
 MarkAtomRange(JSTracer *trc, size_t len, JSAtom **vec, const char *name)
 {
     for (uint32 i = 0; i < len; i++) {
         if (JSAtom *atom = vec[i]) {
             JS_SET_TRACING_INDEX(trc, name, i);
-            if (!atom->isStaticAtom())
-                Mark(trc, atom);
+            Mark(trc, atom);
         }
     }
 }
 
 void
 MarkObjectRange(JSTracer *trc, size_t len, JSObject **vec, const char *name)
 {
     for (uint32 i = 0; i < len; i++) {
@@ -360,23 +356,20 @@ MarkXMLRange(JSTracer *trc, size_t len, 
             Mark(trc, xml);
         }
     }
 }
 
 void
 MarkId(JSTracer *trc, jsid id)
 {
-    if (JSID_IS_STRING(id)) {
-        JSString *str = JSID_TO_STRING(id);
-        if (!str->isStaticAtom())
-            Mark(trc, str);
-    } else if (JS_UNLIKELY(JSID_IS_OBJECT(id))) {
+    if (JSID_IS_STRING(id))
+        Mark(trc, JSID_TO_STRING(id));
+    else if (JS_UNLIKELY(JSID_IS_OBJECT(id)))
         Mark(trc, JSID_TO_OBJECT(id));
-    }
 }
 
 void
 MarkId(JSTracer *trc, jsid id, const char *name)
 {
     JS_SET_TRACING_NAME(trc, name);
     MarkId(trc, id);
 }
@@ -442,19 +435,16 @@ MarkValue(JSTracer *trc, const js::Value
     MarkValueRaw(trc, v);
 }
 
 void
 MarkCrossCompartmentValue(JSTracer *trc, const js::Value &v, const char *name)
 {
     if (v.isMarkable()) {
         js::gc::Cell *cell = (js::gc::Cell *)v.toGCThing();
-        unsigned kind = v.gcKind();
-        if (kind == JSTRACE_STRING && ((JSString *)cell)->isStaticAtom())
-            return;
         JSRuntime *rt = trc->context->runtime;
         if (rt->gcCurrentCompartment && cell->compartment() != rt->gcCurrentCompartment)
             return;
 
         MarkValue(trc, v, name);
     }
 }
 
@@ -599,41 +589,36 @@ PrintPropertyMethod(JSTracer *trc, char 
 }
 
 static inline void
 ScanValue(GCMarker *gcmarker, const Value &v)
 {
     if (v.isMarkable()) {
         JSGCTraceKind kind = v.gcKind();
         if (kind == JSTRACE_STRING) {
-            JSString *str = (JSString *)v.toGCThing();
-            if (!str->isStaticAtom())
-                PushMarkStack(gcmarker, str);
+            PushMarkStack(gcmarker, v.toString());
         } else {
             JS_ASSERT(kind == JSTRACE_OBJECT);
-            PushMarkStack(gcmarker, (JSObject *)v.toGCThing());
+            PushMarkStack(gcmarker, &v.toObject());
         }
     }
 }
 
 static void
 ScanShape(GCMarker *gcmarker, const Shape *shape)
 {
 restart:
     JSRuntime *rt = gcmarker->context->runtime;
     if (rt->gcRegenShapes)
         shape->shapeid = js_RegenerateShapeForGC(rt);
 
-    if (JSID_IS_STRING(shape->propid)) {
-        JSString *str = JSID_TO_STRING(shape->propid);
-        if (!str->isStaticAtom())
-            PushMarkStack(gcmarker, str);
-    } else if (JS_UNLIKELY(JSID_IS_OBJECT(shape->propid))) {
+    if (JSID_IS_STRING(shape->propid))
+        PushMarkStack(gcmarker, JSID_TO_STRING(shape->propid));
+    else if (JS_UNLIKELY(JSID_IS_OBJECT(shape->propid)))
         PushMarkStack(gcmarker, JSID_TO_OBJECT(shape->propid));
-    }
 
     if (shape->hasGetterValue() && shape->getter())
         PushMarkStack(gcmarker, shape->getterObject());
     if (shape->hasSetterValue() && shape->setter())
         PushMarkStack(gcmarker, shape->setterObject());
 
     if (shape->isMethod())
         PushMarkStack(gcmarker, &shape->methodObject());
@@ -889,21 +874,18 @@ restart:
 
 static void
 ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
 {
     if (!type->singleton) {
         unsigned count = type->getPropertyCount();
         for (unsigned i = 0; i < count; i++) {
             types::Property *prop = type->getProperty(i);
-            if (prop && JSID_IS_STRING(prop->id)) {
-                JSString *str = JSID_TO_STRING(prop->id);
-                if (!str->isStaticAtom())
-                    PushMarkStack(gcmarker, str);
-            }
+            if (prop && JSID_IS_STRING(prop->id))
+                PushMarkStack(gcmarker, JSID_TO_STRING(prop->id));
         }
     }
 
     if (type->emptyShapes) {
         int count = FINALIZE_OBJECT_LAST - FINALIZE_OBJECT0 + 1;
         for (int i = 0; i < count; i++) {
             if (type->emptyShapes[i])
                 PushMarkStack(gcmarker, type->emptyShapes[i]);
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -5932,17 +5932,17 @@ TypeCompartment::sweep(JSContext *cx)
             JS_ASSERT(entry.object->proto == key.proto);
 
             bool remove = false;
             if (!entry.object->isMarked())
                 remove = true;
             for (unsigned i = 0; !remove && i < key.nslots; i++) {
                 if (JSID_IS_STRING(key.ids[i])) {
                     JSString *str = JSID_TO_STRING(key.ids[i]);
-                    if (!str->isStaticAtom() && !str->isMarked())
+                    if (!str->isMarked())
                         remove = true;
                 }
                 JS_ASSERT(!entry.types[i].isSingleObject());
                 if (entry.types[i].isTypeObject() && !entry.types[i].typeObject()->isMarked())
                     remove = true;
             }
 
             if (remove) {
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -3832,17 +3832,17 @@ END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2)
 BEGIN_CASE(JSOP_GETELEM)
 {
     Value &lref = regs.sp[-2];
     Value &rref = regs.sp[-1];
     if (lref.isString() && rref.isInt32()) {
         JSString *str = lref.toString();
         int32_t i = rref.toInt32();
         if (size_t(i) < str->length()) {
-            str = JSAtom::getUnitStringForElement(cx, str, size_t(i));
+            str = cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(i));
             if (!str)
                 goto error;
             regs.sp--;
             regs.sp[-1].setString(str);
             TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
             len = JSOP_GETELEM_LENGTH;
             DO_NEXT_OP(len);
         }
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1020,18 +1020,18 @@ js_IteratorNext(JSContext *cx, JSObject 
             *rval = IdToValue(*ni->current());
             ni->incCursor();
 
             if (rval->isString())
                 return true;
 
             JSString *str;
             jsint i;
-            if (rval->isInt32() && JSAtom::hasIntStatic(i = rval->toInt32())) {
-                str = &JSAtom::intStatic(i);
+            if (rval->isInt32() && StaticStrings::hasInt(i = rval->toInt32())) {
+                str = cx->runtime->staticStrings.getInt(i);
             } else {
                 str = js_ValueToString(cx, *rval);
                 if (!str)
                     return false;
             }
 
             rval->setString(str);
             return true;
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -632,18 +632,18 @@ ToCStringBuf::~ToCStringBuf()
         UnwantedForeground::free_(dbuf);
 }
 
 JSString * JS_FASTCALL
 js_IntToString(JSContext *cx, int32 si)
 {
     uint32 ui;
     if (si >= 0) {
-        if (JSAtom::hasIntStatic(si))
-            return &JSAtom::intStatic(si);
+        if (StaticStrings::hasInt(si))
+            return cx->runtime->staticStrings.getInt(si);
         ui = si;
     } else {
         ui = uint32(-si);
         JS_ASSERT_IF(si == INT32_MIN, ui == uint32(INT32_MAX) + 1);
     }
 
     JSCompartment *c = cx->compartment;
     if (JSString *str = c->dtoaCache.lookup(10, si))
@@ -1199,27 +1199,25 @@ js_NumberToStringWithBase(JSContext *cx,
      */
     if (base < 2 || base > 36)
         return NULL;
 
     JSCompartment *c = cx->compartment;
 
     int32_t i;
     if (JSDOUBLE_IS_INT32(d, &i)) {
-        if (base == 10 && JSAtom::hasIntStatic(i))
-            return &JSAtom::intStatic(i);
-#ifdef JS_HAS_STATIC_STRINGS
+        if (base == 10 && StaticStrings::hasInt(i))
+            return cx->runtime->staticStrings.getInt(i);
         if (jsuint(i) < jsuint(base)) {
             if (i < 10)
-                return &JSAtom::intStatic(i);
+                return cx->runtime->staticStrings.getInt(i);
             jschar c = 'a' + i - 10;
-            JS_ASSERT(JSAtom::hasUnitStatic(c));
-            return &JSAtom::unitStatic(c);
+            JS_ASSERT(StaticStrings::hasUnit(c));
+            return cx->runtime->staticStrings.getUnit(c);
         }
-#endif
 
         if (JSFlatString *str = c->dtoaCache.lookup(base, d))
             return str;
 
         numStr = IntToCString(&cbuf, i, base);
         JS_ASSERT(!cbuf.dbuf && numStr >= cbuf.sbuf && numStr < cbuf.sbuf + cbuf.sbufSize);
     } else {
         if (JSFlatString *str = c->dtoaCache.lookup(base, d))
@@ -1255,18 +1253,18 @@ NumberToString(JSContext *cx, jsdouble d
     if (JSString *str = js_NumberToStringWithBase(cx, d, 10))
         return &str->asFixed();
     return NULL;
 }
 
 JSFixedString *
 IndexToString(JSContext *cx, uint32 index)
 {
-    if (JSAtom::hasUintStatic(index))
-        return &JSAtom::uintStatic(index);
+    if (StaticStrings::hasUint(index))
+        return cx->runtime->staticStrings.getUint(index);
 
     JSCompartment *c = cx->compartment;
     if (JSFixedString *str = c->dtoaCache.lookup(10, index))
         return str;
 
     JSShortString *str = js_NewGCShortString(cx);
     if (!str)
         return NULL;
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -367,17 +367,17 @@ str_resolve(JSContext *cx, JSObject *obj
 {
     if (!JSID_IS_INT(id))
         return JS_TRUE;
 
     JSString *str = obj->getPrimitiveThis().toString();
 
     jsint slot = JSID_TO_INT(id);
     if ((size_t)slot < str->length()) {
-        JSString *str1 = JSAtom::getUnitStringForElement(cx, str, size_t(slot));
+        JSString *str1 = cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(slot));
         if (!str1)
             return JS_FALSE;
         if (!obj->defineElement(cx, uint32(slot), StringValue(str1), NULL, NULL,
                                 STRING_ELEMENT_ATTRS)) {
             return JS_FALSE;
         }
         *objp = obj;
     }
@@ -725,17 +725,17 @@ js_str_charAt(JSContext *cx, uintN argc,
         if (argc > 0 && !ToInteger(cx, vp[2], &d))
             return false;
 
         if (d < 0 || str->length() <= d)
             goto out_of_range;
         i = (jsint) d;
     }
 
-    str = JSAtom::getUnitStringForElement(cx, str, size_t(i));
+    str = cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(i));
     if (!str)
         return false;
     vp->setString(str);
     return true;
 
   out_of_range:
     vp->setString(cx->runtime->emptyString);
     return true;
@@ -2606,17 +2606,17 @@ str_slice(JSContext *cx, uintN argc, Val
         begin = vp[2].toInt32();
         end = str->length();
         if (begin <= end) {
             length = end - begin;
             if (length == 0) {
                 str = cx->runtime->emptyString;
             } else {
                 str = (length == 1)
-                      ? JSAtom::getUnitStringForElement(cx, str, begin)
+                      ? cx->runtime->staticStrings.getUnitStringForElement(cx, str, begin)
                       : js_NewDependentString(cx, str, begin, length);
                 if (!str)
                     return JS_FALSE;
             }
             vp->setString(str);
             return JS_TRUE;
         }
     }
@@ -2824,17 +2824,17 @@ str_sub(JSContext *cx, uintN argc, Value
 #endif /* JS_HAS_STR_HTML_HELPERS */
 
 #ifdef JS_TRACER
 JSString* FASTCALL
 js_String_getelem(JSContext* cx, JSString* str, int32 i)
 {
     if ((size_t)i >= str->length())
         return NULL;
-    return JSAtom::getUnitStringForElement(cx, str, size_t(i));
+    return cx->runtime->staticStrings.getUnitStringForElement(cx, str, size_t(i));
 }
 #endif
 
 JS_DEFINE_TRCINFO_1(str_concat,
     (3, (extern, STRING_RETRY, js_ConcatStrings, CONTEXT, THIS_STRING, STRING,
          1, nanojit::ACCSET_NONE)))
 
 static JSFunctionSpec string_methods[] = {
@@ -2888,214 +2888,16 @@ static JSFunctionSpec string_methods[] =
     JS_FN("blink",             str_blink,             0,0),
     JS_FN("sup",               str_sup,               0,0),
     JS_FN("sub",               str_sub,               0,0),
 #endif
 
     JS_FS_END
 };
 
-#ifdef JS_HAS_STATIC_STRINGS
-
-/*
- * Set up some tools to make it easier to generate large tables. After constant
- * folding, for each n, Rn(0) is the comma-separated list R(0), R(1), ..., R(2^n-1).
- * Similary, Rn(k) (for any k and n) generates the list R(k), R(k+1), ..., R(k+2^n-1).
- * To use this, define R appropriately, then use Rn(0) (for some value of n), then
- * undefine R.
- */
-#define R2(n)  R(n),   R((n) + (1 << 0)),    R((n) + (2 << 0)),    R((n) + (3 << 0))
-#define R4(n)  R2(n),  R2((n) + (1 << 2)),   R2((n) + (2 << 2)),   R2((n) + (3 << 2))
-#define R6(n)  R4(n),  R4((n) + (1 << 4)),   R4((n) + (2 << 4)),   R4((n) + (3 << 4))
-#define R8(n)  R6(n),  R6((n) + (1 << 6)),   R6((n) + (2 << 6)),   R6((n) + (3 << 6))
-#define R10(n) R8(n),  R8((n) + (1 << 8)),   R8((n) + (2 << 8)),   R8((n) + (3 << 8))
-#define R12(n) R10(n), R10((n) + (1 << 10)), R10((n) + (2 << 10)), R10((n) + (3 << 10))
-
-#define R3(n) R2(n), R2((n) + (1 << 2))
-#define R7(n) R6(n), R6((n) + (1 << 6))
-
-#define BUILD_LENGTH_AND_FLAGS(length, flags)                                 \
-    (((length) << JSString::LENGTH_SHIFT) | (flags))
-
-/*
- * Declare unit strings. Pack the string data itself into the mInlineChars
- * place in the header.
- */
-#define R(c) {                                                                \
-    BUILD_LENGTH_AND_FLAGS(1, JSString::STATIC_ATOM_FLAGS),                   \
-    { (jschar *)(uintptr_t(unitStaticTable + (c)) +                           \
-      offsetof(JSString::Data, inlineStorage)) },                             \
-    { {(c), 0x00} } }
-
-/*
- * For all the pragma pack usage in this file, the following logic applies:
- *          To apply:       To reset:
- * Sun CC:  pack(#)       / pack(0)
- * IBM xlC: pack(#)       / pack(pop)
- * HP aCC:  pack #        / pack
- * Others:  pack(push, #) / pack(pop)
- * The -Dlint case is explicitly excluded because GCC will error out when
- * pack pragmas are used on unsupported platforms. If GCC is being used
- * simply for error checking, these errors will be avoided.
- */
-
-#if defined(__SUNPRO_CC) || defined(__xlC__)
-#pragma pack(8)
-#elif defined(__HP_aCC)
-#pragma pack 8
-#elif !defined(lint)
-#pragma pack(push, 8)
-#endif
-
-const JSString::Data JSAtom::unitStaticTable[]
-#if defined(__GNUC__) || defined(__xlC__)
-__attribute__ ((aligned (8)))
-#endif
-= { R8(0) };
-
-#if defined(__SUNPRO_CC)
-#pragma pack(0)
-#elif defined(__HP_aCC)
-#pragma pack
-#elif !defined(lint)
-#pragma pack(pop)
-#endif
-
-#undef R
-
-/*
- * Declare length-2 strings. We only store strings where both characters are
- * alphanumeric. The lower 10 short chars are the numerals, the next 26 are
- * the lowercase letters, and the next 26 are the uppercase letters.
- */
-#define TO_SMALL_CHAR(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' :              \
-                          (c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 10 :         \
-                          (c) >= 'A' && (c) <= 'Z' ? (c) - 'A' + 36 :         \
-                          JSAtom::INVALID_SMALL_CHAR)
-
-#define R TO_SMALL_CHAR
-
-const JSAtom::SmallChar JSAtom::toSmallChar[] = { R7(0) };
-
-#undef R
-
-/*
- * This is used when we generate our table of short strings, so the compiler is
- * happier if we use |c| as few times as possible.
- */
-#define FROM_SMALL_CHAR(c) ((c) + ((c) < 10 ? '0' :      \
-                                   (c) < 36 ? 'a' - 10 : \
-                                   'A' - 36))
-#define R FROM_SMALL_CHAR
-
-const jschar JSAtom::fromSmallChar[] = { R6(0) };
-
-#undef R
-
-/*
- * For code-generation ease, length-2 strings are encoded as 12-bit int values,
- * where the upper 6 bits is the first character and the lower 6 bits is the
- * second character.
- */
-#define R(c) {                                                                \
-    BUILD_LENGTH_AND_FLAGS(2, JSString::STATIC_ATOM_FLAGS),                   \
-    { (jschar *)(uintptr_t(length2StaticTable + (c)) +                        \
-      offsetof(JSString::Data, inlineStorage)) },                             \
-    { {FROM_SMALL_CHAR((c) >> 6), FROM_SMALL_CHAR((c) & 0x3F), 0x00} } }
-
-#if defined(__SUNPRO_CC) || defined(__xlC__)
-#pragma pack(8)
-#elif defined(__HP_aCC)
-#pragma pack 8
-#elif !defined(lint)
-#pragma pack(push, 8)
-#endif
-
-const JSString::Data JSAtom::length2StaticTable[]
-#if defined(__GNUC__) || defined(__xlC__)
-__attribute__ ((aligned (8)))
-#endif
-= { R12(0) };
-
-#if defined(__SUNPRO_CC)
-#pragma pack(0)
-#elif defined(__HP_aCC)
-#pragma pack
-#elif !defined(lint)
-#pragma pack(pop)
-#endif
-
-#undef R
-
-/*
- * Declare int strings. Only int strings from 100 to 255 actually have to be
- * generated, since the rest are either unit strings or length-2 strings. To
- * avoid the runtime cost of figuring out where to look for the string for a
- * particular integer, we precompute a table of JSString*s which refer to the
- * correct location of the int string.
- */
-#define R(c) {                                                                \
-    BUILD_LENGTH_AND_FLAGS(3, JSString::STATIC_ATOM_FLAGS),                   \
-    { (jschar *)(uintptr_t(hundredStaticTable + ((c) - 100)) +                \
-      offsetof(JSString::Data, inlineStorage)) },                             \
-    { {((c) / 100) + '0', ((c) / 10 % 10) + '0', ((c) % 10) + '0', 0x00} } }
-
-
-JS_STATIC_ASSERT(100 + (1 << 7) + (1 << 4) + (1 << 3) + (1 << 2) == 256);
-
-#if defined(__SUNPRO_CC) || defined(__xlC__)
-#pragma pack(8)
-#elif defined(__HP_aCC)
-#pragma pack 8
-#elif !defined(lint)
-#pragma pack(push, 8)
-#endif
-
-const JSString::Data JSAtom::hundredStaticTable[]
-#if defined(__GNUC__) || defined(__xlC__)
-__attribute__ ((aligned (8)))
-#endif
-= { R7(100), /* 100 through 227 */
-    R4(100 + (1 << 7)), /* 228 through 243 */
-    R3(100 + (1 << 7) + (1 << 4)), /* 244 through 251 */
-    R2(100 + (1 << 7) + (1 << 4) + (1 << 3)) /* 252 through 255 */
-};
-
-#undef R
-
-#define R(c) ((c) < 10 ? JSAtom::unitStaticTable + ((c) + '0') :              \
-              (c) < 100 ? JSAtom::length2StaticTable +                        \
-              ((size_t)TO_SMALL_CHAR(((c) / 10) + '0') << 6) +                \
-              TO_SMALL_CHAR(((c) % 10) + '0') :                               \
-              JSAtom::hundredStaticTable + ((c) - 100))
-
-const JSString::Data *const JSAtom::intStaticTable[] = { R8(0) };
-
-#undef R
-
-#if defined(__SUNPRO_CC)
-#pragma pack(0)
-#elif defined(__HP_aCC)
-#pragma pack
-#elif !defined(lint)
-#pragma pack(pop)
-#endif
-
-#undef R2
-#undef R4
-#undef R6
-#undef R8
-#undef R10
-#undef R12
-
-#undef R3
-#undef R7
-
-#endif  /* defined(JS_HAS_STATIC_STRINGS) */
-
 JSBool
 js_String(JSContext *cx, uintN argc, Value *vp)
 {
     Value *argv = vp + 2;
 
     JSString *str;
     if (argc > 0) {
         str = js_ValueToString(cx, argv[0]);
@@ -3120,18 +2922,18 @@ static JSBool
 str_fromCharCode(JSContext *cx, uintN argc, Value *vp)
 {
     Value *argv = JS_ARGV(cx, vp);
     JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
     if (argc == 1) {
         uint16_t code;
         if (!ValueToUint16(cx, argv[0], &code))
             return JS_FALSE;
-        if (JSAtom::hasUnitStatic(code)) {
-            vp->setString(&JSAtom::unitStatic(code));
+        if (StaticStrings::hasUnit(code)) {
+            vp->setString(cx->runtime->staticStrings.getUnit(code));
             return JS_TRUE;
         }
         argv[0].setInt32(code);
     }
     jschar *chars = (jschar *) cx->malloc_((argc + 1) * sizeof(jschar));
     if (!chars)
         return JS_FALSE;
     for (uintN i = 0; i < argc; i++) {
@@ -3153,18 +2955,18 @@ str_fromCharCode(JSContext *cx, uintN ar
 }
 
 #ifdef JS_TRACER
 static JSString* FASTCALL
 String_fromCharCode(JSContext* cx, int32 i)
 {
     JS_ASSERT(JS_ON_TRACE(cx));
     jschar c = (jschar)i;
-    if (JSAtom::hasUnitStatic(c))
-        return &JSAtom::unitStatic(c);
+    if (StaticStrings::hasUnit(c))
+        return cx->runtime->staticStrings.getUnit(c);
     return js_NewStringCopyN(cx, &c, 1);
 }
 #endif
 
 JS_DEFINE_TRCINFO_1(str_fromCharCode,
     (2, (static, STRING_RETRY, String_fromCharCode, CONTEXT, INT32, 1, nanojit::ACCSET_NONE)))
 
 static JSFunctionSpec string_static_methods[] = {
@@ -3370,17 +3172,17 @@ js_NewDependentString(JSContext *cx, JSS
     if (!base)
         return NULL;
 
     if (start == 0 && length == base->length())
         return base;
 
     const jschar *chars = base->chars() + start;
 
-    if (JSLinearString *staticStr = JSAtom::lookupStatic(chars, length))
+    if (JSLinearString *staticStr = cx->runtime->staticStrings.lookup(chars, length))
         return staticStr;
 
     JSLinearString *s = JSDependentString::new_(cx, base, chars, length);
     Probes::createString(cx, s, length);
     return s;
 }
 
 JSFixedString *
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -11511,32 +11511,16 @@ TraceRecorder::callNative(uintN argc, JS
                     LIns* abs_ins = w.name(w.cmovi(isNeg_ins, neg_ins, a), "abs");
                     set(&vp[0], w.i2d(abs_ins));
                     pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK;
                     return RECORD_CONTINUE;
                 }
             }
             if (vp[1].isString()) {
                 JSString *str = vp[1].toString();
-#ifdef JS_HAS_STATIC_STRINGS
-                if (native == js_str_charAt) {
-                    jsdouble i = vp[2].toNumber();
-                    if (JSDOUBLE_IS_NaN(i))
-                      i = 0;
-                    if (i < 0 || i >= str->length())
-                        RETURN_STOP("charAt out of bounds");
-                    LIns* str_ins = get(&vp[1]);
-                    LIns* idx_ins = get(&vp[2]);
-                    LIns* char_ins;
-                    CHECK_STATUS(getCharAt(str, str_ins, idx_ins, mode, &char_ins));
-                    set(&vp[0], char_ins);
-                    pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK;
-                    return RECORD_CONTINUE;
-                } else
-#endif
                 if (native == js_str_charCodeAt) {
                     jsdouble i = vp[2].toNumber();
                     if (JSDOUBLE_IS_NaN(i))
                       i = 0;
                     if (i < 0 || i >= str->length())
                         RETURN_STOP("charCodeAt out of bounds");
                     LIns* str_ins = get(&vp[1]);
                     LIns* idx_ins = get(&vp[2]);
@@ -12874,63 +12858,16 @@ TraceRecorder::getCharCodeAt(JSString *s
           snapshot(MISMATCH_EXIT));
     *out = w.i2d(w.getStringChar(str_ins, idx_ins));
     return RECORD_CONTINUE;
 }
 
 JS_STATIC_ASSERT(sizeof(JSString) == 16 || sizeof(JSString) == 32);
 
 
-#ifdef JS_HAS_STATIC_STRINGS
-JS_REQUIRES_STACK LIns*
-TraceRecorder::getUnitString(LIns* str_ins, LIns* idx_ins)
-{
-    LIns *ch_ins = w.getStringChar(str_ins, idx_ins);
-    guard(true, w.ltuiN(ch_ins, JSAtom::UNIT_STATIC_LIMIT), MISMATCH_EXIT);
-    JS_STATIC_ASSERT(sizeof(JSString) == 16 || sizeof(JSString) == 32);
-    return w.addp(w.nameImmpNonGC(JSAtom::unitStaticTable),
-                  w.lshpN(w.ui2p(ch_ins), (sizeof(JSString) == 16) ? 4 : 5));
-}
-
-JS_REQUIRES_STACK RecordingStatus
-TraceRecorder::getCharAt(JSString *str, LIns* str_ins, LIns* idx_ins, JSOp mode, LIns** out)
-{
-    CHECK_STATUS(makeNumberInt32(idx_ins, &idx_ins));
-    idx_ins = w.ui2p(idx_ins);
-    LIns *lengthAndFlags_ins = w.ldpStringLengthAndFlags(str_ins);
-    if (MaybeBranch mbr = w.jt(w.eqp0(w.andp(lengthAndFlags_ins,
-                                             w.nameImmw(JSString::ROPE_BIT)))))
-    {
-        LIns *args[] = { str_ins, cx_ins };
-        LIns *ok_ins = w.call(&js_FlattenOnTrace_ci, args);
-        guard(false, w.eqi0(ok_ins), OOM_EXIT);
-        w.label(mbr);
-    }
-
-    LIns* inRange = w.ltup(idx_ins, w.rshupN(lengthAndFlags_ins, JSString::LENGTH_SHIFT));
-
-    if (mode == JSOP_GETELEM) {
-        guard(true, inRange, MISMATCH_EXIT);
-
-        *out = getUnitString(str_ins, idx_ins);
-    } else {
-        LIns *phi_ins = w.allocp(sizeof(JSString *));
-        w.stAlloc(w.nameImmpNonGC(cx->runtime->emptyString), phi_ins);
-
-        if (MaybeBranch mbr = w.jf(inRange)) {
-            LIns *unitstr_ins = getUnitString(str_ins, idx_ins);
-            w.stAlloc(unitstr_ins, phi_ins);
-            w.label(mbr);
-        }
-        *out = w.ldpAlloc(phi_ins);
-    }
-    return RECORD_CONTINUE;
-}
-#endif
-
 // Typed array tracing depends on EXPANDED_LOADSTORE and F2I
 #if NJ_EXPANDED_LOADSTORE_SUPPORTED && NJ_F2I_SUPPORTED
 static bool OkToTraceTypedArrays = true;
 #else
 static bool OkToTraceTypedArrays = false;
 #endif
 
 JS_REQUIRES_STACK void
@@ -12954,31 +12891,16 @@ TraceRecorder::record_JSOP_GETELEM()
     bool call = *cx->regs().pc == JSOP_CALLELEM;
 
     Value& idx = stackval(-1);
     Value& lval = stackval(-2);
 
     LIns* obj_ins = get(&lval);
     LIns* idx_ins = get(&idx);
 
-#ifdef JS_HAS_STATIC_STRINGS
-    // Special case for array-like access of strings.
-    if (lval.isString() && hasInt32Repr(idx)) {
-        if (call)
-            RETURN_STOP_A("JSOP_CALLELEM on a string");
-        int i = asInt32(idx);
-        if (size_t(i) >= lval.toString()->length())
-            RETURN_STOP_A("Invalid string index in JSOP_GETELEM");
-        LIns* char_ins;
-        CHECK_STATUS_A(getCharAt(lval.toString(), obj_ins, idx_ins, JSOP_GETELEM, &char_ins));
-        set(&lval, char_ins);
-        return ARECORD_CONTINUE;
-    }
-#endif
-
     if (lval.isPrimitive())
         RETURN_STOP_A("JSOP_GETLEM on a primitive");
     RETURN_IF_XML_A(lval);
 
     JSObject* obj = &lval.toObject();
     if (obj == globalObj)
         RETURN_STOP_A("JSOP_GETELEM on global");
     LIns* v_ins;
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -1393,22 +1393,16 @@ class TraceRecorder
                                                                   Value* outp);
     JS_REQUIRES_STACK RecordingStatus getPropertyWithScriptGetter(JSObject *obj,
                                                                   nanojit::LIns* obj_ins,
                                                                   const js::Shape* shape);
 
     JS_REQUIRES_STACK RecordingStatus getCharCodeAt(JSString *str,
                                                     nanojit::LIns* str_ins, nanojit::LIns* idx_ins,
                                                     nanojit::LIns** out_ins);
-#ifdef JS_HAS_STATIC_STRINGS
-    JS_REQUIRES_STACK nanojit::LIns* getUnitString(nanojit::LIns* str_ins, nanojit::LIns* idx_ins);
-    JS_REQUIRES_STACK RecordingStatus getCharAt(JSString *str,
-                                                nanojit::LIns* str_ins, nanojit::LIns* idx_ins,
-                                                JSOp mode, nanojit::LIns** out_ins);
-#endif
 
     JS_REQUIRES_STACK RecordingStatus initOrSetPropertyByName(nanojit::LIns* obj_ins,
                                                               Value* idvalp, Value* rvalp,
                                                               bool init);
     JS_REQUIRES_STACK RecordingStatus initOrSetPropertyByIndex(nanojit::LIns* obj_ins,
                                                                nanojit::LIns* index_ins,
                                                                Value* rvalp, bool init);
     JS_REQUIRES_STACK AbortableRecordingStatus setElem(int lval_spindex, int idx_spindex,
--- a/js/src/methodjit/FastBuiltins.cpp
+++ b/js/src/methodjit/FastBuiltins.cpp
@@ -274,22 +274,23 @@ mjit::Compiler::compileGetChar(FrameEntr
     masm.lshiftPtr(Imm32(1), reg1);
     masm.addPtr(reg1, reg2);
     masm.load16(Address(reg2), reg2);
 
     /* Convert char code to string. */
     if (mode == GetChar) {
         /* Slow path if there's no unit string for this character. */
         Jump notUnitString = masm.branch32(Assembler::AboveOrEqual, reg2,
-                                           Imm32(JSAtom::UNIT_STATIC_LIMIT));
+                                           Imm32(StaticStrings::UNIT_STATIC_LIMIT));
         stubcc.linkExit(notUnitString, Uses(3));
 
         /* Load unit string in reg2. */
-        masm.lshiftPtr(Imm32(sizeof(JSString) == 16 ? 4 : 5), reg2);
-        masm.addPtr(ImmPtr(JSAtom::unitStaticTable), reg2);
+        masm.lshiftPtr(Imm32(sizeof(JSAtom *) == 4 ? 2 : 3), reg2);
+        masm.addPtr(ImmPtr(&cx->runtime->staticStrings.unitStaticTable), reg2);
+        masm.loadPtr(Address(reg2), reg2);
     }
 
     if (thisValue->isConstant())
         frame.freeReg(strReg);
     if (arg->isConstant())
         frame.freeReg(argReg);
     frame.freeReg(reg1);
 
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -431,17 +431,17 @@ stubs::GetElem(VMFrame &f)
     FrameRegs &regs = f.regs;
 
     Value &lref = regs.sp[-2];
     Value &rref = regs.sp[-1];
     if (lref.isString() && rref.isInt32()) {
         JSString *str = lref.toString();
         int32_t i = rref.toInt32();
         if ((size_t)i < str->length()) {
-            str = JSAtom::getUnitStringForElement(cx, str, (size_t)i);
+            str = f.cx->runtime->staticStrings.getUnitStringForElement(cx, str, (size_t)i);
             if (!str)
                 THROW();
             f.regs.sp[-2].setString(str);
             return;
         }
     }
 
     if (lref.isMagic(JS_LAZY_ARGUMENTS)) {
--- a/js/src/vm/String-inl.h
+++ b/js/src/vm/String-inl.h
@@ -1,9 +1,9 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=4 sw=4 et tw=79 ft=cpp:
  *
  * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
@@ -188,103 +188,102 @@ JSExternalString::new_(JSContext *cx, co
     JSExternalString *str = js_NewGCExternalString(cx);
     if (!str)
         return NULL;
     str->init(chars, length, type, closure);
     cx->runtime->updateMallocCounter((length + 1) * sizeof(jschar));
     return str;
 }
 
-#ifdef JS_HAS_STATIC_STRINGS
 inline bool
-JSAtom::fitsInSmallChar(jschar c)
+js::StaticStrings::fitsInSmallChar(jschar c)
 {
     return c < SMALL_CHAR_LIMIT && toSmallChar[c] != INVALID_SMALL_CHAR;
 }
 
 inline bool
-JSAtom::hasUnitStatic(jschar c)
+js::StaticStrings::hasUnit(jschar c)
 {
     return c < UNIT_STATIC_LIMIT;
 }
 
-inline JSStaticAtom &
-JSAtom::unitStatic(jschar c)
+inline JSAtom *
+js::StaticStrings::getUnit(jschar c)
 {
-    JS_ASSERT(hasUnitStatic(c));
-    return (JSStaticAtom &)unitStaticTable[c];
+    JS_ASSERT(hasUnit(c));
+    return unitStaticTable[c];
 }
 
 inline bool
-JSAtom::hasUintStatic(uint32 u)
+js::StaticStrings::hasUint(uint32 u)
 {
     return u < INT_STATIC_LIMIT;
 }
 
-inline JSStaticAtom &
-JSAtom::uintStatic(uint32 u)
+inline JSAtom *
+js::StaticStrings::getUint(uint32 u)
 {
-    JS_ASSERT(hasUintStatic(u));
-    return *reinterpret_cast<JSStaticAtom *>(const_cast<JSString::Data *>(intStaticTable[u]));
+    JS_ASSERT(hasUint(u));
+    return intStaticTable[u];
 }
 
 inline bool
-JSAtom::hasIntStatic(int32 i)
+js::StaticStrings::hasInt(int32 i)
 {
     return uint32(i) < INT_STATIC_LIMIT;
 }
 
-inline JSStaticAtom &
-JSAtom::intStatic(jsint i)
+inline JSAtom *
+js::StaticStrings::getInt(jsint i)
 {
-    JS_ASSERT(hasIntStatic(i));
-    return uintStatic(uint32(i));
+    JS_ASSERT(hasInt(i));
+    return getUint(uint32(i));
 }
 
 inline JSLinearString *
-JSAtom::getUnitStringForElement(JSContext *cx, JSString *str, size_t index)
+js::StaticStrings::getUnitStringForElement(JSContext *cx, JSString *str, size_t index)
 {
     JS_ASSERT(index < str->length());
     const jschar *chars = str->getChars(cx);
     if (!chars)
         return NULL;
     jschar c = chars[index];
     if (c < UNIT_STATIC_LIMIT)
-        return &unitStatic(c);
+        return getUnit(c);
     return js_NewDependentString(cx, str, index, 1);
 }
 
-inline JSStaticAtom &
-JSAtom::length2Static(jschar c1, jschar c2)
+inline JSAtom *
+js::StaticStrings::getLength2(jschar c1, jschar c2)
 {
     JS_ASSERT(fitsInSmallChar(c1));
     JS_ASSERT(fitsInSmallChar(c2));
     size_t index = (((size_t)toSmallChar[c1]) << 6) + toSmallChar[c2];
-    return (JSStaticAtom &)length2StaticTable[index];
+    return length2StaticTable[index];
 }
 
-inline JSStaticAtom &
-JSAtom::length2Static(uint32 i)
+inline JSAtom *
+js::StaticStrings::getLength2(uint32 i)
 {
     JS_ASSERT(i < 100);
-    return length2Static('0' + i / 10, '0' + i % 10);
+    return getLength2('0' + i / 10, '0' + i % 10);
 }
 
 /* Get a static atomized string for chars if possible. */
-inline JSStaticAtom *
-JSAtom::lookupStatic(const jschar *chars, size_t length)
+inline JSAtom *
+js::StaticStrings::lookup(const jschar *chars, size_t length)
 {
     switch (length) {
       case 1:
         if (chars[0] < UNIT_STATIC_LIMIT)
-            return &unitStatic(chars[0]);
+            return getUnit(chars[0]);
         return NULL;
       case 2:
         if (fitsInSmallChar(chars[0]) && fitsInSmallChar(chars[1]))
-            return &length2Static(chars[0], chars[1]);
+            return getLength2(chars[0], chars[1]);
         return NULL;
       case 3:
         /*
          * Here we know that JSString::intStringTable covers only 256 (or at least
          * not 1000 or more) chars. We rely on order here to resolve the unit vs.
          * int string/length-2 string atom identity issue by giving priority to unit
          * strings for "0" through "9" and length-2 strings for "10" through "99".
          */
@@ -292,105 +291,29 @@ JSAtom::lookupStatic(const jschar *chars
         if ('1' <= chars[0] && chars[0] <= '9' &&
             '0' <= chars[1] && chars[1] <= '9' &&
             '0' <= chars[2] && chars[2] <= '9') {
             jsint i = (chars[0] - '0') * 100 +
                       (chars[1] - '0') * 10 +
                       (chars[2] - '0');
 
             if (jsuint(i) < INT_STATIC_LIMIT)
-                return &intStatic(i);
+                return getInt(i);
         }
         return NULL;
     }
 
     return NULL;
 }
 
-#else  /* defined(JS_HAS_STATIC_STRINGS) */
-
-inline bool
-JSAtom::fitsInSmallChar(jschar c)
-{
-    return false;
-}
-
-inline bool
-JSAtom::hasUnitStatic(jschar c)
-{
-    return false;
-}
-
-inline JSStaticAtom &
-JSAtom::unitStatic(jschar c)
-{
-    JS_NOT_REACHED("no static strings");
-    return *(JSStaticAtom *)NULL;
-}
-
-inline bool
-JSAtom::hasUintStatic(uint32 u)
-{
-    return false;
-}
-
-inline JSStaticAtom &
-JSAtom::uintStatic(uint32 u)
-{
-    JS_NOT_REACHED("no static strings");
-    return *(JSStaticAtom *)NULL;
-}
-
-inline bool
-JSAtom::hasIntStatic(int32 i)
-{
-    return false;
-}
-
-inline JSStaticAtom &
-JSAtom::intStatic(jsint i)
-{
-    JS_NOT_REACHED("no static strings");
-    return *(JSStaticAtom *)NULL;
-}
-
-inline JSLinearString *
-JSAtom::getUnitStringForElement(JSContext *cx, JSString *str, size_t index)
-{
-    JS_ASSERT(index < str->length());
-    return js_NewDependentString(cx, str, index, 1);
-}
-
-inline JSStaticAtom &
-JSAtom::length2Static(jschar c1, jschar c2)
-{
-    JS_NOT_REACHED("no static strings");
-    return *(JSStaticAtom *)NULL;
-}
-
-inline JSStaticAtom &
-JSAtom::length2Static(uint32 i)
-{
-    JS_NOT_REACHED("no static strings");
-    return *(JSStaticAtom *)NULL;
-}
-
-/* Get a static atomized string for chars if possible. */
-inline JSStaticAtom *
-JSAtom::lookupStatic(const jschar *chars, size_t length)
-{
-    return NULL;
-}
-#endif /* defined(JS_HAS_STATIC_STRINGS) */
-
 JS_ALWAYS_INLINE void
 JSString::finalize(JSContext *cx)
 {
-    /* Statics are not GC-things and shorts are in a different arena. */
-    JS_ASSERT(!isStaticAtom() && !isShort());
+    /* Shorts are in a different arena. */
+    JS_ASSERT(!isShort());
 
     if (isFlat())
         asFlat().finalize(cx->runtime);
     else
         JS_ASSERT(isDependent() || isRope());
 }
 
 inline void
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -35,16 +35,18 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "mozilla/RangedPtr.h"
 
+#include "jsgcmark.h"
+
 #include "String.h"
 #include "String-inl.h"
 
 using namespace mozilla;
 using namespace js;
 
 bool
 JSString::isShort() const
@@ -73,17 +75,17 @@ JSString::isExternal() const
     JS_ASSERT_IF(is_external, isFixed());
     return is_external;
 }
 
 void
 JSLinearString::mark(JSTracer *)
 {
     JSLinearString *str = this;
-    while (!str->isStaticAtom() && str->markIfUnmarked() && str->isDependent())
+    while (str->markIfUnmarked() && str->isDependent())
         str = str->asDependent().base();
 }
 
 size_t
 JSString::charsHeapSize(JSUsableSizeFun usf)
 {
     /* JSRope: do nothing, we'll count all children chars when we hit the leaf strings. */
     if (isRope())
@@ -109,20 +111,16 @@ JSString::charsHeapSize(JSUsableSizeFun 
     /* JSExternalString: don't count, the chars could be stored anywhere. */
     if (isExternal())
         return 0;
 
     /* JSInlineString, JSShortString, JSInlineAtom, JSShortAtom: the chars are inline. */
     if (isInline())
         return 0;
 
-    /* JSStaticAtom: the chars are static and so not part of the heap. */
-    if (isStaticAtom())
-        return 0;
-
     /* JSAtom, JSFixedString: count the chars. */
     JSFixedString &fixed = asFixed();
     size_t usable = usf((void *)fixed.chars());
     return usable ? usable : length() * sizeof(jschar);
 }
 
 static JS_ALWAYS_INLINE bool
 AllocChars(JSContext *maybecx, size_t length, jschar **chars, size_t *capacity)
@@ -369,8 +367,125 @@ JSFlatString::isElement(uint32 *indexp) 
      */
     if (oldIndex < UINT32_MAX / 10 || (oldIndex == UINT32_MAX / 10 && c <= (UINT32_MAX % 10))) {
         *indexp = index;
         return true;
     }
 
     return false;
 }
+
+/*
+ * Set up some tools to make it easier to generate large tables. After constant
+ * folding, for each n, Rn(0) is the comma-separated list R(0), R(1), ..., R(2^n-1).
+ * Similary, Rn(k) (for any k and n) generates the list R(k), R(k+1), ..., R(k+2^n-1).
+ * To use this, define R appropriately, then use Rn(0) (for some value of n), then
+ * undefine R.
+ */
+#define R2(n) R(n),  R((n) + (1 << 0)),  R((n) + (2 << 0)),  R((n) + (3 << 0))
+#define R4(n) R2(n), R2((n) + (1 << 2)), R2((n) + (2 << 2)), R2((n) + (3 << 2))
+#define R6(n) R4(n), R4((n) + (1 << 4)), R4((n) + (2 << 4)), R4((n) + (3 << 4))
+#define R7(n) R6(n), R6((n) + (1 << 6))
+
+/*
+ * This is used when we generate our table of short strings, so the compiler is
+ * happier if we use |c| as few times as possible.
+ */
+#define FROM_SMALL_CHAR(c) ((c) + ((c) < 10 ? '0' :      \
+                                   (c) < 36 ? 'a' - 10 : \
+                                   'A' - 36))
+
+/*
+ * Declare length-2 strings. We only store strings where both characters are
+ * alphanumeric. The lower 10 short chars are the numerals, the next 26 are
+ * the lowercase letters, and the next 26 are the uppercase letters.
+ */
+#define TO_SMALL_CHAR(c) ((c) >= '0' && (c) <= '9' ? (c) - '0' :              \
+                          (c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 10 :         \
+                          (c) >= 'A' && (c) <= 'Z' ? (c) - 'A' + 36 :         \
+                          StaticStrings::INVALID_SMALL_CHAR)
+
+#define R TO_SMALL_CHAR
+const StaticStrings::SmallChar StaticStrings::toSmallChar[] = { R7(0) };
+#undef R
+
+bool
+StaticStrings::init(JSContext *cx)
+{
+    SwitchToCompartment sc(cx, cx->runtime->atomsCompartment);
+
+    for (uint32 i = 0; i < UNIT_STATIC_LIMIT; i++) {
+        jschar buffer[] = { i, 0x00 };
+        JSFixedString *s = js_NewStringCopyN(cx, buffer, 1);
+        if (!s)
+            return false;
+        unitStaticTable[i] = s->morphAtomizedStringIntoAtom();
+    }
+
+    for (uint32 i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++) {
+        jschar buffer[] = { FROM_SMALL_CHAR(i >> 6), FROM_SMALL_CHAR(i & 0x3F), 0x00 };
+        JSFixedString *s = js_NewStringCopyN(cx, buffer, 2);
+        if (!s)
+            return false;
+        length2StaticTable[i] = s->morphAtomizedStringIntoAtom();
+    }
+
+    for (uint32 i = 0; i < INT_STATIC_LIMIT; i++) {
+        if (i < 10) {
+            intStaticTable[i] = unitStaticTable[i + '0'];
+        } else if (i < 100) {
+            size_t index = ((size_t)TO_SMALL_CHAR((i / 10) + '0') << 6) +
+                TO_SMALL_CHAR((i % 10) + '0');
+            intStaticTable[i] = length2StaticTable[index];
+        } else {
+            jschar buffer[] = { (i / 100) + '0', ((i / 10) % 10) + '0', (i % 10) + '0', 0x00 };
+            JSFixedString *s = js_NewStringCopyN(cx, buffer, 3);
+            if (!s)
+                return false;
+            intStaticTable[i] = s->morphAtomizedStringIntoAtom();
+        }
+    }
+
+    initialized = true;
+    return true;
+}
+
+void
+StaticStrings::trace(JSTracer *trc)
+{
+    if (!initialized)
+        return;
+
+    for (uint32 i = 0; i < UNIT_STATIC_LIMIT; i++)
+        MarkString(trc, unitStaticTable[i], "unit-static-string");
+
+    for (uint32 i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++)
+        MarkString(trc, length2StaticTable[i], "length2-static-string");
+
+    /* This may mark some strings more than once, but so be it. */
+    for (uint32 i = 0; i < INT_STATIC_LIMIT; i++)
+        MarkString(trc, intStaticTable[i], "int-static-string");
+}
+
+bool
+StaticStrings::isStatic(JSAtom *atom)
+{
+    const jschar *chars = atom->chars();
+    switch (atom->length()) {
+      case 1:
+        return (chars[0] < UNIT_STATIC_LIMIT);
+      case 2:
+        return (fitsInSmallChar(chars[0]) && fitsInSmallChar(chars[1]));
+      case 3:
+        if ('1' <= chars[0] && chars[0] <= '9' &&
+            '0' <= chars[1] && chars[1] <= '9' &&
+            '0' <= chars[2] && chars[2] <= '9') {
+            jsint i = (chars[0] - '0') * 100 +
+                      (chars[1] - '0') * 10 +
+                      (chars[2] - '0');
+
+            return (jsuint(i) < INT_STATIC_LIMIT);
+        }
+        return false;
+      default:
+        return false;
+    }
+}
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -1,9 +1,9 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=4 sw=4 et tw=79 ft=cpp:
  *
  * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
@@ -44,22 +44,22 @@
 #include "jscell.h"
 
 class JSString;
 class JSDependentString;
 class JSExtensibleString;
 class JSExternalString;
 class JSLinearString;
 class JSFixedString;
-class JSStaticAtom;
 class JSRope;
 class JSAtom;
 
 namespace js {
 
+class StaticStrings;
 class PropertyName;
 
 /* The buffer length required to contain any unsigned 32-bit integer. */
 static const size_t UINT32_CHAR_BUFFER_LENGTH = sizeof("4294967295") - 1;
 
 /* N.B. must correspond to boolean tagging behavior. */
 enum InternBehavior
 {
@@ -109,21 +109,16 @@ js_AtomizeString(JSContext *cx, JSString
  *  - To avoid allocating small char arrays, short strings can be stored inline
  *    in the string header (JSInlineString). To increase the max size of such
  *    inline strings, extra-large string headers can be used (JSShortString).
  *
  *  - To avoid comparing O(n) string equality comparison, strings can be
  *    canonicalized to "atoms" (JSAtom) such that there is a single atom with a
  *    given (length,chars).
  *
- *  - To avoid dynamic creation of common short strings (e.g., single-letter
- *    alphanumeric strings, numeric strings up to 999) headers and char arrays
- *    for such strings are allocated in static memory (JSStaticAtom) and used
- *    as atoms.
- *
  *  - To avoid copying all strings created through the JSAPI, an "external"
  *    string (JSExternalString) can be created whose chars are managed by the
  *    JSAPI client.
  *
  * Although all strings share the same basic memory layout, we can conceptually
  * arrange them into a hierarchy of operations/invariants and represent this
  * hierarchy in C++ with classes:
  *
@@ -145,22 +140,20 @@ js_AtomizeString(JSContext *cx, JSString
  *  | \  \
  *  |  \ JSExternalString       - / char array memory managed by embedding
  *  |   \
  *  |   JSInlineString          - / chars stored in header
  *  |     | \
  *  |     | JSShortString       - / header is fat
  *  |     |        |
  * JSAtom |        |            - / string equality === pointer equality
- *  | \   |        |
- *  | JSInlineAtom |            - / atomized JSInlineString
- *  |       \      |
- *  |       JSShortAtom         - / atomized JSShortString
- *  |
- * JSStaticAtom                 - / header and chars statically allocated
+ *    \   |        |
+ *    JSInlineAtom |            - / atomized JSInlineString
+ *          \      |
+ *          JSShortAtom         - / atomized JSShortString
  *
  * Classes marked with (abstract) above are not literally C++ Abstract Base
  * Classes (since there are no virtual functions, pure or not, in this
  * hierarchy), but have the same meaning: there are no strings with this type as
  * its most-derived type.
  *
  * Derived string types can be queried from ancestor types via isX() and
  * retrieved with asX() debug-only-checked casts.
@@ -253,19 +246,16 @@ class JSString : public js::gc::Cell
     static const size_t FLAT_MASK         = JS_BITMASK(2);
     static const size_t FLAT_FLAGS        = 0x0;
 
     static const size_t FIXED_FLAGS       = JS_BIT(2);
 
     static const size_t ATOM_MASK         = JS_BITMASK(3);
     static const size_t ATOM_FLAGS        = 0x0;
 
-    static const size_t STATIC_ATOM_MASK  = JS_BITMASK(4);
-    static const size_t STATIC_ATOM_FLAGS = 0x0;
-
     static const size_t EXTENSIBLE_FLAGS  = JS_BIT(2) | JS_BIT(3);
     static const size_t NON_STATIC_ATOM   = JS_BIT(3);
 
     size_t buildLengthAndFlags(size_t length, size_t flags) {
         return (length << LENGTH_SHIFT) | flags;
     }
 
     static void staticAsserts() {
@@ -395,21 +385,16 @@ class JSString : public js::gc::Cell
     }
 
     JS_ALWAYS_INLINE
     JSAtom &asAtom() const {
         JS_ASSERT(isAtom());
         return *(JSAtom *)this;
     }
 
-    JS_ALWAYS_INLINE
-    bool isStaticAtom() const {
-        return (d.lengthAndFlags & FLAGS_MASK) == STATIC_ATOM_FLAGS;
-    }
-
     /* Only called by the GC for strings with the FINALIZE_STRING kind. */
 
     inline void finalize(JSContext *cx);
 
     /* Gets the number of bytes that the chars take on the heap. */
 
     JS_FRIEND_API(size_t) charsHeapSize(JSUsableSizeFun usf);
 
@@ -650,89 +635,19 @@ class JSExternalString : public JSFixedS
     /* Only called by the GC for strings with the FINALIZE_EXTERNAL_STRING kind. */
 
     void finalize(JSContext *cx);
     void finalize();
 };
 
 JS_STATIC_ASSERT(sizeof(JSExternalString) == sizeof(JSString));
 
-#if !defined(__ia64__)
-/*
- * Don't use static strings on ia64 since the compiler may put the static
- * memory out of the acceptable 47-bit jsval pointer range.
- */
-# define JS_HAS_STATIC_STRINGS
-#endif
-
 class JSAtom : public JSFixedString
 {
   public:
-    /* Exposed only for jits. */
-
-#ifdef JS_HAS_STATIC_STRINGS
-    static const size_t UNIT_STATIC_LIMIT   = 256U;
-    static const size_t SMALL_CHAR_LIMIT    = 128U; /* Bigger chars cannot be in a length-2 string. */
-    static const size_t NUM_SMALL_CHARS     = 64U;
-    static const size_t INT_STATIC_LIMIT    = 256U;
-    static const size_t NUM_HUNDRED_STATICS = 156U;
-
-# ifdef __SUNPRO_CC
-#  pragma align 8 (__1cGJSAtomPunitStaticTable_, __1cGJSAtomSlength2StaticTable_, __1cGJSAtomShundredStaticTable_)
-# endif
-
-    static const JSString::Data unitStaticTable[];
-    static const JSString::Data length2StaticTable[];
-    static const JSString::Data hundredStaticTable[];
-    static const JSString::Data *const intStaticTable[];
-#endif
-
-  private:
-    /* Defined in jsgcinlines.h */
-    static inline bool isUnitString(const void *ptr);
-    static inline bool isLength2String(const void *ptr);
-    static inline bool isHundredString(const void *ptr);
-
-    typedef uint8 SmallChar;
-    static const SmallChar INVALID_SMALL_CHAR = -1;
-
-    static inline bool fitsInSmallChar(jschar c);
-
-    static const jschar fromSmallChar[];
-    static const SmallChar toSmallChar[];
-
-    static void staticAsserts() {
-        JS_STATIC_ASSERT(sizeof(JSString::Data) == sizeof(JSString));
-    }
-
-    static JSStaticAtom &length2Static(jschar c1, jschar c2);
-    static JSStaticAtom &length2Static(uint32 i);
-
-  public:
-    /*
-     * While this query can be used for any pointer to GC thing, given a
-     * JSString 'str', it is more efficient to use 'str->isStaticAtom()'.
-     */
-    static inline bool isStatic(const void *ptr);
-
-    static inline bool hasUintStatic(uint32 u);
-    static inline JSStaticAtom &uintStatic(uint32 u);
-
-    static inline bool hasIntStatic(int32 i);
-    static inline JSStaticAtom &intStatic(jsint i);
-
-    static inline bool hasUnitStatic(jschar c);
-    static JSStaticAtom &unitStatic(jschar c);
-
-    /* May not return atom, returns null on (reported) failure. */
-    static inline JSLinearString *getUnitStringForElement(JSContext *cx, JSString *str, size_t index);
-
-    /* Return null if no static atom exists for the given (chars, length). */
-    static inline JSStaticAtom *lookupStatic(const jschar *chars, size_t length);
-
     /* Returns the PropertyName for this.  isElement() must be false. */
     inline js::PropertyName *asPropertyName();
 
     inline void finalize(JSRuntime *rt);
 };
 
 JS_STATIC_ASSERT(sizeof(JSAtom) == sizeof(JSString));
 
@@ -751,22 +666,70 @@ class JSShortAtom : public JSShortString
     /*
      * JSShortAtom is not explicitly used and is only present for consistency.
      * See Atomize() for how JSShortStrings get morphed into JSShortAtoms.
      */
 };
 
 JS_STATIC_ASSERT(sizeof(JSShortAtom) == sizeof(JSShortString));
 
-class JSStaticAtom : public JSAtom
-{};
+namespace js {
+
+class StaticStrings
+{
+  private:
+    bool initialized;
+
+    /* Bigger chars cannot be in a length-2 string. */
+    static const size_t SMALL_CHAR_LIMIT    = 128U;
+    static const size_t NUM_SMALL_CHARS     = 64U;
+
+    static const size_t INT_STATIC_LIMIT    = 256U;
+
+    JSAtom *length2StaticTable[NUM_SMALL_CHARS * NUM_SMALL_CHARS];
+    JSAtom *intStaticTable[INT_STATIC_LIMIT];
+
+  public:
+    /* We keep these public for the methodjit. */
+    static const size_t UNIT_STATIC_LIMIT   = 256U;
+    JSAtom *unitStaticTable[UNIT_STATIC_LIMIT];
+
+    StaticStrings() : initialized(false) {}
+
+    bool init(JSContext *cx);
+    void trace(JSTracer *trc);
 
-JS_STATIC_ASSERT(sizeof(JSStaticAtom) == sizeof(JSString));
+    static inline bool hasUint(uint32 u);
+    inline JSAtom *getUint(uint32 u);
+
+    static inline bool hasInt(int32 i);
+    inline JSAtom *getInt(jsint i);
+
+    static inline bool hasUnit(jschar c);
+    JSAtom *getUnit(jschar c);
+
+    /* May not return atom, returns null on (reported) failure. */
+    inline JSLinearString *getUnitStringForElement(JSContext *cx, JSString *str, size_t index);
+
+    static bool isStatic(JSAtom *atom);
 
-namespace js {
+    /* Return null if no static atom exists for the given (chars, length). */
+    inline JSAtom *lookup(const jschar *chars, size_t length);
+
+  private:
+    typedef uint8 SmallChar;
+    static const SmallChar INVALID_SMALL_CHAR = -1;
+
+    static inline bool fitsInSmallChar(jschar c);
+
+    static const SmallChar toSmallChar[];
+
+    JSAtom *getLength2(jschar c1, jschar c2);
+    JSAtom *getLength2(uint32 i);
+};
 
 /*
  * Represents an atomized string which does not contain an unsigned 32-bit
  * value.  That is, it is never the case that for a PropertyName propname,
  * ToString(ToUint32(propname)) is equal to propname.
  */
 class PropertyName : public JSAtom
 {};