Bug 665189 - hoist JSString and family into vm/String{.h, -inl.h, .cpp} (r=njn)
authorLuke Wagner <luke@mozilla.com>
Mon, 20 Jun 2011 11:44:20 -0700
changeset 71397 aa383564c5b6c97f3205d4d279263794e467da88
parent 71396 6408e502189106a1875a78fd7e677b27b30b11a3
child 71398 1f11cd362858310ea1fe1635f042744406480886
push idunknown
push userunknown
push dateunknown
reviewersnjn
bugs665189
milestone7.0a1
Bug 665189 - hoist JSString and family into vm/String{.h, -inl.h, .cpp} (r=njn)
js/src/Makefile.in
js/src/jsapi-tests/testConservativeGC.cpp
js/src/jsapi-tests/testIntString.cpp
js/src/jsapi-tests/testParseJSON.cpp
js/src/jsapi.cpp
js/src/jsarray.h
js/src/jsatom.cpp
js/src/jsatom.h
js/src/jsbool.h
js/src/jscell.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jsgc.cpp
js/src/jsgcmark.cpp
js/src/jsinterp.cpp
js/src/jsiter.cpp
js/src/jsnum.cpp
js/src/jsstr.cpp
js/src/jsstr.h
js/src/jsstrinlines.h
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/StubCalls.cpp
js/src/vm/String-inl.h
js/src/vm/String.cpp
js/src/vm/String.h
js/src/yarr/wtfbridge.h
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -180,16 +180,17 @@ CPPSRCS		= \
 		jsweakmap.cpp \
 		jswrapper.cpp \
 		jsxdrapi.cpp \
 		jsxml.cpp \
 		prmjtime.cpp \
 		sharkctl.cpp \
 		GlobalObject.cpp \
 		Stack.cpp \
+		String.cpp \
 		$(NULL)
 
 # Changes to internal header files, used externally, massively slow down
 # browser builds.  Don't add new files here unless you know what you're
 # doing!
 INSTALLED_HEADERS = \
 		js-config.h \
 		jsautocfg.h \
@@ -262,18 +263,19 @@ INSTALLED_HEADERS = \
 VPATH		+= \
 		$(srcdir)/vm 	\
 		$(NULL)
 
 EXPORTS_NAMESPACES = vm
 
 EXPORTS_vm = \
 		ArgumentsObject.h \
-		GlobalObject.h			\
-		Stack.h			\
+		GlobalObject.h	 \
+		Stack.h \
+		String.h \
 		StringObject.h \
 		$(NULL)
 
 ###############################################
 # BEGIN include sources for low-level code shared with Gecko
 #
 VPATH		+= \
 		$(srcdir)/../../mfbt \
--- a/js/src/jsapi-tests/testConservativeGC.cpp
+++ b/js/src/jsapi-tests/testConservativeGC.cpp
@@ -1,11 +1,11 @@
 #include "tests.h"
 #include "jsobj.h"
-#include "jsstr.h"
+#include "vm/String.h"
 
 BEGIN_TEST(testConservativeGC)
 {
     jsval v2;
     EVAL("({foo: 'bar'});", &v2);
     CHECK(JSVAL_IS_OBJECT(v2));
     JSObject objCopy = *JSVAL_TO_OBJECT(v2);
 
--- a/js/src/jsapi-tests/testIntString.cpp
+++ b/js/src/jsapi-tests/testIntString.cpp
@@ -1,14 +1,14 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sw=4 et tw=99:
  */
 
 #include "tests.h"
-#include "jsstr.h"
+#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());
--- a/js/src/jsapi-tests/testParseJSON.cpp
+++ b/js/src/jsapi-tests/testParseJSON.cpp
@@ -2,16 +2,17 @@
  * vim: set ts=8 sw=4 et tw=99:
  */
 
 #include <limits>
 #include <math.h>
 
 #include "tests.h"
 #include "jsstr.h"
+#include "vm/String.h"
 
 using namespace js;
 
 class AutoInflatedString {
     JSContext * const cx;
     jschar *chars_;
     size_t length_;
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -89,20 +89,20 @@
 #include "jswrapper.h"
 #include "jstypedarray.h"
 
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jsregexpinlines.h"
 #include "jsscriptinlines.h"
-#include "jsstrinlines.h"
 #include "assembler/wtf/Platform.h"
 
 #include "vm/Stack-inl.h"
+#include "vm/String-inl.h"
 
 #if ENABLE_YARR_JIT
 #include "assembler/jit/ExecutableAllocator.h"
 #include "methodjit/Logging.h"
 #endif
 
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -41,17 +41,16 @@
 #define jsarray_h___
 /*
  * JS Array interface.
  */
 #include "jsprvtd.h"
 #include "jspubtd.h"
 #include "jsatom.h"
 #include "jsobj.h"
-#include "jsstr.h"
 
 /* Small arrays are dense, no matter what. */
 const uintN MIN_SPARSE_INDEX = 256;
 
 inline JSObject::EnsureDenseResult
 JSObject::ensureDenseArrayElements(JSContext *cx, uintN index, uintN extra)
 {
     JS_ASSERT(isDenseArray());
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -59,16 +59,18 @@
 #include "jsstr.h"
 #include "jsversion.h"
 #include "jsxml.h"
 
 #include "jsstrinlines.h"
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 
+#include "vm/String-inl.h"
+
 using namespace js;
 using namespace js::gc;
 
 const size_t JSAtomState::commonAtomsOffset = offsetof(JSAtomState, emptyAtom);
 const size_t JSAtomState::lazyAtomsOffset = offsetof(JSAtomState, lazy);
 
 /*
  * ATOM_HASH assumes that JSHashNumber is 32-bit even on 64-bit systems.
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -48,16 +48,18 @@
 #include "jsprvtd.h"
 #include "jshash.h"
 #include "jshashtable.h"
 #include "jspubtd.h"
 #include "jsstr.h"
 #include "jslock.h"
 #include "jsvalue.h"
 
+#include "vm/String.h"
+
 /* Engine-internal extensions of jsid */
 
 static JS_ALWAYS_INLINE jsid
 JSID_FROM_BITS(size_t bits)
 {
     jsid id;
     JSID_BITS(id) = bits;
     return id;
--- a/js/src/jsbool.h
+++ b/js/src/jsbool.h
@@ -40,17 +40,16 @@
 #ifndef jsbool_h___
 #define jsbool_h___
 /*
  * JS boolean interface.
  */
 
 #include "jsapi.h"
 #include "jsobj.h"
-#include "jsstr.h"
 
 extern js::Class js_BooleanClass;
 
 inline bool
 JSObject::isBoolean() const
 {
     return getClass() == &js_BooleanClass;
 }
--- a/js/src/jscell.h
+++ b/js/src/jscell.h
@@ -35,16 +35,18 @@
  * 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 ***** */
 
 #ifndef jscell_h___
 #define jscell_h___
 
+#include "jspubtd.h"
+
 struct JSCompartment;
 
 namespace js {
 namespace gc {
 
 struct ArenaHeader;
 struct Chunk;
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1708,9 +1708,20 @@ JS_FORCES_STACK JS_FRIEND_API(void)
 LeaveTrace(JSContext *cx)
 {
 #ifdef JS_TRACER
     if (JS_ON_TRACE(cx))
         DeepBail(cx);
 #endif
 }
 
+bool
+CanLeaveTrace(JSContext *cx)
+{
+    JS_ASSERT(JS_ON_TRACE(cx));
+#ifdef JS_TRACER
+    return JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit != NULL;
+#else
+    return false;
+#endif
+}
+
 } /* namespace js */
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -2449,16 +2449,19 @@ js_CurrentPCIsInImacro(JSContext *cx);
 
 namespace js {
 
 class RegExpStatics;
 
 extern JS_FORCES_STACK JS_FRIEND_API(void)
 LeaveTrace(JSContext *cx);
 
+extern bool
+CanLeaveTrace(JSContext *cx);
+
 } /* namespace js */
 
 /*
  * Get the current frame, first lazily instantiating stack frames if needed.
  * (Do not access cx->fp() directly except in JS_REQUIRES_STACK code.)
  *
  * Defined in jstracer.cpp if JS_TRACER is defined.
  */
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -381,27 +381,16 @@ LeaveTraceIfGlobalObject(JSContext *cx, 
 
 static JS_INLINE void
 LeaveTraceIfArgumentsObject(JSContext *cx, JSObject *obj)
 {
     if (obj->isArguments())
         LeaveTrace(cx);
 }
 
-static JS_INLINE JSBool
-CanLeaveTrace(JSContext *cx)
-{
-    JS_ASSERT(JS_ON_TRACE(cx));
-#ifdef JS_TRACER
-    return JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit != NULL;
-#else
-    return JS_FALSE;
-#endif
-}
-
 }  /* namespace js */
 
 #ifdef JS_METHODJIT
 inline js::mjit::JaegerCompartment *JSContext::jaegerCompartment()
 {
     return compartment->jaegerCompartment;
 }
 #endif
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -54,48 +54,48 @@
 #include "jsstdint.h"
 #include "jsutil.h"
 #include "jshash.h"
 #include "jsbit.h"
 #include "jsclist.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsatom.h"
+#include "jscompartment.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsdbgapi.h"
 #include "jsexn.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsgcchunk.h"
 #include "jsgcmark.h"
+#include "jshashtable.h"
 #include "jsinterp.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsparse.h"
+#include "jsprobes.h"
 #include "jsproxy.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstaticcheck.h"
-#include "jsstr.h"
-#include "methodjit/MethodJIT.h"
-
+#include "jsweakmap.h"
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
-#include "jsprobes.h"
+#include "methodjit/MethodJIT.h"
+#include "vm/String.h"
+
 #include "jsobjinlines.h"
-#include "jshashtable.h"
-#include "jsweakmap.h"
-
-#include "jsstrinlines.h"
-#include "jscompartment.h"
+
+#include "vm/String-inl.h"
 
 #ifdef MOZ_VALGRIND
 # define JS_VALGRIND
 #endif
 #ifdef JS_VALGRIND
 # include <valgrind/memcheck.h>
 #endif
 
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -40,16 +40,18 @@
 #include "jsgcmark.h"
 #include "jsprf.h"
 #include "jsscope.h"
 #include "jsstr.h"
 
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 
+#include "vm/String-inl.h"
+
 /*
  * There are two mostly separate mark paths. The first is a fast path used
  * internally in the GC. The second is a slow path used for root marking and
  * for API consumers like the cycle collector.
  *
  * The fast path uses explicit stacks. The basic marking process during a GC is
  * that all roots are pushed on to a mark stack, and then each item on the
  * stack is scanned (possibly pushing more stuff) until the stack is empty.
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -82,20 +82,20 @@
 #endif
 #include "jsatominlines.h"
 #include "jsinterpinlines.h"
 #include "jsobjinlines.h"
 #include "jsprobes.h"
 #include "jspropertycacheinlines.h"
 #include "jsscopeinlines.h"
 #include "jsscriptinlines.h"
-#include "jsstrinlines.h"
 #include "jsopcodeinlines.h"
 
 #include "vm/Stack-inl.h"
+#include "vm/String-inl.h"
 
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
 #include "jsautooplen.h"
 
 #if defined(JS_METHODJIT) && defined(JS_MONOIC)
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -71,19 +71,19 @@
 #include "jsstaticcheck.h"
 #include "jsvector.h"
 
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
 #include "jsobjinlines.h"
-#include "jsstrinlines.h"
 
 #include "vm/Stack-inl.h"
+#include "vm/String-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 static void iterator_finalize(JSContext *cx, JSObject *obj);
 static void iterator_trace(JSTracer *trc, JSObject *obj);
 static JSObject *iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly);
 
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -71,16 +71,18 @@
 #include "jstracer.h"
 #include "jsvector.h"
 
 #include "jsinterpinlines.h"
 #include "jsnuminlines.h"
 #include "jsobjinlines.h"
 #include "jsstrinlines.h"
 
+#include "vm/String-inl.h"
+
 using namespace js;
 
 #ifndef JS_HAVE_STDINT_H /* Native support is innocent until proven guilty. */
 
 JS_STATIC_ASSERT(uint8_t(-1) == UINT8_MAX);
 JS_STATIC_ASSERT(uint16_t(-1) == UINT16_MAX);
 JS_STATIC_ASSERT(uint32_t(-1) == UINT32_MAX);
 JS_STATIC_ASSERT(uint64_t(-1) == UINT64_MAX);
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -74,309 +74,24 @@
 #include "jsstr.h"
 #include "jsbit.h"
 #include "jsvector.h"
 #include "jsversion.h"
 
 #include "jsinterpinlines.h"
 #include "jsobjinlines.h"
 #include "jsregexpinlines.h"
-#include "jsstrinlines.h"
 #include "jsautooplen.h"        // generated headers last
 
 #include "vm/StringObject-inl.h"
+#include "vm/String-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
-bool
-JSString::isShort() const
-{
-    bool is_short = arenaHeader()->getThingKind() == FINALIZE_SHORT_STRING;
-    JS_ASSERT_IF(is_short, isFlat());
-    return is_short;
-}
-
-bool
-JSString::isFixed() const
-{
-    return isFlat() && !isExtensible();
-}
-
-bool
-JSString::isInline() const
-{
-    return isFixed() && (d.u1.chars == d.inlineStorage || isShort());
-}
-
-bool
-JSString::isExternal() const
-{
-    bool is_external = arenaHeader()->getThingKind() == FINALIZE_EXTERNAL_STRING;
-    JS_ASSERT_IF(is_external, isFixed());
-    return is_external;
-}
-
-void
-JSLinearString::mark(JSTracer *)
-{
-    JSLinearString *str = this;
-    while (!str->isStaticAtom() && str->markIfUnmarked() && str->isDependent())
-        str = str->asDependent().base();
-}
-
-size_t
-JSString::charsHeapSize()
-{
-    /* JSRope: do nothing, we'll count all children chars when we hit the leaf strings. */
-    if (isRope())
-        return 0;
-
-    JS_ASSERT(isLinear());
-
-    /* JSDependentString: do nothing, we'll count the chars when we hit the base string. */
-    if (isDependent())
-        return 0;
-
-    JS_ASSERT(isFlat());
-    
-    /* JSExtensibleString: count the full capacity, not just the used space. */
-    if (isExtensible())
-        return asExtensible().capacity() * sizeof(jschar);
-
-    JS_ASSERT(isFixed());
-
-    /* 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. */
-    return length() * sizeof(jschar);
-}
-
-static JS_ALWAYS_INLINE size_t
-RopeCapacityFor(size_t length)
-{
-    static const size_t ROPE_DOUBLING_MAX = 1024 * 1024;
-
-    /*
-     * Grow by 12.5% if the buffer is very large. Otherwise, round up to the
-     * next power of 2. This is similar to what we do with arrays; see
-     * JSObject::ensureDenseArrayElements.
-     */
-    if (length > ROPE_DOUBLING_MAX)
-        return length + (length / 8);
-    return RoundUpPow2(length);
-}
-
-static JS_ALWAYS_INLINE jschar *
-AllocChars(JSContext *maybecx, size_t wholeCapacity)
-{
-    /* +1 for the null char at the end. */
-    JS_STATIC_ASSERT(JSString::MAX_LENGTH * sizeof(jschar) < UINT32_MAX);
-    size_t bytes = (wholeCapacity + 1) * sizeof(jschar);
-    if (maybecx)
-        return (jschar *)maybecx->malloc_(bytes);
-    return (jschar *)OffTheBooks::malloc_(bytes);
-}
-
-JSFlatString *
-JSRope::flatten(JSContext *maybecx)
-{
-    /*
-     * Perform a depth-first dag traversal, splatting each node's characters
-     * into a contiguous buffer. Visit each rope node three times:
-     *   1. record position in the buffer and recurse into left child;
-     *   2. recurse into the right child;
-     *   3. transform the node into a dependent string.
-     * To avoid maintaining a stack, tree nodes are mutated to indicate how many
-     * times they have been visited. Since ropes can be dags, a node may be
-     * encountered multiple times during traversal. However, step 3 above leaves
-     * a valid dependent string, so everything works out. This algorithm is
-     * homomorphic to marking code.
-     *
-     * While ropes avoid all sorts of quadratic cases with string
-     * concatenation, they can't help when ropes are immediately flattened.
-     * One idiomatic case that we'd like to keep linear (and has traditionally
-     * been linear in SM and other JS engines) is:
-     *
-     *   while (...) {
-     *     s += ...
-     *     s.flatten
-     *   }
-     *
-     * To do this, when the buffer for a to-be-flattened rope is allocated, the
-     * allocation size is rounded up. Then, if the resulting flat string is the
-     * left-hand side of a new rope that gets flattened and there is enough
-     * capacity, the rope is flattened into the same buffer, thereby avoiding
-     * copying the left-hand side. Clearing the 'extensible' bit turns off this
-     * optimization. This is necessary, e.g., when the JSAPI hands out the raw
-     * null-terminated char array of a flat string.
-     *
-     * N.B. This optimization can create chains of dependent strings.
-     */
-    const size_t wholeLength = length();
-    size_t wholeCapacity;
-    jschar *wholeChars;
-    JSString *str = this;
-    jschar *pos;
-
-    if (this->leftChild()->isExtensible()) {
-        JSExtensibleString &left = this->leftChild()->asExtensible();
-        size_t capacity = left.capacity();
-        if (capacity >= wholeLength) {
-            wholeCapacity = capacity;
-            wholeChars = const_cast<jschar *>(left.chars());
-            size_t bits = left.d.lengthAndFlags;
-            pos = wholeChars + (bits >> LENGTH_SHIFT);
-            left.d.lengthAndFlags = bits ^ (EXTENSIBLE_FLAGS | DEPENDENT_BIT);
-            left.d.s.u2.base = (JSLinearString *)this;  /* will be true on exit */
-            goto visit_right_child;
-        }
-    }
-
-    wholeCapacity = RopeCapacityFor(wholeLength);
-    wholeChars = AllocChars(maybecx, wholeCapacity);
-    if (!wholeChars)
-        return NULL;
-
-    pos = wholeChars;
-    first_visit_node: {
-        JSString &left = *str->d.u1.left;
-        str->d.u1.chars = pos;
-        if (left.isRope()) {
-            left.d.s.u3.parent = str;          /* Return to this when 'left' done, */
-            left.d.lengthAndFlags = 0x200;     /* but goto visit_right_child. */
-            str = &left;
-            goto first_visit_node;
-        }
-        size_t len = left.length();
-        PodCopy(pos, left.d.u1.chars, len);
-        pos += len;
-    }
-    visit_right_child: {
-        JSString &right = *str->d.s.u2.right;
-        if (right.isRope()) {
-            right.d.s.u3.parent = str;         /* Return to this node when 'right' done, */
-            right.d.lengthAndFlags = 0x300;    /* but goto finish_node. */
-            str = &right;
-            goto first_visit_node;
-        }
-        size_t len = right.length();
-        PodCopy(pos, right.d.u1.chars, len);
-        pos += len;
-    }
-    finish_node: {
-        if (str == this) {
-            JS_ASSERT(pos == wholeChars + wholeLength);
-            *pos = '\0';
-            str->d.lengthAndFlags = buildLengthAndFlags(wholeLength, EXTENSIBLE_FLAGS);
-            str->d.u1.chars = wholeChars;
-            str->d.s.u2.capacity = wholeCapacity;
-            return &this->asFlat();
-        }
-        size_t progress = str->d.lengthAndFlags;
-        str->d.lengthAndFlags = buildLengthAndFlags(pos - str->d.u1.chars, DEPENDENT_BIT);
-        str->d.s.u2.base = (JSLinearString *)this;       /* will be true on exit */
-        str = str->d.s.u3.parent;
-        if (progress == 0x200)
-            goto visit_right_child;
-        JS_ASSERT(progress == 0x300);
-        goto finish_node;
-    }
-}
-
-JSString * JS_FASTCALL
-js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
-{
-    JS_ASSERT_IF(!left->isAtom(), left->compartment() == cx->compartment);
-    JS_ASSERT_IF(!right->isAtom(), right->compartment() == cx->compartment);
-
-    size_t leftLen = left->length();
-    if (leftLen == 0)
-        return right;
-
-    size_t rightLen = right->length();
-    if (rightLen == 0)
-        return left;
-
-    size_t wholeLength = leftLen + rightLen;
-
-    if (JSShortString::lengthFits(wholeLength)) {
-        JSShortString *str = js_NewGCShortString(cx);
-        if (!str)
-            return NULL;
-        const jschar *leftChars = left->getChars(cx);
-        if (!leftChars)
-            return NULL;
-        const jschar *rightChars = right->getChars(cx);
-        if (!rightChars)
-            return NULL;
-
-        jschar *buf = str->init(wholeLength);
-        PodCopy(buf, leftChars, leftLen);
-        PodCopy(buf + leftLen, rightChars, rightLen);
-        buf[wholeLength] = 0;
-        return str;
-    }
-
-    if (wholeLength > JSString::MAX_LENGTH) {
-        if (JS_ON_TRACE(cx)) {
-            if (!CanLeaveTrace(cx))
-                return NULL;
-            LeaveTrace(cx);
-        }
-        js_ReportAllocationOverflow(cx);
-        return NULL;
-    }
-
-    return JSRope::new_(cx, left, right, wholeLength);
-}
-
-JSFixedString *
-JSDependentString::undepend(JSContext *cx)
-{
-    JS_ASSERT(isDependent());
-
-    size_t n = length();
-    size_t size = (n + 1) * sizeof(jschar);
-    jschar *s = (jschar *) cx->malloc_(size);
-    if (!s)
-        return NULL;
-
-    PodCopy(s, chars(), n);
-    s[n] = 0;
-
-    d.lengthAndFlags = buildLengthAndFlags(n, FIXED_FLAGS);
-    d.u1.chars = s;
-
-#ifdef DEBUG
-    JSRuntime *rt = cx->runtime;
-    JS_RUNTIME_UNMETER(rt, liveDependentStrings);
-    JS_RUNTIME_UNMETER(rt, totalDependentStrings);
-    JS_LOCK_RUNTIME_VOID(rt,
-        (rt->strdepLengthSum -= (double)n,
-         rt->strdepLengthSquaredSum -= (double)n * (double)n));
-#endif
-
-    return &this->asFixed();
-}
-
-JSStringFinalizeOp JSExternalString::str_finalizers[JSExternalString::TYPE_LIMIT] = {
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
-
 #ifdef JS_TRACER
 
 JSBool JS_FASTCALL
 js_FlattenOnTrace(JSContext *cx, JSString* str)
 {
     return !!str->ensureLinear(cx);
 }
 JS_DEFINE_CALLINFO_2(extern, BOOL, js_FlattenOnTrace, CONTEXT, STRING, 0, nanojit::ACCSET_STORE_ANY)
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -44,718 +44,16 @@
 #include "jsapi.h"
 #include "jsprvtd.h"
 #include "jshashtable.h"
 #include "jslock.h"
 #include "jsobj.h"
 #include "jsvalue.h"
 #include "jscell.h"
 
-/*
- * JS strings
- *
- * Conceptually, a JS string is just an array of chars and a length. To improve
- * performance of common string operations, the following optimizations are
- * made which affect the engine's representation of strings:
- *
- *  - The plain vanilla representation is a "flat" string which consists of a
- *    string header in the GC heap and a malloc'd null terminated char array.
- *
- *  - To avoid copying a substring of an existing "base" string , a "dependent"
- *    string (JSDependentString) can be created which points into the base
- *    string's char array.
- *
- *  - To avoid O(n^2) char buffer copying, a "rope" node (JSRope) can be created
- *    to represent a delayed string concatenation. Concatenation (called
- *    flattening) is performed if and when a linear char array is requested. In
- *    general, ropes form a binary dag whose internal nodes are JSRope string
- *    headers with no associated char array and whose leaf nodes are either flat
- *    or dependent strings.
- *
- *  - To avoid copying the left-hand side when flattening, the left-hand side's
- *    buffer may be grown to make space for a copy of the right-hand side (see
- *    comment in JSString::flatten). This optimization requires that there are
- *    no external pointers into the char array. We conservatively maintain this
- *    property via a flat string's "extensible" property.
- *
- *  - 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:
- *
- * C++ type                     operations+fields / invariants+properties
- *
- * JSString (abstract)          getCharsZ, getChars, length / -
- *  | \
- *  | JSRope                    leftChild, rightChild / -
- *  |
- * JSLinearString (abstract)    chars / not null-terminated
- *  | \
- *  | JSDependentString         base / -
- *  |
- * JSFlatString (abstract)      chars / not null-terminated
- *  | \
- *  | JSExtensibleString        capacity / no external pointers into char array
- *  |
- * JSFixedString                - / may have external pointers into char array
- *  | \  \
- *  |  \ 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
- *
- * 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.
- *
- * The ensureX() operations mutate 'this' in place to effectively the type to be
- * at least X (e.g., ensureLinear will change a JSRope to be a JSFlatString).
- */
-
-class JSString : public js::gc::Cell
-{
-  protected:
-    static const size_t NUM_INLINE_CHARS = 2 * sizeof(void *) / sizeof(jschar);
-
-    /* Fields only apply to string types commented on the right. */
-    struct Data
-    {
-        size_t                     lengthAndFlags;      /* JSString */
-        union {
-            const jschar           *chars;              /* JSLinearString */
-            JSString               *left;               /* JSRope */
-        } u1;
-        union {
-            jschar                 inlineStorage[NUM_INLINE_CHARS]; /* JS(Inline|Short)String */
-            struct {
-                union {
-                    JSLinearString *base;               /* JSDependentString */
-                    JSString       *right;              /* JSRope */
-                    size_t         capacity;            /* JSFlatString (extensible) */
-                    size_t         externalType;        /* JSExternalString */
-                } u2;
-                union {
-                    JSString       *parent;             /* JSRope (temporary) */
-                    void           *externalClosure;    /* JSExternalString */
-                    size_t         reserved;            /* may use for bug 615290 */
-                } u3;
-            } s;
-        };
-    } d;
-
-  public:
-    /* Flags exposed only for jits */
-
-    static const size_t LENGTH_SHIFT      = 4;
-    static const size_t FLAGS_MASK        = JS_BITMASK(LENGTH_SHIFT);
-    static const size_t MAX_LENGTH        = JS_BIT(32 - LENGTH_SHIFT) - 1;
-
-    /*
-     * The low LENGTH_SHIFT bits of lengthAndFlags are used to encode the type
-     * of the string. The remaining bits store the string length (which must be
-     * less or equal than MAX_LENGTH).
-     * 
-     * Instead of using a dense index to represent the most-derived type, string
-     * types are encoded to allow single-op tests for hot queries (isRope,
-     * isDependent, isFlat, isAtom, isStaticAtom) which, in view of subtyping,
-     * would require slower (isX() || isY() || isZ()).
-     *
-     * The string type encoding can be summarized as follows. The "instance
-     * encoding" entry for a type specifies the flag bits used to create a
-     * string instance of that type. Abstract types have no instances and thus
-     * have no such entry. The "subtype predicate" entry for a type specifies
-     * the predicate used to query whether a JSString instance is subtype
-     * (reflexively) of that type.
-     *
-     *   string       instance   subtype
-     *   type         encoding   predicate
-     *
-     *   String       -          true
-     *   Rope         0001       xxx1
-     *   Linear       -          xxx0
-     *   Dependent    0010       xx1x
-     *   Flat         -          xx00
-     *   Extensible   1100       1100
-     *   Fixed        0100       isFlat && !isExtensible
-     *   Inline       0100       isFixed && (u1.chars == inlineStorage || isShort)
-     *   Short        0100       xxxx && header in FINALIZE_SHORT_STRING arena
-     *   External     0100       xxxx && header in FINALIZE_EXTERNAL_STRING arena
-     *   Atom         1000       x000
-     *   InlineAtom   1000       1000 && is Inline
-     *   ShortAtom    1000       1000 && is Short
-     *   StaticAtom   0000       0000
-     */
-
-    static const size_t ROPE_BIT          = JS_BIT(0);
-
-    static const size_t LINEAR_MASK       = JS_BITMASK(1);
-    static const size_t LINEAR_FLAGS      = 0x0;
-
-    static const size_t DEPENDENT_BIT     = JS_BIT(1);
-
-    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() {
-        JS_STATIC_ASSERT(size_t(JSString::MAX_LENGTH) <= size_t(JSVAL_INT_MAX));
-        JS_STATIC_ASSERT(JSString::MAX_LENGTH <= JSVAL_INT_MAX);
-        JS_STATIC_ASSERT(JS_BITS_PER_WORD >= 32);
-        JS_STATIC_ASSERT(((JSString::MAX_LENGTH << JSString::LENGTH_SHIFT) >>
-                           JSString::LENGTH_SHIFT) == JSString::MAX_LENGTH);
-        JS_STATIC_ASSERT(sizeof(JSString) ==
-                         offsetof(JSString, d.inlineStorage) +
-                         NUM_INLINE_CHARS * sizeof(jschar));
-    }
-
-    /* Avoid lame compile errors in JSRope::flatten */
-    friend class JSRope;
-
-  public:
-    /* All strings have length. */
-
-    JS_ALWAYS_INLINE
-    size_t length() const {
-        return d.lengthAndFlags >> LENGTH_SHIFT;
-    }
-
-    JS_ALWAYS_INLINE
-    bool empty() const {
-        return d.lengthAndFlags <= FLAGS_MASK;
-    }
-
-    /*
-     * All strings have a fallible operation to get an array of chars.
-     * getCharsZ additionally ensures the array is null terminated.
-     */
-
-    inline const jschar *getChars(JSContext *cx);
-    inline const jschar *getCharsZ(JSContext *cx);
-
-    /* Fallible conversions to more-derived string types. */
-
-    inline JSLinearString *ensureLinear(JSContext *cx);
-    inline JSFlatString *ensureFlat(JSContext *cx);
-    inline JSFixedString *ensureFixed(JSContext *cx);
-
-    /* Type query and debug-checked casts */
-
-    JS_ALWAYS_INLINE
-    bool isRope() const {
-        bool rope = d.lengthAndFlags & ROPE_BIT;
-        JS_ASSERT_IF(rope, (d.lengthAndFlags & FLAGS_MASK) == ROPE_BIT);
-        return rope;
-    }
-
-    JS_ALWAYS_INLINE
-    JSRope &asRope() {
-        JS_ASSERT(isRope());
-        return *(JSRope *)this;
-    }
-
-    JS_ALWAYS_INLINE
-    bool isLinear() const {
-        return (d.lengthAndFlags & LINEAR_MASK) == LINEAR_FLAGS;
-    }
-
-    JS_ALWAYS_INLINE
-    JSLinearString &asLinear() {
-        JS_ASSERT(isLinear());
-        return *(JSLinearString *)this;
-    }
-
-    JS_ALWAYS_INLINE
-    bool isDependent() const {
-        bool dependent = d.lengthAndFlags & DEPENDENT_BIT;
-        JS_ASSERT_IF(dependent, (d.lengthAndFlags & FLAGS_MASK) == DEPENDENT_BIT);
-        return dependent;
-    }
-
-    JS_ALWAYS_INLINE
-    JSDependentString &asDependent() {
-        JS_ASSERT(isDependent());
-        return *(JSDependentString *)this;
-    }
-
-    JS_ALWAYS_INLINE
-    bool isFlat() const {
-        return (d.lengthAndFlags & FLAT_MASK) == FLAT_FLAGS;
-    }
-
-    JS_ALWAYS_INLINE
-    JSFlatString &asFlat() {
-        JS_ASSERT(isFlat());
-        return *(JSFlatString *)this;
-    }
-
-    JS_ALWAYS_INLINE
-    bool isExtensible() const {
-        return (d.lengthAndFlags & FLAGS_MASK) == EXTENSIBLE_FLAGS;
-    }
-
-    JS_ALWAYS_INLINE
-    JSExtensibleString &asExtensible() const {
-        JS_ASSERT(isExtensible());
-        return *(JSExtensibleString *)this;
-    }
-
-    /* For hot code, prefer other type queries. */
-    bool isShort() const;
-    bool isFixed() const;
-    bool isInline() const;
-
-    JS_ALWAYS_INLINE
-    JSFixedString &asFixed() {
-        JS_ASSERT(isFixed());
-        return *(JSFixedString *)this;
-    }
-
-    bool isExternal() const;
-
-    JS_ALWAYS_INLINE
-    JSExternalString &asExternal() {
-        JS_ASSERT(isExternal());
-        return *(JSExternalString *)this;
-    }
-
-    JS_ALWAYS_INLINE
-    bool isAtom() const {
-        bool atomized = (d.lengthAndFlags & ATOM_MASK) == ATOM_FLAGS;
-        JS_ASSERT_IF(atomized, isFlat());
-        return atomized;
-    }
-
-    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();
-
-    /* Offsets for direct field from jit code. */
-
-    static size_t offsetOfLengthAndFlags() {
-        return offsetof(JSString, d.lengthAndFlags);
-    }
-
-    static size_t offsetOfChars() {
-        return offsetof(JSString, d.u1.chars);
-    }
-};
-
-class JSRope : public JSString
-{
-    friend class JSString;
-    JSFlatString *flatten(JSContext *cx);
-
-    void init(JSString *left, JSString *right, size_t length);
-
-  public:
-    static inline JSRope *new_(JSContext *cx, JSString *left,
-                               JSString *right, size_t length);
-
-    inline JSString *leftChild() const {
-        JS_ASSERT(isRope());
-        return d.u1.left;
-    }
-
-    inline JSString *rightChild() const {
-        JS_ASSERT(isRope());
-        return d.s.u2.right;
-    }
-};
-
-JS_STATIC_ASSERT(sizeof(JSRope) == sizeof(JSString));
-
-class JSLinearString : public JSString
-{
-    friend class JSString;
-
-  public:
-    void mark(JSTracer *trc);
-
-    JS_ALWAYS_INLINE
-    const jschar *chars() const {
-        JS_ASSERT(isLinear());
-        return d.u1.chars;
-    }
-};
-
-JS_STATIC_ASSERT(sizeof(JSLinearString) == sizeof(JSString));
-
-class JSDependentString : public JSLinearString
-{
-    friend class JSString;
-    JSFixedString *undepend(JSContext *cx);
-
-    void init(JSLinearString *base, const jschar *chars, size_t length);
-
-  public:
-    static inline JSDependentString *new_(JSContext *cx, JSLinearString *base,
-                                          const jschar *chars, size_t length);
-
-    JSLinearString *base() const {
-        JS_ASSERT(isDependent());
-        return d.s.u2.base;
-    }
-};
-
-JS_STATIC_ASSERT(sizeof(JSDependentString) == sizeof(JSString));
-
-class JSFlatString : public JSLinearString
-{
-    friend class JSRope;
-    void morphExtensibleIntoDependent(JSLinearString *base) {
-        d.lengthAndFlags = buildLengthAndFlags(length(), DEPENDENT_BIT);
-        d.s.u2.base = base;
-    }
-
-  public:
-    JS_ALWAYS_INLINE
-    const jschar *charsZ() const {
-        JS_ASSERT(isFlat());
-        return chars();
-    }
-
-    /* Only called by the GC for strings with the FINALIZE_STRING kind. */
-
-    inline void finalize(JSRuntime *rt);
-};
-
-JS_STATIC_ASSERT(sizeof(JSFlatString) == sizeof(JSString));
-
-class JSExtensibleString : public JSFlatString
-{
-  public:
-    JS_ALWAYS_INLINE
-    size_t capacity() const {
-        JS_ASSERT(isExtensible());
-        return d.s.u2.capacity;
-    }
-};
-
-JS_STATIC_ASSERT(sizeof(JSExtensibleString) == sizeof(JSString));
-
-class JSFixedString : public JSFlatString
-{
-    void init(const jschar *chars, size_t length);
-
-  public:
-    static inline JSFixedString *new_(JSContext *cx, const jschar *chars, size_t length);
-
-    /*
-     * Once a JSFixedString has been added to the atom state, this operation
-     * changes the type (in place, as reflected by the flag bits) of the
-     * JSFixedString into a JSAtom.
-     */
-    inline JSAtom *morphAtomizedStringIntoAtom();
-};
-
-JS_STATIC_ASSERT(sizeof(JSFixedString) == sizeof(JSString));
-
-class JSInlineString : public JSFixedString
-{
-    static const size_t MAX_INLINE_LENGTH = NUM_INLINE_CHARS - 1;
-
-  public:
-    static inline JSInlineString *new_(JSContext *cx);
-
-    inline jschar *init(size_t length);
-
-    inline void resetLength(size_t length);
-
-    static bool lengthFits(size_t length) {
-        return length <= MAX_INLINE_LENGTH;
-    }
-
-};
-
-JS_STATIC_ASSERT(sizeof(JSInlineString) == sizeof(JSString));
-
-class JSShortString : public JSInlineString
-{
-    /* This can be any value that is a multiple of Cell::CellSize. */
-    static const size_t INLINE_EXTENSION_CHARS = sizeof(JSString::Data) / sizeof(jschar);
-
-    static void staticAsserts() {
-        JS_STATIC_ASSERT(INLINE_EXTENSION_CHARS % js::gc::Cell::CellSize == 0);
-        JS_STATIC_ASSERT(MAX_SHORT_LENGTH + 1 ==
-                         (sizeof(JSShortString) -
-                          offsetof(JSShortString, d.inlineStorage)) / sizeof(jschar));
-    }
-
-    jschar inlineStorageExtension[INLINE_EXTENSION_CHARS];
-
-  public:
-    static inline JSShortString *new_(JSContext *cx);
-
-    jschar *inlineStorageBeforeInit() {
-        return d.inlineStorage;
-    }
-
-    inline void initAtOffsetInBuffer(const jschar *chars, size_t length);
-
-    static const size_t MAX_SHORT_LENGTH = JSString::NUM_INLINE_CHARS +
-                                           INLINE_EXTENSION_CHARS
-                                           -1 /* null terminator */;
-
-    static bool lengthFits(size_t length) {
-        return length <= MAX_SHORT_LENGTH;
-    }
-
-    /* Only called by the GC for strings with the FINALIZE_EXTERNAL_STRING kind. */
-
-    JS_ALWAYS_INLINE void finalize(JSContext *cx);
-};
-
-JS_STATIC_ASSERT(sizeof(JSShortString) == 2 * sizeof(JSString));
-
-/*
- * The externalClosure stored in an external string is a black box to the JS
- * engine; see JS_NewExternalStringWithClosure.
- */
-class JSExternalString : public JSFixedString
-{
-    static void staticAsserts() {
-        JS_STATIC_ASSERT(TYPE_LIMIT == 8);
-    }
-
-    void init(const jschar *chars, size_t length, intN type, void *closure);
-
-  public:
-    static inline JSExternalString *new_(JSContext *cx, const jschar *chars,
-                                         size_t length, intN type, void *closure);
-
-    intN externalType() const {
-        JS_ASSERT(isExternal());
-        JS_ASSERT(d.s.u2.externalType < TYPE_LIMIT);
-        return d.s.u2.externalType;
-    }
-
-    void *externalClosure() const {
-        JS_ASSERT(isExternal());
-        return d.s.u3.externalClosure;
-    }
-
-    static const uintN TYPE_LIMIT = 8;
-    static JSStringFinalizeOp str_finalizers[TYPE_LIMIT];
-
-    static intN changeFinalizer(JSStringFinalizeOp oldop,
-                                JSStringFinalizeOp newop) {
-        for (uintN i = 0; i != JS_ARRAY_LENGTH(str_finalizers); i++) {
-            if (str_finalizers[i] == oldop) {
-                str_finalizers[i] = newop;
-                return intN(i);
-            }
-        }
-        return -1;
-    }
-
-    /* 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));
-
-class JSAtom : public JSFixedString
-{
-  public:
-    /* Exposed only for jits. */
-
-    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[];
-
-  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 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);
-
-    inline void finalize(JSRuntime *rt);
-};
-
-JS_STATIC_ASSERT(sizeof(JSAtom) == sizeof(JSString));
-
-class JSInlineAtom : public JSInlineString /*, JSAtom */
-{
-    /*
-     * JSInlineAtom is not explicitly used and is only present for consistency.
-     * See Atomize() for how JSInlineStrings get morphed into JSInlineAtoms.
-     */
-};
-
-JS_STATIC_ASSERT(sizeof(JSInlineAtom) == sizeof(JSInlineString));
-
-class JSShortAtom : public JSShortString /*, JSInlineAtom */
-{
-    /*
-     * 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
-{};
-
-JS_STATIC_ASSERT(sizeof(JSStaticAtom) == sizeof(JSString));
-
-/* Avoid requring jsstrinlines.h just to call getChars. */
-
-JS_ALWAYS_INLINE const jschar *
-JSString::getChars(JSContext *cx)
-{
-    if (JSLinearString *str = ensureLinear(cx))
-        return str->chars();
-    return NULL;
-}
-
-JS_ALWAYS_INLINE const jschar *
-JSString::getCharsZ(JSContext *cx)
-{
-    if (JSFlatString *str = ensureFlat(cx))
-        return str->chars();
-    return NULL;
-}
-
-JS_ALWAYS_INLINE JSLinearString *
-JSString::ensureLinear(JSContext *cx)
-{
-    return isLinear()
-           ? &asLinear()
-           : asRope().flatten(cx);
-}
-
-JS_ALWAYS_INLINE JSFlatString *
-JSString::ensureFlat(JSContext *cx)
-{
-    return isFlat()
-           ? &asFlat()
-           : isDependent()
-             ? asDependent().undepend(cx)
-             : asRope().flatten(cx);
-}
-
-JS_ALWAYS_INLINE JSFixedString *
-JSString::ensureFixed(JSContext *cx)
-{
-    if (!ensureFlat(cx))
-        return NULL;
-    if (isExtensible()) {
-        JS_ASSERT((d.lengthAndFlags & FLAT_MASK) == 0);
-        JS_STATIC_ASSERT(EXTENSIBLE_FLAGS == (JS_BIT(2) | JS_BIT(3)));
-        JS_STATIC_ASSERT(FIXED_FLAGS == JS_BIT(2));
-        d.lengthAndFlags ^= JS_BIT(3);
-    }
-    return &asFixed();
-}
-
 namespace js {
 
 /* Implemented in jsstrinlines.h */
 class StringBuffer;
 
 /*
  * When an algorithm does not need a string represented as a single linear
  * array of characters, this range utility may be used to traverse the string a
--- a/js/src/jsstrinlines.h
+++ b/js/src/jsstrinlines.h
@@ -252,339 +252,16 @@ inline bool
 ValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb)
 {
     if (v.isString())
         return sb.append(v.toString());
 
     return ValueToStringBufferSlow(cx, v, sb);
 }
 
-} /* namespace js */
-
-JS_ALWAYS_INLINE void
-JSRope::init(JSString *left, JSString *right, size_t length)
-{
-    d.lengthAndFlags = buildLengthAndFlags(length, ROPE_BIT);
-    d.u1.left = left;
-    d.s.u2.right = right;
-}
-
-JS_ALWAYS_INLINE JSRope *
-JSRope::new_(JSContext *cx, JSString *left, JSString *right, size_t length)
-{
-    JSRope *str = (JSRope *)js_NewGCString(cx);
-    if (!str)
-        return NULL;
-    str->init(left, right, length);
-    return str;
-}
-
-JS_ALWAYS_INLINE void
-JSDependentString::init(JSLinearString *base, const jschar *chars, size_t length)
-{
-    d.lengthAndFlags = buildLengthAndFlags(length, DEPENDENT_BIT);
-    d.u1.chars = chars;
-    d.s.u2.base = base;
-}
-
-JS_ALWAYS_INLINE JSDependentString *
-JSDependentString::new_(JSContext *cx, JSLinearString *base, const jschar *chars, size_t length)
-{
-    /* Try to avoid long chains of dependent strings. */
-    while (base->isDependent())
-        base = base->asDependent().base();
-
-    JS_ASSERT(base->isFlat());
-    JS_ASSERT(chars >= base->chars() && chars < base->chars() + base->length());
-    JS_ASSERT(length <= base->length() - (chars - base->chars()));
-
-    JSDependentString *str = (JSDependentString *)js_NewGCString(cx);
-    if (!str)
-        return NULL;
-    str->init(base, chars, length);
-#ifdef DEBUG
-    JSRuntime *rt = cx->runtime;
-    JS_RUNTIME_METER(rt, liveDependentStrings);
-    JS_RUNTIME_METER(rt, totalDependentStrings);
-    JS_RUNTIME_METER(rt, liveStrings);
-    JS_RUNTIME_METER(rt, totalStrings);
-    JS_LOCK_RUNTIME_VOID(rt,
-        (rt->strdepLengthSum += (double)length,
-         rt->strdepLengthSquaredSum += (double)length * (double)length));
-    JS_LOCK_RUNTIME_VOID(rt,
-        (rt->lengthSum += (double)length,
-         rt->lengthSquaredSum += (double)length * (double)length));
-#endif
-    return str;
-}
-
-JS_ALWAYS_INLINE void
-JSFixedString::init(const jschar *chars, size_t length)
-{
-    d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
-    d.u1.chars = chars;
-}
-
-JS_ALWAYS_INLINE JSFixedString *
-JSFixedString::new_(JSContext *cx, const jschar *chars, size_t length)
-{
-    JS_ASSERT(length <= MAX_LENGTH);
-    JS_ASSERT(chars[length] == jschar(0));
-
-    JSFixedString *str = (JSFixedString *)js_NewGCString(cx);
-    if (!str)
-        return NULL;
-    str->init(chars, length);
-
-#ifdef DEBUG
-    JSRuntime *rt = cx->runtime;
-    JS_RUNTIME_METER(rt, liveStrings);
-    JS_RUNTIME_METER(rt, totalStrings);
-    JS_LOCK_RUNTIME_VOID(rt,
-        (rt->lengthSum += (double)length,
-         rt->lengthSquaredSum += (double)length * (double)length));
-#endif
-    return str;
-}
-
-JS_ALWAYS_INLINE JSAtom *
-JSFixedString::morphAtomizedStringIntoAtom()
-{
-    JS_ASSERT((d.lengthAndFlags & FLAGS_MASK) == JS_BIT(2));
-    JS_STATIC_ASSERT(NON_STATIC_ATOM == JS_BIT(3));
-    d.lengthAndFlags ^= (JS_BIT(2) | JS_BIT(3));
-    return &asAtom();
-}
-
-JS_ALWAYS_INLINE JSInlineString *
-JSInlineString::new_(JSContext *cx)
-{
-    return (JSInlineString *)js_NewGCString(cx);
-}
-
-JS_ALWAYS_INLINE jschar *
-JSInlineString::init(size_t length)
-{
-    d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
-    d.u1.chars = d.inlineStorage;
-    JS_ASSERT(lengthFits(length) || (isShort() && JSShortString::lengthFits(length)));
-    return d.inlineStorage;
-}
-
-JS_ALWAYS_INLINE void
-JSInlineString::resetLength(size_t length)
-{
-    d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
-    JS_ASSERT(lengthFits(length) || (isShort() && JSShortString::lengthFits(length)));
-}
-
-JS_ALWAYS_INLINE JSShortString *
-JSShortString::new_(JSContext *cx)
-{
-    return js_NewGCShortString(cx);
-}
-
-JS_ALWAYS_INLINE void
-JSShortString::initAtOffsetInBuffer(const jschar *chars, size_t length)
-{
-    JS_ASSERT(lengthFits(length + (chars - d.inlineStorage)));
-    JS_ASSERT(chars >= d.inlineStorage && chars < d.inlineStorage + MAX_SHORT_LENGTH);
-    d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
-    d.u1.chars = chars;
-}
-
-JS_ALWAYS_INLINE void
-JSExternalString::init(const jschar *chars, size_t length, intN type, void *closure)
-{
-    d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
-    d.u1.chars = chars;
-    d.s.u2.externalType = type;
-    d.s.u3.externalClosure = closure;
-}
-
-JS_ALWAYS_INLINE JSExternalString *
-JSExternalString::new_(JSContext *cx, const jschar *chars, size_t length, intN type, void *closure)
-{
-    JS_ASSERT(uintN(type) < JSExternalString::TYPE_LIMIT);
-    JS_ASSERT(chars[length] == 0);
-
-    JSExternalString *str = js_NewGCExternalString(cx);
-    if (!str)
-        return NULL;
-    str->init(chars, length, type, closure);
-    cx->runtime->updateMallocCounter((length + 1) * sizeof(jschar));
-    return str;
-}
-
-inline bool
-JSAtom::fitsInSmallChar(jschar c)
-{
-    return c < SMALL_CHAR_LIMIT && toSmallChar[c] != INVALID_SMALL_CHAR;
-}
-
-inline bool
-JSAtom::hasUnitStatic(jschar c)
-{
-    return c < UNIT_STATIC_LIMIT;
-}
-
-inline JSStaticAtom &
-JSAtom::unitStatic(jschar c)
-{
-    JS_ASSERT(hasUnitStatic(c));
-    return (JSStaticAtom &)unitStaticTable[c];
-}
-
-inline bool
-JSAtom::hasIntStatic(int32 i)
-{
-    return uint32(i) < INT_STATIC_LIMIT;
-}
-
-inline JSStaticAtom &
-JSAtom::intStatic(jsint i)
-{
-    JS_ASSERT(hasIntStatic(i));
-    return (JSStaticAtom &)*intStaticTable[i];
-}
-
-inline JSLinearString *
-JSAtom::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 js_NewDependentString(cx, str, index, 1);
-}
-
-inline JSStaticAtom &
-JSAtom::length2Static(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];
-}
-
-inline JSStaticAtom &
-JSAtom::length2Static(uint32 i)
-{
-    JS_ASSERT(i < 100);
-    return length2Static('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)
-{
-    switch (length) {
-      case 1:
-        if (chars[0] < UNIT_STATIC_LIMIT)
-            return &unitStatic(chars[0]);
-        return NULL;
-      case 2:
-        if (fitsInSmallChar(chars[0]) && fitsInSmallChar(chars[1]))
-            return &length2Static(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".
-         */
-        JS_STATIC_ASSERT(INT_STATIC_LIMIT <= 999);
-        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 NULL;
-    }
-
-    return NULL;
-}
-
-JS_ALWAYS_INLINE void
-JSString::finalize(JSContext *cx)
-{
-    JS_ASSERT(!isStaticAtom() && !isShort());
-
-    JS_RUNTIME_UNMETER(cx->runtime, liveStrings);
-
-    if (isDependent())
-        JS_RUNTIME_UNMETER(cx->runtime, liveDependentStrings);
-    else if (isFlat())
-        asFlat().finalize(cx->runtime);
-    else
-        JS_ASSERT(isRope());
-}
-
-inline void
-JSFlatString::finalize(JSRuntime *rt)
-{
-    JS_ASSERT(!isShort());
-
-    /*
-     * This check depends on the fact that 'chars' is only initialized to the
-     * beginning of inlineStorage. E.g., this is not the case for short strings.
-     */
-    if (chars() != d.inlineStorage)
-        rt->free_(const_cast<jschar *>(chars()));
-}
-
-inline void
-JSShortString::finalize(JSContext *cx)
-{
-    JS_ASSERT(isShort());
-    JS_RUNTIME_UNMETER(cx->runtime, liveStrings);
-}
-
-inline void
-JSAtom::finalize(JSRuntime *rt)
-{
-    JS_ASSERT(isAtom());
-    if (arenaHeader()->getThingKind() == js::gc::FINALIZE_STRING)
-        asFlat().finalize(rt);
-    else
-        JS_ASSERT(arenaHeader()->getThingKind() == js::gc::FINALIZE_SHORT_STRING);
-}
-
-inline void
-JSExternalString::finalize(JSContext *cx)
-{
-    JS_RUNTIME_UNMETER(cx->runtime, liveStrings);
-    if (JSStringFinalizeOp finalizer = str_finalizers[externalType()])
-        finalizer(cx, this);
-}
-
-inline void
-JSExternalString::finalize()
-{
-    JSStringFinalizeOp finalizer = str_finalizers[externalType()];
-    if (finalizer) {
-        /*
-         * Assume that the finalizer for the permanently interned
-         * string knows how to deal with null context.
-         */
-        finalizer(NULL, this);
-    }
-}
-
-namespace js {
-
 class RopeBuilder {
     JSContext *cx;
     JSString *res;
 
   public:
     RopeBuilder(JSContext *cx)
       : cx(cx), res(cx->runtime->emptyString)
     {}
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -58,17 +58,16 @@
 #include "jsanalyze.h"
 #include "methodjit/BaseCompiler.h"
 #include "methodjit/ICRepatcher.h"
 
 #include "jsinterpinlines.h"
 #include "jspropertycacheinlines.h"
 #include "jsscopeinlines.h"
 #include "jsscriptinlines.h"
-#include "jsstrinlines.h"
 #include "jsobjinlines.h"
 #include "jscntxtinlines.h"
 #include "jsatominlines.h"
 #include "StubCalls-inl.h"
 #include "MethodJIT-inl.h"
 
 #include "jsautooplen.h"
 
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -45,33 +45,35 @@
 #include "jsiter.h"
 #include "jsnum.h"
 #include "jsxml.h"
 #include "jsstaticcheck.h"
 #include "jsbool.h"
 #include "assembler/assembler/MacroAssemblerCodeRef.h"
 #include "jsiter.h"
 #include "jstypes.h"
+#include "vm/String.h"
 #include "methodjit/Compiler.h"
 #include "methodjit/StubCalls.h"
 
 #include "jsinterpinlines.h"
 #include "jspropertycache.h"
 #include "jspropertycacheinlines.h"
 #include "jsscopeinlines.h"
 #include "jsscriptinlines.h"
-#include "jsstrinlines.h"
 #include "jsnuminlines.h"
 #include "jsobjinlines.h"
 #include "jscntxtinlines.h"
 #include "jsatominlines.h"
 #include "StubCalls-inl.h"
 #include "jsfuninlines.h"
 #include "jstypedarray.h"
 
+#include "vm/String-inl.h"
+
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
 
 #include "jsautooplen.h"
 
 using namespace js;
 using namespace js::mjit;
new file mode 100644
--- /dev/null
+++ b/js/src/vm/String-inl.h
@@ -0,0 +1,367 @@
+/* -*- 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
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is SpiderMonkey JavaScript engine.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Luke Wagner <luke@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#ifndef String_inl_h__
+#define String_inl_h__
+
+#include "String.h"
+
+#include "jsgcinlines.h"
+
+JS_ALWAYS_INLINE void
+JSRope::init(JSString *left, JSString *right, size_t length)
+{
+    d.lengthAndFlags = buildLengthAndFlags(length, ROPE_BIT);
+    d.u1.left = left;
+    d.s.u2.right = right;
+}
+
+JS_ALWAYS_INLINE JSRope *
+JSRope::new_(JSContext *cx, JSString *left, JSString *right, size_t length)
+{
+    JSRope *str = (JSRope *)js_NewGCString(cx);
+    if (!str)
+        return NULL;
+    str->init(left, right, length);
+    return str;
+}
+
+JS_ALWAYS_INLINE void
+JSDependentString::init(JSLinearString *base, const jschar *chars, size_t length)
+{
+    d.lengthAndFlags = buildLengthAndFlags(length, DEPENDENT_BIT);
+    d.u1.chars = chars;
+    d.s.u2.base = base;
+}
+
+JS_ALWAYS_INLINE JSDependentString *
+JSDependentString::new_(JSContext *cx, JSLinearString *base, const jschar *chars, size_t length)
+{
+    /* Try to avoid long chains of dependent strings. */
+    while (base->isDependent())
+        base = base->asDependent().base();
+
+    JS_ASSERT(base->isFlat());
+    JS_ASSERT(chars >= base->chars() && chars < base->chars() + base->length());
+    JS_ASSERT(length <= base->length() - (chars - base->chars()));
+
+    JSDependentString *str = (JSDependentString *)js_NewGCString(cx);
+    if (!str)
+        return NULL;
+    str->init(base, chars, length);
+#ifdef DEBUG
+    JSRuntime *rt = cx->runtime;
+    JS_RUNTIME_METER(rt, liveDependentStrings);
+    JS_RUNTIME_METER(rt, totalDependentStrings);
+    JS_RUNTIME_METER(rt, liveStrings);
+    JS_RUNTIME_METER(rt, totalStrings);
+    JS_LOCK_RUNTIME_VOID(rt,
+        (rt->strdepLengthSum += (double)length,
+         rt->strdepLengthSquaredSum += (double)length * (double)length));
+    JS_LOCK_RUNTIME_VOID(rt,
+        (rt->lengthSum += (double)length,
+         rt->lengthSquaredSum += (double)length * (double)length));
+#endif
+    return str;
+}
+
+JS_ALWAYS_INLINE void
+JSFixedString::init(const jschar *chars, size_t length)
+{
+    d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
+    d.u1.chars = chars;
+}
+
+JS_ALWAYS_INLINE JSFixedString *
+JSFixedString::new_(JSContext *cx, const jschar *chars, size_t length)
+{
+    JS_ASSERT(length <= MAX_LENGTH);
+    JS_ASSERT(chars[length] == jschar(0));
+
+    JSFixedString *str = (JSFixedString *)js_NewGCString(cx);
+    if (!str)
+        return NULL;
+    str->init(chars, length);
+
+#ifdef DEBUG
+    JSRuntime *rt = cx->runtime;
+    JS_RUNTIME_METER(rt, liveStrings);
+    JS_RUNTIME_METER(rt, totalStrings);
+    JS_LOCK_RUNTIME_VOID(rt,
+        (rt->lengthSum += (double)length,
+         rt->lengthSquaredSum += (double)length * (double)length));
+#endif
+    return str;
+}
+
+JS_ALWAYS_INLINE JSAtom *
+JSFixedString::morphAtomizedStringIntoAtom()
+{
+    JS_ASSERT((d.lengthAndFlags & FLAGS_MASK) == JS_BIT(2));
+    JS_STATIC_ASSERT(NON_STATIC_ATOM == JS_BIT(3));
+    d.lengthAndFlags ^= (JS_BIT(2) | JS_BIT(3));
+    return &asAtom();
+}
+
+JS_ALWAYS_INLINE JSInlineString *
+JSInlineString::new_(JSContext *cx)
+{
+    return (JSInlineString *)js_NewGCString(cx);
+}
+
+JS_ALWAYS_INLINE jschar *
+JSInlineString::init(size_t length)
+{
+    d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
+    d.u1.chars = d.inlineStorage;
+    JS_ASSERT(lengthFits(length) || (isShort() && JSShortString::lengthFits(length)));
+    return d.inlineStorage;
+}
+
+JS_ALWAYS_INLINE void
+JSInlineString::resetLength(size_t length)
+{
+    d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
+    JS_ASSERT(lengthFits(length) || (isShort() && JSShortString::lengthFits(length)));
+}
+
+JS_ALWAYS_INLINE JSShortString *
+JSShortString::new_(JSContext *cx)
+{
+    return js_NewGCShortString(cx);
+}
+
+JS_ALWAYS_INLINE void
+JSShortString::initAtOffsetInBuffer(const jschar *chars, size_t length)
+{
+    JS_ASSERT(lengthFits(length + (chars - d.inlineStorage)));
+    JS_ASSERT(chars >= d.inlineStorage && chars < d.inlineStorage + MAX_SHORT_LENGTH);
+    d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
+    d.u1.chars = chars;
+}
+
+JS_ALWAYS_INLINE void
+JSExternalString::init(const jschar *chars, size_t length, intN type, void *closure)
+{
+    d.lengthAndFlags = buildLengthAndFlags(length, FIXED_FLAGS);
+    d.u1.chars = chars;
+    d.s.u2.externalType = type;
+    d.s.u3.externalClosure = closure;
+}
+
+JS_ALWAYS_INLINE JSExternalString *
+JSExternalString::new_(JSContext *cx, const jschar *chars, size_t length, intN type, void *closure)
+{
+    JS_ASSERT(uintN(type) < JSExternalString::TYPE_LIMIT);
+    JS_ASSERT(chars[length] == 0);
+
+    JSExternalString *str = js_NewGCExternalString(cx);
+    if (!str)
+        return NULL;
+    str->init(chars, length, type, closure);
+    cx->runtime->updateMallocCounter((length + 1) * sizeof(jschar));
+    return str;
+}
+
+inline bool
+JSAtom::fitsInSmallChar(jschar c)
+{
+    return c < SMALL_CHAR_LIMIT && toSmallChar[c] != INVALID_SMALL_CHAR;
+}
+
+inline bool
+JSAtom::hasUnitStatic(jschar c)
+{
+    return c < UNIT_STATIC_LIMIT;
+}
+
+inline JSStaticAtom &
+JSAtom::unitStatic(jschar c)
+{
+    JS_ASSERT(hasUnitStatic(c));
+    return (JSStaticAtom &)unitStaticTable[c];
+}
+
+inline bool
+JSAtom::hasIntStatic(int32 i)
+{
+    return uint32(i) < INT_STATIC_LIMIT;
+}
+
+inline JSStaticAtom &
+JSAtom::intStatic(jsint i)
+{
+    JS_ASSERT(hasIntStatic(i));
+    return (JSStaticAtom &)*intStaticTable[i];
+}
+
+inline JSLinearString *
+JSAtom::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 js_NewDependentString(cx, str, index, 1);
+}
+
+inline JSStaticAtom &
+JSAtom::length2Static(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];
+}
+
+inline JSStaticAtom &
+JSAtom::length2Static(uint32 i)
+{
+    JS_ASSERT(i < 100);
+    return length2Static('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)
+{
+    switch (length) {
+      case 1:
+        if (chars[0] < UNIT_STATIC_LIMIT)
+            return &unitStatic(chars[0]);
+        return NULL;
+      case 2:
+        if (fitsInSmallChar(chars[0]) && fitsInSmallChar(chars[1]))
+            return &length2Static(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".
+         */
+        JS_STATIC_ASSERT(INT_STATIC_LIMIT <= 999);
+        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 NULL;
+    }
+
+    return NULL;
+}
+
+JS_ALWAYS_INLINE void
+JSString::finalize(JSContext *cx)
+{
+    JS_ASSERT(!isStaticAtom() && !isShort());
+
+    JS_RUNTIME_UNMETER(cx->runtime, liveStrings);
+
+    if (isDependent())
+        JS_RUNTIME_UNMETER(cx->runtime, liveDependentStrings);
+    else if (isFlat())
+        asFlat().finalize(cx->runtime);
+    else
+        JS_ASSERT(isRope());
+}
+
+inline void
+JSFlatString::finalize(JSRuntime *rt)
+{
+    JS_ASSERT(!isShort());
+
+    /*
+     * This check depends on the fact that 'chars' is only initialized to the
+     * beginning of inlineStorage. E.g., this is not the case for short strings.
+     */
+    if (chars() != d.inlineStorage)
+        rt->free_(const_cast<jschar *>(chars()));
+}
+
+inline void
+JSShortString::finalize(JSContext *cx)
+{
+    JS_ASSERT(isShort());
+    JS_RUNTIME_UNMETER(cx->runtime, liveStrings);
+}
+
+inline void
+JSAtom::finalize(JSRuntime *rt)
+{
+    JS_ASSERT(isAtom());
+    if (arenaHeader()->getThingKind() == js::gc::FINALIZE_STRING)
+        asFlat().finalize(rt);
+    else
+        JS_ASSERT(arenaHeader()->getThingKind() == js::gc::FINALIZE_SHORT_STRING);
+}
+
+inline void
+JSExternalString::finalize(JSContext *cx)
+{
+    JS_RUNTIME_UNMETER(cx->runtime, liveStrings);
+    if (JSStringFinalizeOp finalizer = str_finalizers[externalType()])
+        finalizer(cx, this);
+}
+
+inline void
+JSExternalString::finalize()
+{
+    JSStringFinalizeOp finalizer = str_finalizers[externalType()];
+    if (finalizer) {
+        /*
+         * Assume that the finalizer for the permanently interned
+         * string knows how to deal with null context.
+         */
+        finalizer(NULL, this);
+    }
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/js/src/vm/String.cpp
@@ -0,0 +1,329 @@
+/* -*- 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
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is SpiderMonkey JavaScript engine.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Luke Wagner <luke@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 "String.h"
+#include "String-inl.h"
+
+using namespace js;
+
+bool
+JSString::isShort() const
+{
+    bool is_short = arenaHeader()->getThingKind() == gc::FINALIZE_SHORT_STRING;
+    JS_ASSERT_IF(is_short, isFlat());
+    return is_short;
+}
+
+bool
+JSString::isFixed() const
+{
+    return isFlat() && !isExtensible();
+}
+
+bool
+JSString::isInline() const
+{
+    return isFixed() && (d.u1.chars == d.inlineStorage || isShort());
+}
+
+bool
+JSString::isExternal() const
+{
+    bool is_external = arenaHeader()->getThingKind() == gc::FINALIZE_EXTERNAL_STRING;
+    JS_ASSERT_IF(is_external, isFixed());
+    return is_external;
+}
+
+void
+JSLinearString::mark(JSTracer *)
+{
+    JSLinearString *str = this;
+    while (!str->isStaticAtom() && str->markIfUnmarked() && str->isDependent())
+        str = str->asDependent().base();
+}
+
+size_t
+JSString::charsHeapSize()
+{
+    /* JSRope: do nothing, we'll count all children chars when we hit the leaf strings. */
+    if (isRope())
+        return 0;
+
+    JS_ASSERT(isLinear());
+
+    /* JSDependentString: do nothing, we'll count the chars when we hit the base string. */
+    if (isDependent())
+        return 0;
+
+    JS_ASSERT(isFlat());
+
+    /* JSExtensibleString: count the full capacity, not just the used space. */
+    if (isExtensible())
+        return asExtensible().capacity() * sizeof(jschar);
+
+    JS_ASSERT(isFixed());
+
+    /* 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. */
+    return length() * sizeof(jschar);
+}
+
+static JS_ALWAYS_INLINE size_t
+RopeCapacityFor(size_t length)
+{
+    static const size_t ROPE_DOUBLING_MAX = 1024 * 1024;
+
+    /*
+     * Grow by 12.5% if the buffer is very large. Otherwise, round up to the
+     * next power of 2. This is similar to what we do with arrays; see
+     * JSObject::ensureDenseArrayElements.
+     */
+    if (length > ROPE_DOUBLING_MAX)
+        return length + (length / 8);
+    return RoundUpPow2(length);
+}
+
+static JS_ALWAYS_INLINE jschar *
+AllocChars(JSContext *maybecx, size_t wholeCapacity)
+{
+    /* +1 for the null char at the end. */
+    JS_STATIC_ASSERT(JSString::MAX_LENGTH * sizeof(jschar) < UINT32_MAX);
+    size_t bytes = (wholeCapacity + 1) * sizeof(jschar);
+    if (maybecx)
+        return (jschar *)maybecx->malloc_(bytes);
+    return (jschar *)OffTheBooks::malloc_(bytes);
+}
+
+JSFlatString *
+JSRope::flatten(JSContext *maybecx)
+{
+    /*
+     * Perform a depth-first dag traversal, splatting each node's characters
+     * into a contiguous buffer. Visit each rope node three times:
+     *   1. record position in the buffer and recurse into left child;
+     *   2. recurse into the right child;
+     *   3. transform the node into a dependent string.
+     * To avoid maintaining a stack, tree nodes are mutated to indicate how many
+     * times they have been visited. Since ropes can be dags, a node may be
+     * encountered multiple times during traversal. However, step 3 above leaves
+     * a valid dependent string, so everything works out. This algorithm is
+     * homomorphic to marking code.
+     *
+     * While ropes avoid all sorts of quadratic cases with string
+     * concatenation, they can't help when ropes are immediately flattened.
+     * One idiomatic case that we'd like to keep linear (and has traditionally
+     * been linear in SM and other JS engines) is:
+     *
+     *   while (...) {
+     *     s += ...
+     *     s.flatten
+     *   }
+     *
+     * To do this, when the buffer for a to-be-flattened rope is allocated, the
+     * allocation size is rounded up. Then, if the resulting flat string is the
+     * left-hand side of a new rope that gets flattened and there is enough
+     * capacity, the rope is flattened into the same buffer, thereby avoiding
+     * copying the left-hand side. Clearing the 'extensible' bit turns off this
+     * optimization. This is necessary, e.g., when the JSAPI hands out the raw
+     * null-terminated char array of a flat string.
+     *
+     * N.B. This optimization can create chains of dependent strings.
+     */
+    const size_t wholeLength = length();
+    size_t wholeCapacity;
+    jschar *wholeChars;
+    JSString *str = this;
+    jschar *pos;
+
+    if (this->leftChild()->isExtensible()) {
+        JSExtensibleString &left = this->leftChild()->asExtensible();
+        size_t capacity = left.capacity();
+        if (capacity >= wholeLength) {
+            wholeCapacity = capacity;
+            wholeChars = const_cast<jschar *>(left.chars());
+            size_t bits = left.d.lengthAndFlags;
+            pos = wholeChars + (bits >> LENGTH_SHIFT);
+            left.d.lengthAndFlags = bits ^ (EXTENSIBLE_FLAGS | DEPENDENT_BIT);
+            left.d.s.u2.base = (JSLinearString *)this;  /* will be true on exit */
+            goto visit_right_child;
+        }
+    }
+
+    wholeCapacity = RopeCapacityFor(wholeLength);
+    wholeChars = AllocChars(maybecx, wholeCapacity);
+    if (!wholeChars)
+        return NULL;
+
+    pos = wholeChars;
+    first_visit_node: {
+        JSString &left = *str->d.u1.left;
+        str->d.u1.chars = pos;
+        if (left.isRope()) {
+            left.d.s.u3.parent = str;          /* Return to this when 'left' done, */
+            left.d.lengthAndFlags = 0x200;     /* but goto visit_right_child. */
+            str = &left;
+            goto first_visit_node;
+        }
+        size_t len = left.length();
+        PodCopy(pos, left.d.u1.chars, len);
+        pos += len;
+    }
+    visit_right_child: {
+        JSString &right = *str->d.s.u2.right;
+        if (right.isRope()) {
+            right.d.s.u3.parent = str;         /* Return to this node when 'right' done, */
+            right.d.lengthAndFlags = 0x300;    /* but goto finish_node. */
+            str = &right;
+            goto first_visit_node;
+        }
+        size_t len = right.length();
+        PodCopy(pos, right.d.u1.chars, len);
+        pos += len;
+    }
+    finish_node: {
+        if (str == this) {
+            JS_ASSERT(pos == wholeChars + wholeLength);
+            *pos = '\0';
+            str->d.lengthAndFlags = buildLengthAndFlags(wholeLength, EXTENSIBLE_FLAGS);
+            str->d.u1.chars = wholeChars;
+            str->d.s.u2.capacity = wholeCapacity;
+            return &this->asFlat();
+        }
+        size_t progress = str->d.lengthAndFlags;
+        str->d.lengthAndFlags = buildLengthAndFlags(pos - str->d.u1.chars, DEPENDENT_BIT);
+        str->d.s.u2.base = (JSLinearString *)this;       /* will be true on exit */
+        str = str->d.s.u3.parent;
+        if (progress == 0x200)
+            goto visit_right_child;
+        JS_ASSERT(progress == 0x300);
+        goto finish_node;
+    }
+}
+
+JSString * JS_FASTCALL
+js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
+{
+    JS_ASSERT_IF(!left->isAtom(), left->compartment() == cx->compartment);
+    JS_ASSERT_IF(!right->isAtom(), right->compartment() == cx->compartment);
+
+    size_t leftLen = left->length();
+    if (leftLen == 0)
+        return right;
+
+    size_t rightLen = right->length();
+    if (rightLen == 0)
+        return left;
+
+    size_t wholeLength = leftLen + rightLen;
+
+    if (JSShortString::lengthFits(wholeLength)) {
+        JSShortString *str = js_NewGCShortString(cx);
+        if (!str)
+            return NULL;
+        const jschar *leftChars = left->getChars(cx);
+        if (!leftChars)
+            return NULL;
+        const jschar *rightChars = right->getChars(cx);
+        if (!rightChars)
+            return NULL;
+
+        jschar *buf = str->init(wholeLength);
+        PodCopy(buf, leftChars, leftLen);
+        PodCopy(buf + leftLen, rightChars, rightLen);
+        buf[wholeLength] = 0;
+        return str;
+    }
+
+    if (wholeLength > JSString::MAX_LENGTH) {
+        if (JS_ON_TRACE(cx)) {
+            if (!CanLeaveTrace(cx))
+                return NULL;
+            LeaveTrace(cx);
+        }
+        js_ReportAllocationOverflow(cx);
+        return NULL;
+    }
+
+    return JSRope::new_(cx, left, right, wholeLength);
+}
+
+JSFixedString *
+JSDependentString::undepend(JSContext *cx)
+{
+    JS_ASSERT(isDependent());
+
+    size_t n = length();
+    size_t size = (n + 1) * sizeof(jschar);
+    jschar *s = (jschar *) cx->malloc_(size);
+    if (!s)
+        return NULL;
+
+    PodCopy(s, chars(), n);
+    s[n] = 0;
+
+    d.lengthAndFlags = buildLengthAndFlags(n, FIXED_FLAGS);
+    d.u1.chars = s;
+
+#ifdef DEBUG
+    JSRuntime *rt = cx->runtime;
+    JS_RUNTIME_UNMETER(rt, liveDependentStrings);
+    JS_RUNTIME_UNMETER(rt, totalDependentStrings);
+    JS_LOCK_RUNTIME_VOID(rt,
+        (rt->strdepLengthSum -= (double)n,
+         rt->strdepLengthSquaredSum -= (double)n * (double)n));
+#endif
+
+    return &this->asFixed();
+}
+
+JSStringFinalizeOp JSExternalString::str_finalizers[JSExternalString::TYPE_LIMIT] = {
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
new file mode 100644
--- /dev/null
+++ b/js/src/vm/String.h
@@ -0,0 +1,755 @@
+/* -*- 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
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is SpiderMonkey JavaScript engine.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Luke Wagner <luke@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#ifndef String_h_
+#define String_h_
+
+#include "jscell.h"
+
+class JSDependentString;
+class JSExtensibleString;
+class JSExternalString;
+class JSLinearString;
+class JSFixedString;
+class JSStaticAtom;
+class JSRope;
+class JSAtom;
+
+/*
+ * JavaScript strings
+ *
+ * Conceptually, a JS string is just an array of chars and a length. To improve
+ * performance of common string operations, the following optimizations are
+ * made which affect the engine's representation of strings:
+ *
+ *  - The plain vanilla representation is a "flat" string which consists of a
+ *    string header in the GC heap and a malloc'd null terminated char array.
+ *
+ *  - To avoid copying a substring of an existing "base" string , a "dependent"
+ *    string (JSDependentString) can be created which points into the base
+ *    string's char array.
+ *
+ *  - To avoid O(n^2) char buffer copying, a "rope" node (JSRope) can be created
+ *    to represent a delayed string concatenation. Concatenation (called
+ *    flattening) is performed if and when a linear char array is requested. In
+ *    general, ropes form a binary dag whose internal nodes are JSRope string
+ *    headers with no associated char array and whose leaf nodes are either flat
+ *    or dependent strings.
+ *
+ *  - To avoid copying the left-hand side when flattening, the left-hand side's
+ *    buffer may be grown to make space for a copy of the right-hand side (see
+ *    comment in JSString::flatten). This optimization requires that there are
+ *    no external pointers into the char array. We conservatively maintain this
+ *    property via a flat string's "extensible" property.
+ *
+ *  - 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:
+ *
+ * C++ type                     operations+fields / invariants+properties
+ *
+ * JSString (abstract)          getCharsZ, getChars, length / -
+ *  | \
+ *  | JSRope                    leftChild, rightChild / -
+ *  |
+ * JSLinearString (abstract)    chars / not null-terminated
+ *  | \
+ *  | JSDependentString         base / -
+ *  |
+ * JSFlatString (abstract)      chars / not null-terminated
+ *  | \
+ *  | JSExtensibleString        capacity / no external pointers into char array
+ *  |
+ * JSFixedString                - / may have external pointers into char array
+ *  | \  \
+ *  |  \ 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
+ *
+ * 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.
+ *
+ * The ensureX() operations mutate 'this' in place to effectively the type to be
+ * at least X (e.g., ensureLinear will change a JSRope to be a JSFlatString).
+ */
+
+class JSString : public js::gc::Cell
+{
+  protected:
+    static const size_t NUM_INLINE_CHARS = 2 * sizeof(void *) / sizeof(jschar);
+
+    /* Fields only apply to string types commented on the right. */
+    struct Data
+    {
+        size_t                     lengthAndFlags;      /* JSString */
+        union {
+            const jschar           *chars;              /* JSLinearString */
+            JSString               *left;               /* JSRope */
+        } u1;
+        union {
+            jschar                 inlineStorage[NUM_INLINE_CHARS]; /* JS(Inline|Short)String */
+            struct {
+                union {
+                    JSLinearString *base;               /* JSDependentString */
+                    JSString       *right;              /* JSRope */
+                    size_t         capacity;            /* JSFlatString (extensible) */
+                    size_t         externalType;        /* JSExternalString */
+                } u2;
+                union {
+                    JSString       *parent;             /* JSRope (temporary) */
+                    void           *externalClosure;    /* JSExternalString */
+                    size_t         reserved;            /* may use for bug 615290 */
+                } u3;
+            } s;
+        };
+    } d;
+
+  public:
+    /* Flags exposed only for jits */
+
+    static const size_t LENGTH_SHIFT      = 4;
+    static const size_t FLAGS_MASK        = JS_BITMASK(LENGTH_SHIFT);
+    static const size_t MAX_LENGTH        = JS_BIT(32 - LENGTH_SHIFT) - 1;
+
+    /*
+     * The low LENGTH_SHIFT bits of lengthAndFlags are used to encode the type
+     * of the string. The remaining bits store the string length (which must be
+     * less or equal than MAX_LENGTH).
+     *
+     * Instead of using a dense index to represent the most-derived type, string
+     * types are encoded to allow single-op tests for hot queries (isRope,
+     * isDependent, isFlat, isAtom, isStaticAtom) which, in view of subtyping,
+     * would require slower (isX() || isY() || isZ()).
+     *
+     * The string type encoding can be summarized as follows. The "instance
+     * encoding" entry for a type specifies the flag bits used to create a
+     * string instance of that type. Abstract types have no instances and thus
+     * have no such entry. The "subtype predicate" entry for a type specifies
+     * the predicate used to query whether a JSString instance is subtype
+     * (reflexively) of that type.
+     *
+     *   string       instance   subtype
+     *   type         encoding   predicate
+     *
+     *   String       -          true
+     *   Rope         0001       xxx1
+     *   Linear       -          xxx0
+     *   Dependent    0010       xx1x
+     *   Flat         -          xx00
+     *   Extensible   1100       1100
+     *   Fixed        0100       isFlat && !isExtensible
+     *   Inline       0100       isFixed && (u1.chars == inlineStorage || isShort)
+     *   Short        0100       xxxx && header in FINALIZE_SHORT_STRING arena
+     *   External     0100       xxxx && header in FINALIZE_EXTERNAL_STRING arena
+     *   Atom         1000       x000
+     *   InlineAtom   1000       1000 && is Inline
+     *   ShortAtom    1000       1000 && is Short
+     *   StaticAtom   0000       0000
+     */
+
+    static const size_t ROPE_BIT          = JS_BIT(0);
+
+    static const size_t LINEAR_MASK       = JS_BITMASK(1);
+    static const size_t LINEAR_FLAGS      = 0x0;
+
+    static const size_t DEPENDENT_BIT     = JS_BIT(1);
+
+    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() {
+        JS_STATIC_ASSERT(JS_BITS_PER_WORD >= 32);
+        JS_STATIC_ASSERT(((JSString::MAX_LENGTH << JSString::LENGTH_SHIFT) >>
+                           JSString::LENGTH_SHIFT) == JSString::MAX_LENGTH);
+        JS_STATIC_ASSERT(sizeof(JSString) ==
+                         offsetof(JSString, d.inlineStorage) +
+                         NUM_INLINE_CHARS * sizeof(jschar));
+    }
+
+    /* Avoid lame compile errors in JSRope::flatten */
+    friend class JSRope;
+
+  public:
+    /* All strings have length. */
+
+    JS_ALWAYS_INLINE
+    size_t length() const {
+        return d.lengthAndFlags >> LENGTH_SHIFT;
+    }
+
+    JS_ALWAYS_INLINE
+    bool empty() const {
+        return d.lengthAndFlags <= FLAGS_MASK;
+    }
+
+    /*
+     * All strings have a fallible operation to get an array of chars.
+     * getCharsZ additionally ensures the array is null terminated.
+     */
+
+    inline const jschar *getChars(JSContext *cx);
+    inline const jschar *getCharsZ(JSContext *cx);
+
+    /* Fallible conversions to more-derived string types. */
+
+    inline JSLinearString *ensureLinear(JSContext *cx);
+    inline JSFlatString *ensureFlat(JSContext *cx);
+    inline JSFixedString *ensureFixed(JSContext *cx);
+
+    /* Type query and debug-checked casts */
+
+    JS_ALWAYS_INLINE
+    bool isRope() const {
+        bool rope = d.lengthAndFlags & ROPE_BIT;
+        JS_ASSERT_IF(rope, (d.lengthAndFlags & FLAGS_MASK) == ROPE_BIT);
+        return rope;
+    }
+
+    JS_ALWAYS_INLINE
+    JSRope &asRope() {
+        JS_ASSERT(isRope());
+        return *(JSRope *)this;
+    }
+
+    JS_ALWAYS_INLINE
+    bool isLinear() const {
+        return (d.lengthAndFlags & LINEAR_MASK) == LINEAR_FLAGS;
+    }
+
+    JS_ALWAYS_INLINE
+    JSLinearString &asLinear() {
+        JS_ASSERT(isLinear());
+        return *(JSLinearString *)this;
+    }
+
+    JS_ALWAYS_INLINE
+    bool isDependent() const {
+        bool dependent = d.lengthAndFlags & DEPENDENT_BIT;
+        JS_ASSERT_IF(dependent, (d.lengthAndFlags & FLAGS_MASK) == DEPENDENT_BIT);
+        return dependent;
+    }
+
+    JS_ALWAYS_INLINE
+    JSDependentString &asDependent() {
+        JS_ASSERT(isDependent());
+        return *(JSDependentString *)this;
+    }
+
+    JS_ALWAYS_INLINE
+    bool isFlat() const {
+        return (d.lengthAndFlags & FLAT_MASK) == FLAT_FLAGS;
+    }
+
+    JS_ALWAYS_INLINE
+    JSFlatString &asFlat() {
+        JS_ASSERT(isFlat());
+        return *(JSFlatString *)this;
+    }
+
+    JS_ALWAYS_INLINE
+    bool isExtensible() const {
+        return (d.lengthAndFlags & FLAGS_MASK) == EXTENSIBLE_FLAGS;
+    }
+
+    JS_ALWAYS_INLINE
+    JSExtensibleString &asExtensible() const {
+        JS_ASSERT(isExtensible());
+        return *(JSExtensibleString *)this;
+    }
+
+    /* For hot code, prefer other type queries. */
+    bool isShort() const;
+    bool isFixed() const;
+    bool isInline() const;
+
+    JS_ALWAYS_INLINE
+    JSFixedString &asFixed() {
+        JS_ASSERT(isFixed());
+        return *(JSFixedString *)this;
+    }
+
+    bool isExternal() const;
+
+    JS_ALWAYS_INLINE
+    JSExternalString &asExternal() {
+        JS_ASSERT(isExternal());
+        return *(JSExternalString *)this;
+    }
+
+    JS_ALWAYS_INLINE
+    bool isAtom() const {
+        bool atomized = (d.lengthAndFlags & ATOM_MASK) == ATOM_FLAGS;
+        JS_ASSERT_IF(atomized, isFlat());
+        return atomized;
+    }
+
+    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();
+
+    /* Offsets for direct field from jit code. */
+
+    static size_t offsetOfLengthAndFlags() {
+        return offsetof(JSString, d.lengthAndFlags);
+    }
+
+    static size_t offsetOfChars() {
+        return offsetof(JSString, d.u1.chars);
+    }
+};
+
+class JSRope : public JSString
+{
+    friend class JSString;
+    JSFlatString *flatten(JSContext *cx);
+
+    void init(JSString *left, JSString *right, size_t length);
+
+  public:
+    static inline JSRope *new_(JSContext *cx, JSString *left,
+                               JSString *right, size_t length);
+
+    inline JSString *leftChild() const {
+        JS_ASSERT(isRope());
+        return d.u1.left;
+    }
+
+    inline JSString *rightChild() const {
+        JS_ASSERT(isRope());
+        return d.s.u2.right;
+    }
+};
+
+JS_STATIC_ASSERT(sizeof(JSRope) == sizeof(JSString));
+
+class JSLinearString : public JSString
+{
+    friend class JSString;
+
+  public:
+    void mark(JSTracer *trc);
+
+    JS_ALWAYS_INLINE
+    const jschar *chars() const {
+        JS_ASSERT(isLinear());
+        return d.u1.chars;
+    }
+};
+
+JS_STATIC_ASSERT(sizeof(JSLinearString) == sizeof(JSString));
+
+class JSDependentString : public JSLinearString
+{
+    friend class JSString;
+    JSFixedString *undepend(JSContext *cx);
+
+    void init(JSLinearString *base, const jschar *chars, size_t length);
+
+  public:
+    static inline JSDependentString *new_(JSContext *cx, JSLinearString *base,
+                                          const jschar *chars, size_t length);
+
+    JSLinearString *base() const {
+        JS_ASSERT(isDependent());
+        return d.s.u2.base;
+    }
+};
+
+JS_STATIC_ASSERT(sizeof(JSDependentString) == sizeof(JSString));
+
+class JSFlatString : public JSLinearString
+{
+    friend class JSRope;
+    void morphExtensibleIntoDependent(JSLinearString *base) {
+        d.lengthAndFlags = buildLengthAndFlags(length(), DEPENDENT_BIT);
+        d.s.u2.base = base;
+    }
+
+  public:
+    JS_ALWAYS_INLINE
+    const jschar *charsZ() const {
+        JS_ASSERT(isFlat());
+        return chars();
+    }
+
+    /* Only called by the GC for strings with the FINALIZE_STRING kind. */
+
+    inline void finalize(JSRuntime *rt);
+};
+
+JS_STATIC_ASSERT(sizeof(JSFlatString) == sizeof(JSString));
+
+class JSExtensibleString : public JSFlatString
+{
+  public:
+    JS_ALWAYS_INLINE
+    size_t capacity() const {
+        JS_ASSERT(isExtensible());
+        return d.s.u2.capacity;
+    }
+};
+
+JS_STATIC_ASSERT(sizeof(JSExtensibleString) == sizeof(JSString));
+
+class JSFixedString : public JSFlatString
+{
+    void init(const jschar *chars, size_t length);
+
+  public:
+    static inline JSFixedString *new_(JSContext *cx, const jschar *chars, size_t length);
+
+    /*
+     * Once a JSFixedString has been added to the atom state, this operation
+     * changes the type (in place, as reflected by the flag bits) of the
+     * JSFixedString into a JSAtom.
+     */
+    inline JSAtom *morphAtomizedStringIntoAtom();
+};
+
+JS_STATIC_ASSERT(sizeof(JSFixedString) == sizeof(JSString));
+
+class JSInlineString : public JSFixedString
+{
+    static const size_t MAX_INLINE_LENGTH = NUM_INLINE_CHARS - 1;
+
+  public:
+    static inline JSInlineString *new_(JSContext *cx);
+
+    inline jschar *init(size_t length);
+
+    inline void resetLength(size_t length);
+
+    static bool lengthFits(size_t length) {
+        return length <= MAX_INLINE_LENGTH;
+    }
+
+};
+
+JS_STATIC_ASSERT(sizeof(JSInlineString) == sizeof(JSString));
+
+class JSShortString : public JSInlineString
+{
+    /* This can be any value that is a multiple of Cell::CellSize. */
+    static const size_t INLINE_EXTENSION_CHARS = sizeof(JSString::Data) / sizeof(jschar);
+
+    static void staticAsserts() {
+        JS_STATIC_ASSERT(INLINE_EXTENSION_CHARS % js::gc::Cell::CellSize == 0);
+        JS_STATIC_ASSERT(MAX_SHORT_LENGTH + 1 ==
+                         (sizeof(JSShortString) -
+                          offsetof(JSShortString, d.inlineStorage)) / sizeof(jschar));
+    }
+
+    jschar inlineStorageExtension[INLINE_EXTENSION_CHARS];
+
+  public:
+    static inline JSShortString *new_(JSContext *cx);
+
+    jschar *inlineStorageBeforeInit() {
+        return d.inlineStorage;
+    }
+
+    inline void initAtOffsetInBuffer(const jschar *chars, size_t length);
+
+    static const size_t MAX_SHORT_LENGTH = JSString::NUM_INLINE_CHARS +
+                                           INLINE_EXTENSION_CHARS
+                                           -1 /* null terminator */;
+
+    static bool lengthFits(size_t length) {
+        return length <= MAX_SHORT_LENGTH;
+    }
+
+    /* Only called by the GC for strings with the FINALIZE_EXTERNAL_STRING kind. */
+
+    JS_ALWAYS_INLINE void finalize(JSContext *cx);
+};
+
+JS_STATIC_ASSERT(sizeof(JSShortString) == 2 * sizeof(JSString));
+
+/*
+ * The externalClosure stored in an external string is a black box to the JS
+ * engine; see JS_NewExternalStringWithClosure.
+ */
+class JSExternalString : public JSFixedString
+{
+    static void staticAsserts() {
+        JS_STATIC_ASSERT(TYPE_LIMIT == 8);
+    }
+
+    void init(const jschar *chars, size_t length, intN type, void *closure);
+
+  public:
+    static inline JSExternalString *new_(JSContext *cx, const jschar *chars,
+                                         size_t length, intN type, void *closure);
+
+    intN externalType() const {
+        JS_ASSERT(isExternal());
+        JS_ASSERT(d.s.u2.externalType < TYPE_LIMIT);
+        return d.s.u2.externalType;
+    }
+
+    void *externalClosure() const {
+        JS_ASSERT(isExternal());
+        return d.s.u3.externalClosure;
+    }
+
+    static const uintN TYPE_LIMIT = 8;
+    static JSStringFinalizeOp str_finalizers[TYPE_LIMIT];
+
+    static intN changeFinalizer(JSStringFinalizeOp oldop,
+                                JSStringFinalizeOp newop) {
+        for (uintN i = 0; i != JS_ARRAY_LENGTH(str_finalizers); i++) {
+            if (str_finalizers[i] == oldop) {
+                str_finalizers[i] = newop;
+                return intN(i);
+            }
+        }
+        return -1;
+    }
+
+    /* 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));
+
+class JSAtom : public JSFixedString
+{
+  public:
+    /* Exposed only for jits. */
+
+    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[];
+
+  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 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);
+
+    inline void finalize(JSRuntime *rt);
+};
+
+JS_STATIC_ASSERT(sizeof(JSAtom) == sizeof(JSString));
+
+class JSInlineAtom : public JSInlineString /*, JSAtom */
+{
+    /*
+     * JSInlineAtom is not explicitly used and is only present for consistency.
+     * See Atomize() for how JSInlineStrings get morphed into JSInlineAtoms.
+     */
+};
+
+JS_STATIC_ASSERT(sizeof(JSInlineAtom) == sizeof(JSInlineString));
+
+class JSShortAtom : public JSShortString /*, JSInlineAtom */
+{
+    /*
+     * 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
+{};
+
+JS_STATIC_ASSERT(sizeof(JSStaticAtom) == sizeof(JSString));
+
+/* Avoid requring vm/String-inl.h just to call getChars. */
+
+JS_ALWAYS_INLINE const jschar *
+JSString::getChars(JSContext *cx)
+{
+    if (JSLinearString *str = ensureLinear(cx))
+        return str->chars();
+    return NULL;
+}
+
+JS_ALWAYS_INLINE const jschar *
+JSString::getCharsZ(JSContext *cx)
+{
+    if (JSFlatString *str = ensureFlat(cx))
+        return str->chars();
+    return NULL;
+}
+
+JS_ALWAYS_INLINE JSLinearString *
+JSString::ensureLinear(JSContext *cx)
+{
+    return isLinear()
+           ? &asLinear()
+           : asRope().flatten(cx);
+}
+
+JS_ALWAYS_INLINE JSFlatString *
+JSString::ensureFlat(JSContext *cx)
+{
+    return isFlat()
+           ? &asFlat()
+           : isDependent()
+             ? asDependent().undepend(cx)
+             : asRope().flatten(cx);
+}
+
+JS_ALWAYS_INLINE JSFixedString *
+JSString::ensureFixed(JSContext *cx)
+{
+    if (!ensureFlat(cx))
+        return NULL;
+    if (isExtensible()) {
+        JS_ASSERT((d.lengthAndFlags & FLAT_MASK) == 0);
+        JS_STATIC_ASSERT(EXTENSIBLE_FLAGS == (JS_BIT(2) | JS_BIT(3)));
+        JS_STATIC_ASSERT(FIXED_FLAGS == JS_BIT(2));
+        d.lengthAndFlags ^= JS_BIT(3);
+    }
+    return &asFixed();
+}
+
+#endif
--- a/js/src/yarr/wtfbridge.h
+++ b/js/src/yarr/wtfbridge.h
@@ -43,16 +43,17 @@
 /*
  * WTF compatibility layer. This file provides various type and data
  * definitions for use by Yarr.
  */
 
 #include "jsstr.h"
 #include "jsprvtd.h"
 #include "jstl.h"
+#include "vm/String.h"
 #include "assembler/wtf/Platform.h"
 #include "assembler/jit/ExecutableAllocator.h"
 
 namespace JSC { namespace Yarr {
 
 /*
  * Basic type definitions.
  */