Bug 1439026 - Part 2: Move the most basic text-processing routines to util/Text.cpp. r=jandem.
authorJason Orendorff <jorendorff@mozilla.com>
Sat, 24 Feb 2018 07:33:57 -0600
changeset 407217 e27feda042d6
parent 407216 eb84980f64f2
child 407218 9a824b10df47
push id33596
push userncsoregi@mozilla.com
push dateFri, 09 Mar 2018 00:18:11 +0000
treeherdermozilla-central@31a33fc61956 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1439026
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1439026 - Part 2: Move the most basic text-processing routines to util/Text.cpp. r=jandem. Many operations on strings are also moved from builtin/String.cpp to vm/StringType.cpp.
js/src/builtin/String.cpp
js/src/builtin/String.h
js/src/builtin/TestingFunctions.cpp
js/src/frontend/FoldConstants.cpp
js/src/frontend/Parser.cpp
js/src/gc/Statistics.cpp
js/src/gc/Tracer.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/MIR.cpp
js/src/jit/OptimizationTracking.cpp
js/src/jit/SharedIC.cpp
js/src/jsapi-tests/testSharedImmutableStringsCache.cpp
js/src/jsapi-tests/testUbiNode.cpp
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsexn.cpp
js/src/moz.build
js/src/shell/OSObject.cpp
js/src/shell/js.cpp
js/src/util/Text.cpp
js/src/util/Text.h
js/src/vm/CodeCoverage.cpp
js/src/vm/Interpreter.cpp
js/src/vm/JSAtom.cpp
js/src/vm/JSObject.cpp
js/src/vm/JSScript.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/Shape.cpp
js/src/vm/SharedImmutableStringsCache.cpp
js/src/vm/StringType.cpp
js/src/vm/StringType.h
js/src/vm/TraceLoggingGraph.cpp
js/src/vm/TraceLoggingTypes.cpp
js/src/vm/UbiNode.cpp
js/src/vm/UbiNodeCensus.cpp
js/src/vm/UbiNodeShortestPaths.cpp
js/src/wasm/AsmJS.cpp
js/src/wasm/WasmCompile.cpp
js/src/wasm/WasmDebug.cpp
js/src/wasm/WasmGenerator.cpp
js/src/wasm/WasmJS.cpp
--- a/js/src/builtin/String.cpp
+++ b/js/src/builtin/String.cpp
@@ -58,20 +58,18 @@
 using namespace js;
 using namespace js::gc;
 
 using JS::Symbol;
 using JS::SymbolCode;
 
 using mozilla::CheckedInt;
 using mozilla::IsNaN;
-using mozilla::IsNegativeZero;
 using mozilla::IsSame;
 using mozilla::PodCopy;
-using mozilla::PodEqual;
 using mozilla::RangedPtr;
 
 using JS::AutoCheckCannotGC;
 
 static JSLinearString*
 ArgToLinearString(JSContext* cx, const CallArgs& args, unsigned argno)
 {
     if (argno >= args.length())
@@ -2411,39 +2409,16 @@ js::str_lastIndexOf(JSContext* cx, unsig
         else
             res = LastIndexOfImpl(textChars, len, searchStr->twoByteChars(nogc), searchLen, start);
     }
 
     args.rval().setInt32(res);
     return true;
 }
 
-bool
-js::HasSubstringAt(JSLinearString* text, JSLinearString* pat, size_t start)
-{
-    MOZ_ASSERT(start + pat->length() <= text->length());
-
-    size_t patLen = pat->length();
-
-    AutoCheckCannotGC nogc;
-    if (text->hasLatin1Chars()) {
-        const Latin1Char* textChars = text->latin1Chars(nogc) + start;
-        if (pat->hasLatin1Chars())
-            return PodEqual(textChars, pat->latin1Chars(nogc), patLen);
-
-        return EqualChars(textChars, pat->twoByteChars(nogc), patLen);
-    }
-
-    const char16_t* textChars = text->twoByteChars(nogc) + start;
-    if (pat->hasTwoByteChars())
-        return PodEqual(textChars, pat->twoByteChars(nogc), patLen);
-
-    return EqualChars(pat->latin1Chars(nogc), textChars, patLen);
-}
-
 // ES2018 draft rev de77aaeffce115deaf948ed30c7dbe4c60983c0c
 // 21.1.3.20 String.prototype.startsWith ( searchString [ , position ] )
 bool
 js::str_startsWith(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Steps 1-2.
@@ -3702,429 +3677,16 @@ js::InitStringClass(JSContext* cx, Handl
         return nullptr;
 
     if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_String, ctor, proto))
         return nullptr;
 
     return proto;
 }
 
-const char*
-js::ValueToPrintable(JSContext* cx, const Value& vArg, JSAutoByteString* bytes, bool asSource)
-{
-    RootedValue v(cx, vArg);
-    JSString* str;
-    if (asSource)
-        str = ValueToSource(cx, v);
-    else
-        str = ToString<CanGC>(cx, v);
-    if (!str)
-        return nullptr;
-    str = QuoteString(cx, str, 0);
-    if (!str)
-        return nullptr;
-    return bytes->encodeLatin1(cx, str);
-}
-
-template <AllowGC allowGC>
-JSString*
-js::ToStringSlow(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType arg)
-{
-    /* As with ToObjectSlow, callers must verify that |arg| isn't a string. */
-    MOZ_ASSERT(!arg.isString());
-
-    Value v = arg;
-    if (!v.isPrimitive()) {
-        MOZ_ASSERT(!cx->helperThread());
-        if (!allowGC)
-            return nullptr;
-        RootedValue v2(cx, v);
-        if (!ToPrimitive(cx, JSTYPE_STRING, &v2))
-            return nullptr;
-        v = v2;
-    }
-
-    JSString* str;
-    if (v.isString()) {
-        str = v.toString();
-    } else if (v.isInt32()) {
-        str = Int32ToString<allowGC>(cx, v.toInt32());
-    } else if (v.isDouble()) {
-        str = NumberToString<allowGC>(cx, v.toDouble());
-    } else if (v.isBoolean()) {
-        str = BooleanToString(cx, v.toBoolean());
-    } else if (v.isNull()) {
-        str = cx->names().null;
-    } else if (v.isSymbol()) {
-        MOZ_ASSERT(!cx->helperThread());
-        if (allowGC) {
-            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                                      JSMSG_SYMBOL_TO_STRING);
-        }
-        return nullptr;
-    } else {
-        MOZ_ASSERT(v.isUndefined());
-        str = cx->names().undefined;
-    }
-    return str;
-}
-
-template JSString*
-js::ToStringSlow<CanGC>(JSContext* cx, HandleValue arg);
-
-template JSString*
-js::ToStringSlow<NoGC>(JSContext* cx, const Value& arg);
-
-JS_PUBLIC_API(JSString*)
-js::ToStringSlow(JSContext* cx, HandleValue v)
-{
-    return ToStringSlow<CanGC>(cx, v);
-}
-
-static JSString*
-SymbolToSource(JSContext* cx, Symbol* symbol)
-{
-    RootedString desc(cx, symbol->description());
-    SymbolCode code = symbol->code();
-    if (code != SymbolCode::InSymbolRegistry && code != SymbolCode::UniqueSymbol) {
-        // Well-known symbol.
-        MOZ_ASSERT(uint32_t(code) < JS::WellKnownSymbolLimit);
-        return desc;
-    }
-
-    StringBuffer buf(cx);
-    if (code == SymbolCode::InSymbolRegistry ? !buf.append("Symbol.for(") : !buf.append("Symbol("))
-        return nullptr;
-    if (desc) {
-        desc = StringToSource(cx, desc);
-        if (!desc || !buf.append(desc))
-            return nullptr;
-    }
-    if (!buf.append(')'))
-        return nullptr;
-    return buf.finishString();
-}
-
-JSString*
-js::ValueToSource(JSContext* cx, HandleValue v)
-{
-    if (!CheckRecursionLimit(cx))
-        return nullptr;
-    assertSameCompartment(cx, v);
-
-    if (v.isUndefined())
-        return cx->names().void0;
-    if (v.isString())
-        return StringToSource(cx, v.toString());
-    if (v.isSymbol())
-        return SymbolToSource(cx, v.toSymbol());
-    if (v.isPrimitive()) {
-        /* Special case to preserve negative zero, _contra_ toString. */
-        if (v.isDouble() && IsNegativeZero(v.toDouble())) {
-            static const Latin1Char negativeZero[] = {'-', '0'};
-
-            return NewStringCopyN<CanGC>(cx, negativeZero, mozilla::ArrayLength(negativeZero));
-        }
-        return ToString<CanGC>(cx, v);
-    }
-
-    RootedValue fval(cx);
-    RootedObject obj(cx, &v.toObject());
-    if (!GetProperty(cx, obj, obj, cx->names().toSource, &fval))
-        return nullptr;
-    if (IsCallable(fval)) {
-        RootedValue v(cx);
-        if (!js::Call(cx, fval, obj, &v))
-            return nullptr;
-
-        return ToString<CanGC>(cx, v);
-    }
-
-    return ObjectToSource(cx, obj);
-}
-
-JSString*
-js::StringToSource(JSContext* cx, JSString* str)
-{
-    return QuoteString(cx, str, '"');
-}
-
-bool
-js::EqualChars(JSLinearString* str1, JSLinearString* str2)
-{
-    MOZ_ASSERT(str1->length() == str2->length());
-
-    size_t len = str1->length();
-
-    AutoCheckCannotGC nogc;
-    if (str1->hasTwoByteChars()) {
-        if (str2->hasTwoByteChars())
-            return PodEqual(str1->twoByteChars(nogc), str2->twoByteChars(nogc), len);
-
-        return EqualChars(str2->latin1Chars(nogc), str1->twoByteChars(nogc), len);
-    }
-
-    if (str2->hasLatin1Chars())
-        return PodEqual(str1->latin1Chars(nogc), str2->latin1Chars(nogc), len);
-
-    return EqualChars(str1->latin1Chars(nogc), str2->twoByteChars(nogc), len);
-}
-
-bool
-js::EqualStrings(JSContext* cx, JSString* str1, JSString* str2, bool* result)
-{
-    if (str1 == str2) {
-        *result = true;
-        return true;
-    }
-
-    size_t length1 = str1->length();
-    if (length1 != str2->length()) {
-        *result = false;
-        return true;
-    }
-
-    JSLinearString* linear1 = str1->ensureLinear(cx);
-    if (!linear1)
-        return false;
-    JSLinearString* linear2 = str2->ensureLinear(cx);
-    if (!linear2)
-        return false;
-
-    *result = EqualChars(linear1, linear2);
-    return true;
-}
-
-bool
-js::EqualStrings(JSLinearString* str1, JSLinearString* str2)
-{
-    if (str1 == str2)
-        return true;
-
-    size_t length1 = str1->length();
-    if (length1 != str2->length())
-        return false;
-
-    return EqualChars(str1, str2);
-}
-
-static int32_t
-CompareStringsImpl(JSLinearString* str1, JSLinearString* str2)
-{
-    size_t len1 = str1->length();
-    size_t len2 = str2->length();
-
-    AutoCheckCannotGC nogc;
-    if (str1->hasLatin1Chars()) {
-        const Latin1Char* chars1 = str1->latin1Chars(nogc);
-        return str2->hasLatin1Chars()
-               ? CompareChars(chars1, len1, str2->latin1Chars(nogc), len2)
-               : CompareChars(chars1, len1, str2->twoByteChars(nogc), len2);
-    }
-
-    const char16_t* chars1 = str1->twoByteChars(nogc);
-    return str2->hasLatin1Chars()
-           ? CompareChars(chars1, len1, str2->latin1Chars(nogc), len2)
-           : CompareChars(chars1, len1, str2->twoByteChars(nogc), len2);
-}
-
-int32_t
-js::CompareChars(const char16_t* s1, size_t len1, JSLinearString* s2)
-{
-    AutoCheckCannotGC nogc;
-    return s2->hasLatin1Chars()
-           ? CompareChars(s1, len1, s2->latin1Chars(nogc), s2->length())
-           : CompareChars(s1, len1, s2->twoByteChars(nogc), s2->length());
-}
-
-bool
-js::CompareStrings(JSContext* cx, JSString* str1, JSString* str2, int32_t* result)
-{
-    MOZ_ASSERT(str1);
-    MOZ_ASSERT(str2);
-
-    if (str1 == str2) {
-        *result = 0;
-        return true;
-    }
-
-    JSLinearString* linear1 = str1->ensureLinear(cx);
-    if (!linear1)
-        return false;
-
-    JSLinearString* linear2 = str2->ensureLinear(cx);
-    if (!linear2)
-        return false;
-
-    *result = CompareStringsImpl(linear1, linear2);
-    return true;
-}
-
-int32_t
-js::CompareAtoms(JSAtom* atom1, JSAtom* atom2)
-{
-    return CompareStringsImpl(atom1, atom2);
-}
-
-bool
-js::StringEqualsAscii(JSLinearString* str, const char* asciiBytes)
-{
-    size_t length = strlen(asciiBytes);
-#ifdef DEBUG
-    for (size_t i = 0; i != length; ++i)
-        MOZ_ASSERT(unsigned(asciiBytes[i]) <= 127);
-#endif
-    if (length != str->length())
-        return false;
-
-    const Latin1Char* latin1 = reinterpret_cast<const Latin1Char*>(asciiBytes);
-
-    AutoCheckCannotGC nogc;
-    return str->hasLatin1Chars()
-           ? PodEqual(latin1, str->latin1Chars(nogc), length)
-           : EqualChars(latin1, str->twoByteChars(nogc), length);
-}
-
-int32_t
-js_fputs(const char16_t* s, FILE* f)
-{
-    while (*s != 0) {
-        if (fputwc(wchar_t(*s), f) == WEOF)
-            return WEOF;
-        s++;
-    }
-    return 1;
-}
-
-UniqueChars
-js::DuplicateString(JSContext* cx, const char* s)
-{
-    size_t n = strlen(s) + 1;
-    auto ret = cx->make_pod_array<char>(n);
-    if (!ret)
-        return ret;
-    PodCopy(ret.get(), s, n);
-    return ret;
-}
-
-UniqueTwoByteChars
-js::DuplicateString(JSContext* cx, const char16_t* s)
-{
-    size_t n = js_strlen(s) + 1;
-    auto ret = cx->make_pod_array<char16_t>(n);
-    if (!ret)
-        return ret;
-    PodCopy(ret.get(), s, n);
-    return ret;
-}
-
-UniqueChars
-js::DuplicateString(const char* s)
-{
-    size_t n = strlen(s) + 1;
-    UniqueChars ret(js_pod_malloc<char>(n));
-    if (!ret)
-        return ret;
-    PodCopy(ret.get(), s, n);
-    return ret;
-}
-
-UniqueChars
-js::DuplicateString(const char* s, size_t n)
-{
-    UniqueChars ret(js_pod_malloc<char>(n + 1));
-    if (!ret)
-        return nullptr;
-    PodCopy(ret.get(), s, n);
-    ret[n] = 0;
-    return ret;
-}
-
-UniqueTwoByteChars
-js::DuplicateString(const char16_t* s)
-{
-    return DuplicateString(s, js_strlen(s));
-}
-
-UniqueTwoByteChars
-js::DuplicateString(const char16_t* s, size_t n)
-{
-    UniqueTwoByteChars ret(js_pod_malloc<char16_t>(n + 1));
-    if (!ret)
-        return nullptr;
-    PodCopy(ret.get(), s, n);
-    ret[n] = 0;
-    return ret;
-}
-
-JS_PUBLIC_API(char*)
-js_strdup(const char* s)
-{
-    return DuplicateString(s).release();
-}
-
-template <typename CharT>
-const CharT*
-js_strchr_limit(const CharT* s, char16_t c, const CharT* limit)
-{
-    while (s < limit) {
-        if (*s == c)
-            return s;
-        s++;
-    }
-    return nullptr;
-}
-
-template const Latin1Char*
-js_strchr_limit(const Latin1Char* s, char16_t c, const Latin1Char* limit);
-
-template const char16_t*
-js_strchr_limit(const char16_t* s, char16_t c, const char16_t* limit);
-
-char16_t*
-js::InflateString(JSContext* cx, const char* bytes, size_t length)
-{
-    char16_t* chars = cx->pod_malloc<char16_t>(length + 1);
-    if (!chars)
-        return nullptr;
-    CopyAndInflateChars(chars, bytes, length);
-    chars[length] = 0;
-    return chars;
-}
-
-template <typename CharT>
-bool
-js::DeflateStringToBuffer(JSContext* maybecx, const CharT* src, size_t srclen,
-                          char* dst, size_t* dstlenp)
-{
-    size_t dstlen = *dstlenp;
-    if (srclen > dstlen) {
-        for (size_t i = 0; i < dstlen; i++)
-            dst[i] = char(src[i]);
-        if (maybecx) {
-            AutoSuppressGC suppress(maybecx);
-            JS_ReportErrorNumberASCII(maybecx, GetErrorMessage, nullptr,
-                                      JSMSG_BUFFER_TOO_SMALL);
-        }
-        return false;
-    }
-    for (size_t i = 0; i < srclen; i++)
-        dst[i] = char(src[i]);
-    *dstlenp = srclen;
-    return true;
-}
-
-template bool
-js::DeflateStringToBuffer(JSContext* maybecx, const Latin1Char* src, size_t srclen,
-                          char* dst, size_t* dstlenp);
-
-template bool
-js::DeflateStringToBuffer(JSContext* maybecx, const char16_t* src, size_t srclen,
-                          char* dst, size_t* dstlenp);
-
 #define ____ false
 
 /*
  * Uri reserved chars + #:
  * - 35: #
  * - 36: $
  * - 38: &
  * - 43: +
@@ -4470,192 +4032,16 @@ js::EncodeURI(JSContext* cx, StringBuffe
         return false;
     if (result == EncodeResult::Encode_BadUri) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_URI);
         return false;
     }
     return true;
 }
 
-/*
- * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at
- * least 4 bytes long.  Return the number of UTF-8 bytes of data written.
- */
-uint32_t
-js::OneUcs4ToUtf8Char(uint8_t* utf8Buffer, uint32_t ucs4Char)
-{
-    MOZ_ASSERT(ucs4Char <= unicode::NonBMPMax);
-
-    if (ucs4Char < 0x80) {
-        utf8Buffer[0] = uint8_t(ucs4Char);
-        return 1;
-    }
-
-    uint32_t a = ucs4Char >> 11;
-    uint32_t utf8Length = 2;
-    while (a) {
-        a >>= 5;
-        utf8Length++;
-    }
-
-    MOZ_ASSERT(utf8Length <= 4);
-
-    uint32_t i = utf8Length;
-    while (--i) {
-        utf8Buffer[i] = uint8_t((ucs4Char & 0x3F) | 0x80);
-        ucs4Char >>= 6;
-    }
-
-    utf8Buffer[0] = uint8_t(0x100 - (1 << (8 - utf8Length)) + ucs4Char);
-    return utf8Length;
-}
-
-size_t
-js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, JSLinearString* str,
-                         uint32_t quote)
-{
-    size_t len = str->length();
-    AutoCheckCannotGC nogc;
-    return str->hasLatin1Chars()
-           ? PutEscapedStringImpl(buffer, bufferSize, out, str->latin1Chars(nogc), len, quote)
-           : PutEscapedStringImpl(buffer, bufferSize, out, str->twoByteChars(nogc), len, quote);
-}
-
-template <typename CharT>
-size_t
-js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const CharT* chars,
-                         size_t length, uint32_t quote)
-{
-    enum {
-        STOP, FIRST_QUOTE, LAST_QUOTE, CHARS, ESCAPE_START, ESCAPE_MORE
-    } state;
-
-    MOZ_ASSERT(quote == 0 || quote == '\'' || quote == '"');
-    MOZ_ASSERT_IF(!buffer, bufferSize == 0);
-    MOZ_ASSERT_IF(out, !buffer);
-
-    if (bufferSize == 0)
-        buffer = nullptr;
-    else
-        bufferSize--;
-
-    const CharT* charsEnd = chars + length;
-    size_t n = 0;
-    state = FIRST_QUOTE;
-    unsigned shift = 0;
-    unsigned hex = 0;
-    unsigned u = 0;
-    char c = 0;  /* to quell GCC warnings */
-
-    for (;;) {
-        switch (state) {
-          case STOP:
-            goto stop;
-          case FIRST_QUOTE:
-            state = CHARS;
-            goto do_quote;
-          case LAST_QUOTE:
-            state = STOP;
-          do_quote:
-            if (quote == 0)
-                continue;
-            c = (char)quote;
-            break;
-          case CHARS:
-            if (chars == charsEnd) {
-                state = LAST_QUOTE;
-                continue;
-            }
-            u = *chars++;
-            if (u < ' ') {
-                if (u != 0) {
-                    const char* escape = strchr(js_EscapeMap, (int)u);
-                    if (escape) {
-                        u = escape[1];
-                        goto do_escape;
-                    }
-                }
-                goto do_hex_escape;
-            }
-            if (u < 127) {
-                if (u == quote || u == '\\')
-                    goto do_escape;
-                c = (char)u;
-            } else if (u < 0x100) {
-                goto do_hex_escape;
-            } else {
-                shift = 16;
-                hex = u;
-                u = 'u';
-                goto do_escape;
-            }
-            break;
-          do_hex_escape:
-            shift = 8;
-            hex = u;
-            u = 'x';
-          do_escape:
-            c = '\\';
-            state = ESCAPE_START;
-            break;
-          case ESCAPE_START:
-            MOZ_ASSERT(' ' <= u && u < 127);
-            c = (char)u;
-            state = ESCAPE_MORE;
-            break;
-          case ESCAPE_MORE:
-            if (shift == 0) {
-                state = CHARS;
-                continue;
-            }
-            shift -= 4;
-            u = 0xF & (hex >> shift);
-            c = (char)(u + (u < 10 ? '0' : 'A' - 10));
-            break;
-        }
-        if (buffer) {
-            MOZ_ASSERT(n <= bufferSize);
-            if (n != bufferSize) {
-                buffer[n] = c;
-            } else {
-                buffer[n] = '\0';
-                buffer = nullptr;
-            }
-        } else if (out) {
-            if (!out->put(&c, 1))
-                return size_t(-1);
-        }
-        n++;
-    }
-  stop:
-    if (buffer)
-        buffer[n] = '\0';
-    return n;
-}
-
-template size_t
-js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const Latin1Char* chars,
-                         size_t length, uint32_t quote);
-
-template size_t
-js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const char* chars,
-                         size_t length, uint32_t quote);
-
-template size_t
-js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const char16_t* chars,
-                         size_t length, uint32_t quote);
-
-template size_t
-js::PutEscapedString(char* buffer, size_t bufferSize, const Latin1Char* chars, size_t length,
-                     uint32_t quote);
-
-template size_t
-js::PutEscapedString(char* buffer, size_t bufferSize, const char16_t* chars, size_t length,
-                     uint32_t quote);
-
 static bool
 FlatStringMatchHelper(JSContext* cx, HandleString str, HandleString pattern, bool* isFlat, int32_t* match)
 {
     RootedLinearString linearPattern(cx, pattern->ensureLinear(cx));
     if (!linearPattern)
         return false;
 
     static const size_t MAX_FLAT_PAT_LEN = 256;
--- a/js/src/builtin/String.h
+++ b/js/src/builtin/String.h
@@ -17,279 +17,22 @@
 #include "NamespaceImports.h"
 
 #include "gc/Rooting.h"
 #include "js/RootingAPI.h"
 #include "js/UniquePtr.h"
 #include "util/Unicode.h"
 #include "vm/Printer.h"
 
-class JSAutoByteString;
-class JSLinearString;
-
-namespace js {
-
-class StringBuffer;
-
-template <AllowGC allowGC>
-extern JSString*
-ConcatStrings(JSContext* cx,
-              typename MaybeRooted<JSString*, allowGC>::HandleType left,
-              typename MaybeRooted<JSString*, allowGC>::HandleType right);
-
-// Return s advanced past any Unicode white space characters.
-template <typename CharT>
-static inline const CharT*
-SkipSpace(const CharT* s, const CharT* end)
-{
-    MOZ_ASSERT(s <= end);
-
-    while (s < end && unicode::IsSpace(*s))
-        s++;
-
-    return s;
-}
-
-// Return less than, equal to, or greater than zero depending on whether
-// s1 is less than, equal to, or greater than s2.
-template <typename Char1, typename Char2>
-inline int32_t
-CompareChars(const Char1* s1, size_t len1, const Char2* s2, size_t len2)
-{
-    size_t n = Min(len1, len2);
-    for (size_t i = 0; i < n; i++) {
-        if (int32_t cmp = s1[i] - s2[i])
-            return cmp;
-    }
-
-    return int32_t(len1 - len2);
-}
-
-extern int32_t
-CompareChars(const char16_t* s1, size_t len1, JSLinearString* s2);
-
-}  /* namespace js */
-
-/*
- * Shorthands for ASCII (7-bit) decimal and hex conversion.
- * Manually inline isdigit and isxdigit for performance; MSVC doesn't do this for us.
- */
-#define JS7_ISDEC(c)    ((((unsigned)(c)) - '0') <= 9)
-#define JS7_ISA2F(c)    ((((((unsigned)(c)) - 'a') <= 5) || (((unsigned)(c)) - 'A') <= 5))
-#define JS7_UNDEC(c)    ((c) - '0')
-#define JS7_ISOCT(c)    ((((unsigned)(c)) - '0') <= 7)
-#define JS7_UNOCT(c)    (JS7_UNDEC(c))
-#define JS7_ISHEX(c)    ((c) < 128 && (JS7_ISDEC(c) || JS7_ISA2F(c)))
-#define JS7_UNHEX(c)    (unsigned)(JS7_ISDEC(c) ? (c) - '0' : 10 + tolower(c) - 'a')
-#define JS7_ISLET(c)    ((c) < 128 && isalpha(c))
-
-static MOZ_ALWAYS_INLINE size_t
-js_strlen(const char16_t* s)
-{
-    return std::char_traits<char16_t>::length(s);
-}
-
-template <typename CharT>
-extern const CharT*
-js_strchr_limit(const CharT* s, char16_t c, const CharT* limit);
-
-extern int32_t
-js_fputs(const char16_t* s, FILE* f);
-
 namespace js {
 
 /* Initialize the String class, returning its prototype object. */
 extern JSObject*
 InitStringClass(JSContext* cx, HandleObject obj);
 
-/*
- * Convert a value to a printable C string.
- */
-extern const char*
-ValueToPrintable(JSContext* cx, const Value&, JSAutoByteString* bytes, bool asSource = false);
-
-extern UniqueChars
-DuplicateString(JSContext* cx, const char* s);
-
-extern UniqueTwoByteChars
-DuplicateString(JSContext* cx, const char16_t* s);
-
-/*
- * These variants do not report OOMs, you must arrange for OOMs to be reported
- * yourself.
- */
-extern UniqueChars
-DuplicateString(const char* s);
-
-extern UniqueChars
-DuplicateString(const char* s, size_t n);
-
-extern UniqueTwoByteChars
-DuplicateString(const char16_t* s);
-
-extern UniqueTwoByteChars
-DuplicateString(const char16_t* s, size_t n);
-
-/*
- * Convert a non-string value to a string, returning null after reporting an
- * error, otherwise returning a new string reference.
- */
-template <AllowGC allowGC>
-extern JSString*
-ToStringSlow(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType arg);
-
-/*
- * Convert the given value to a string.  This method includes an inline
- * fast-path for the case where the value is already a string; if the value is
- * known not to be a string, use ToStringSlow instead.
- */
-template <AllowGC allowGC>
-static MOZ_ALWAYS_INLINE JSString*
-ToString(JSContext* cx, JS::HandleValue v)
-{
-    if (v.isString())
-        return v.toString();
-    return ToStringSlow<allowGC>(cx, v);
-}
-
-/*
- * This function implements E-262-3 section 9.8, toString. Convert the given
- * value to a string of characters appended to the given buffer. On error, the
- * passed buffer may have partial results appended.
- */
-inline bool
-ValueToStringBuffer(JSContext* cx, const Value& v, StringBuffer& sb);
-
-/*
- * Convert a value to its source expression, returning null after reporting
- * an error, otherwise returning a new string reference.
- */
-extern JSString*
-ValueToSource(JSContext* cx, HandleValue v);
-
-/*
- * Convert a JSString to its source expression; returns null after reporting an
- * error, otherwise returns a new string reference. No Handle needed since the
- * input is dead after the GC.
- */
-extern JSString*
-StringToSource(JSContext* cx, JSString* str);
-
-/*
- * Test if strings are equal. The caller can call the function even if str1
- * or str2 are not GC-allocated things.
- */
-extern bool
-EqualStrings(JSContext* cx, JSString* str1, JSString* str2, bool* result);
-
-/* Use the infallible method instead! */
-extern bool
-EqualStrings(JSContext* cx, JSLinearString* str1, JSLinearString* str2, bool* result) = delete;
-
-/* EqualStrings is infallible on linear strings. */
-extern bool
-EqualStrings(JSLinearString* str1, JSLinearString* str2);
-
-extern bool
-EqualChars(JSLinearString* str1, JSLinearString* str2);
-
-/*
- * Return less than, equal to, or greater than zero depending on whether
- * str1 is less than, equal to, or greater than str2.
- */
-extern bool
-CompareStrings(JSContext* cx, JSString* str1, JSString* str2, int32_t* result);
-
-/*
- * Same as CompareStrings but for atoms.  Don't use this to just test
- * for equality; use this when you need an ordering on atoms.
- */
-extern int32_t
-CompareAtoms(JSAtom* atom1, JSAtom* atom2);
-
-/*
- * Return true if the string matches the given sequence of ASCII bytes.
- */
-extern bool
-StringEqualsAscii(JSLinearString* str, const char* asciiBytes);
-
-extern int
-StringFindPattern(JSLinearString* text, JSLinearString* pat, size_t start);
-
-/* Return true if the string contains a pattern at |start|. */
-extern bool
-HasSubstringAt(JSLinearString* text, JSLinearString* pat, size_t start);
-
-template <typename Char1, typename Char2>
-inline bool
-EqualChars(const Char1* s1, const Char2* s2, size_t len);
-
-template <typename Char1>
-inline bool
-EqualChars(const Char1* s1, const Char1* s2, size_t len)
-{
-    return mozilla::PodEqual(s1, s2, len);
-}
-
-template <typename Char1, typename Char2>
-inline bool
-EqualChars(const Char1* s1, const Char2* s2, size_t len)
-{
-    for (const Char1* s1end = s1 + len; s1 < s1end; s1++, s2++) {
-        if (*s1 != *s2)
-            return false;
-    }
-    return true;
-}
-
-/*
- * Computes |str|'s substring for the range [beginInt, beginInt + lengthInt).
- * Negative, overlarge, swapped, etc. |beginInt| and |lengthInt| are forbidden
- * and constitute API misuse.
- */
-JSString*
-SubstringKernel(JSContext* cx, HandleString str, int32_t beginInt, int32_t lengthInt);
-
-/*
- * Inflate bytes in ASCII encoding to char16_t code units. Return null on error,
- * otherwise return the char16_t buffer that was malloc'ed. A null char is
- * appended.
- */
-extern char16_t*
-InflateString(JSContext* cx, const char* bytes, size_t length);
-
-/*
- * Inflate bytes to JS chars in an existing buffer. 'dst' must be large
- * enough for 'srclen' char16_t code units. The buffer is NOT null-terminated.
- */
-inline void
-CopyAndInflateChars(char16_t* dst, const char* src, size_t srclen)
-{
-    for (size_t i = 0; i < srclen; i++)
-        dst[i] = (unsigned char) src[i];
-}
-
-inline void
-CopyAndInflateChars(char16_t* dst, const JS::Latin1Char* src, size_t srclen)
-{
-    for (size_t i = 0; i < srclen; i++)
-        dst[i] = src[i];
-}
-
-/*
- * Deflate JS chars to bytes into a buffer. 'bytes' must be large enough for
- * 'length chars. The buffer is NOT null-terminated. The destination length
- * must to be initialized with the buffer size and will contain on return the
- * number of copied bytes.
- */
-template <typename CharT>
-extern bool
-DeflateStringToBuffer(JSContext* maybecx, const CharT* chars,
-                      size_t charsLength, char* bytes, size_t* length);
-
 extern bool
 str_fromCharCode(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 str_fromCharCode_one_arg(JSContext* cx, HandleValue code, MutableHandleValue rval);
 
 extern bool
 str_fromCodePoint(JSContext* cx, unsigned argc, Value* vp);
@@ -409,100 +152,16 @@ str_normalize(JSContext* cx, unsigned ar
 extern bool
 str_localeCompare(JSContext* cx, unsigned argc, Value* vp);
 
 #endif // EXPOSE_INTL_API
 
 extern bool
 str_concat(JSContext* cx, unsigned argc, Value* vp);
 
-/*
- * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at
- * least 4 bytes long.  Return the number of UTF-8 bytes of data written.
- */
-extern uint32_t
-OneUcs4ToUtf8Char(uint8_t* utf8Buffer, uint32_t ucs4Char);
-
-extern size_t
-PutEscapedStringImpl(char* buffer, size_t size, GenericPrinter* out, JSLinearString* str,
-                     uint32_t quote);
-
-template <typename CharT>
-extern size_t
-PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const CharT* chars,
-                     size_t length, uint32_t quote);
-
-/*
- * Write str into buffer escaping any non-printable or non-ASCII character
- * using \escapes for JS string literals.
- * Guarantees that a NUL is at the end of the buffer unless size is 0. Returns
- * the length of the written output, NOT including the NUL. Thus, a return
- * value of size or more means that the output was truncated. If buffer
- * is null, just returns the length of the output. If quote is not 0, it must
- * be a single or double quote character that will quote the output.
-*/
-inline size_t
-PutEscapedString(char* buffer, size_t size, JSLinearString* str, uint32_t quote)
-{
-    size_t n = PutEscapedStringImpl(buffer, size, nullptr, str, quote);
-
-    /* PutEscapedStringImpl can only fail with a file. */
-    MOZ_ASSERT(n != size_t(-1));
-    return n;
-}
-
-template <typename CharT>
-inline size_t
-PutEscapedString(char* buffer, size_t bufferSize, const CharT* chars, size_t length, uint32_t quote)
-{
-    size_t n = PutEscapedStringImpl(buffer, bufferSize, nullptr, chars, length, quote);
-
-    /* PutEscapedStringImpl can only fail with a file. */
-    MOZ_ASSERT(n != size_t(-1));
-    return n;
-}
-
-inline bool
-EscapedStringPrinter(GenericPrinter& out, JSLinearString* str, uint32_t quote)
-{
-    return PutEscapedStringImpl(nullptr, 0, &out, str, quote) != size_t(-1);
-}
-
-inline bool
-EscapedStringPrinter(GenericPrinter& out, const char* chars, size_t length, uint32_t quote)
-{
-    return PutEscapedStringImpl(nullptr, 0, &out, chars, length, quote) != size_t(-1);
-}
-
-/*
- * Write str into file escaping any non-printable or non-ASCII character.
- * If quote is not 0, it must be a single or double quote character that
- * will quote the output.
-*/
-inline bool
-FileEscapedString(FILE* fp, JSLinearString* str, uint32_t quote)
-{
-    Fprinter out(fp);
-    bool res = EscapedStringPrinter(out, str, quote);
-    out.finish();
-    return res;
-}
-
-inline bool
-FileEscapedString(FILE* fp, const char* chars, size_t length, uint32_t quote)
-{
-    Fprinter out(fp);
-    bool res = EscapedStringPrinter(out, chars, length, quote);
-    out.finish();
-    return res;
-}
-
-bool
-EncodeURI(JSContext* cx, StringBuffer& sb, const char* chars, size_t length);
-
 ArrayObject*
 str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep,
                  uint32_t limit);
 
 JSString *
 str_flat_replace_string(JSContext *cx, HandleString string, HandleString pattern,
                         HandleString replacement);
 
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -34,16 +34,17 @@
 #include "js/StructuredClone.h"
 #include "js/UbiNode.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/UbiNodeShortestPaths.h"
 #include "js/UniquePtr.h"
 #include "js/Vector.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #include "vm/Debugger.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/FloatingPoint.h"
 
 #include "jslibmath.h"
 
 #include "frontend/ParseNode.h"
 #include "frontend/Parser.h"
 #include "js/Conversions.h"
+#include "vm/StringType.h"
 
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
 
 using namespace js;
 using namespace js::frontend;
 
 using mozilla::IsNaN;
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -33,16 +33,17 @@
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpParser.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSScript.h"
 #include "vm/RegExpObject.h"
+#include "vm/StringType.h"
 #include "wasm/AsmJS.h"
 
 #include "frontend/ParseContext-inl.h"
 #include "frontend/ParseNode-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/JSAtom-inl.h"
 #include "vm/JSScript-inl.h"
 
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -15,16 +15,17 @@
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdio.h>
 
 #include "jsutil.h"
 
 #include "gc/GC.h"
 #include "gc/Memory.h"
+#include "util/Text.h"
 #include "vm/Debugger.h"
 #include "vm/HelperThreads.h"
 #include "vm/Runtime.h"
 #include "vm/Time.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::gcstats;
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -10,16 +10,17 @@
 
 #include "jsutil.h"
 #include "NamespaceImports.h"
 
 #include "gc/GCInternals.h"
 #include "gc/Marking.h"
 #include "gc/PublicIterators.h"
 #include "gc/Zone.h"
+#include "util/Text.h"
 #include "vm/JSFunction.h"
 #include "vm/JSScript.h"
 #include "vm/Shape.h"
 #include "vm/SymbolType.h"
 
 #include "gc/GC-inl.h"
 #include "vm/JSCompartment-inl.h"
 #include "vm/ObjectGroup-inl.h"
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -44,16 +44,17 @@
 #include "jit/SharedICHelpers.h"
 #include "jit/StackSlotAllocator.h"
 #include "util/Unicode.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #include "vm/MatchPairs.h"
 #include "vm/RegExpObject.h"
 #include "vm/RegExpStatics.h"
+#include "vm/StringType.h"
 #include "vm/TraceLogging.h"
 #include "vm/TypedArrayObject.h"
 #include "vtune/VTuneWrapper.h"
 
 #include "jsboolinlines.h"
 
 #include "jit/MacroAssembler-inl.h"
 #include "jit/shared/CodeGenerator-shared-inl.h"
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -20,16 +20,17 @@
 #include "builtin/String.h"
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineInspector.h"
 #include "jit/IonBuilder.h"
 #include "jit/JitSpewer.h"
 #include "jit/MIRGraph.h"
 #include "jit/RangeAnalysis.h"
 #include "js/Conversions.h"
+#include "util/Text.h"
 
 #include "jsboolinlines.h"
 
 #include "vm/JSAtom-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
--- a/js/src/jit/OptimizationTracking.cpp
+++ b/js/src/jit/OptimizationTracking.cpp
@@ -6,16 +6,17 @@
 
 #include "jit/OptimizationTracking.h"
 
 #include "ds/Sort.h"
 #include "jit/IonBuilder.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitSpewer.h"
 #include "js/TrackedOptimizationInfo.h"
+#include "util/Text.h"
 
 #include "vm/ObjectGroup-inl.h"
 #include "vm/TypeInference-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::Maybe;
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -20,16 +20,17 @@
 #include "jit/JitSpewer.h"
 #include "jit/Linker.h"
 #include "jit/SharedICHelpers.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/VMFunctions.h"
 #include "vm/Interpreter.h"
+#include "vm/StringType.h"
 
 #include "jit/MacroAssembler-inl.h"
 #include "jit/SharedICHelpers-inl.h"
 #include "vm/Interpreter-inl.h"
 
 using mozilla::BitwiseCast;
 
 namespace js {
--- a/js/src/jsapi-tests/testSharedImmutableStringsCache.cpp
+++ b/js/src/jsapi-tests/testSharedImmutableStringsCache.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/IntegerRange.h"
 
 #include "js/Vector.h"
 #include "jsapi-tests/tests.h"
 #include "threading/Thread.h"
+#include "util/Text.h"
 #include "vm/SharedImmutableStringsCache.h"
 
 const int NUM_THREADS = 256;
 const int NUM_ITERATIONS = 256;
 
 const int NUM_STRINGS = 4;
 const char16_t *const STRINGS[NUM_STRINGS] = {
     u"uno",
--- a/js/src/jsapi-tests/testUbiNode.cpp
+++ b/js/src/jsapi-tests/testUbiNode.cpp
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "builtin/TestingFunctions.h"
 #include "js/UbiNode.h"
 #include "js/UbiNodeDominatorTree.h"
 #include "js/UbiNodePostOrder.h"
 #include "js/UbiNodeShortestPaths.h"
 #include "jsapi-tests/tests.h"
+#include "util/Text.h"
 #include "vm/SavedFrame.h"
 
 using JS::RootedObject;
 using JS::RootedScript;
 using JS::RootedString;
 using namespace js;
 
 // A helper JS::ubi::Node concrete implementation that can be used to make mock
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -62,16 +62,17 @@
 #include "js/Date.h"
 #include "js/Initialization.h"
 #include "js/Proxy.h"
 #include "js/SliceBudget.h"
 #include "js/StructuredClone.h"
 #include "js/Utility.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #include "vm/DateObject.h"
 #include "vm/Debugger.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/ErrorObject.h"
 #include "vm/HelperThreads.h"
 #include "vm/Interpreter.h"
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -20,16 +20,17 @@
 #include "jsutil.h"
 
 #include "ds/Sort.h"
 #include "gc/Heap.h"
 #include "jit/InlinableNatives.h"
 #include "js/Class.h"
 #include "js/Conversions.h"
 #include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
 #include "vm/SelfHosting.h"
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -27,16 +27,17 @@
 #include "vm/ErrorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/SavedStacks.h"
 #include "vm/SelfHosting.h"
+#include "vm/StringType.h"
 
 #include "vm/ErrorObject-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/SavedStacks-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -302,16 +302,17 @@ UNIFIED_SOURCES += [
     'proxy/SecurityWrapper.cpp',
     'proxy/Wrapper.cpp',
     'threading/Mutex.cpp',
     'threading/ProtectedData.cpp',
     'util/AllocPolicy.cpp',
     'util/NativeStack.cpp',
     'util/Printf.cpp',
     'util/StringBuffer.cpp',
+    'util/Text.cpp',
     'util/Unicode.cpp',
     'vm/ArgumentsObject.cpp',
     'vm/ArrayBufferObject.cpp',
     'vm/AsyncFunction.cpp',
     'vm/AsyncIteration.cpp',
     'vm/BytecodeUtil.cpp',
     'vm/Caches.cpp',
     'vm/CallNonGenericMethod.cpp',
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -24,16 +24,17 @@
 #include "jsfriendapi.h"
 
 #include "builtin/String.h"
 #include "gc/FreeOp.h"
 #include "js/Conversions.h"
 #include "js/Wrapper.h"
 #include "shell/jsshell.h"
 #include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "util/Windows.h"
 #include "vm/JSObject.h"
 #include "vm/TypedArrayObject.h"
 
 #include "vm/JSObject-inl.h"
 
 #ifdef XP_WIN
 # ifndef PATH_MAX
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -86,16 +86,17 @@
 #include "shell/jsoptparse.h"
 #include "shell/jsshell.h"
 #include "shell/OSObject.h"
 #include "threading/ConditionVariable.h"
 #include "threading/ExclusiveData.h"
 #include "threading/LockGuard.h"
 #include "threading/Thread.h"
 #include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "util/Windows.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Compression.h"
 #include "vm/Debugger.h"
 #include "vm/HelperThreads.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
new file mode 100644
--- /dev/null
+++ b/js/src/util/Text.cpp
@@ -0,0 +1,332 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "util/Text.h"
+
+#include "mozilla/PodOperations.h"
+
+#include "gc/GC.h"
+#include "js/GCAPI.h"
+#include "vm/JSContext.h"
+#include "vm/StringType.h"
+
+using namespace JS;
+using namespace js;
+using js::gc::AutoSuppressGC;
+using mozilla::PodCopy;
+
+template <typename CharT>
+const CharT*
+js_strchr_limit(const CharT* s, char16_t c, const CharT* limit)
+{
+    while (s < limit) {
+        if (*s == c)
+            return s;
+        s++;
+    }
+    return nullptr;
+}
+
+template const Latin1Char*
+js_strchr_limit(const Latin1Char* s, char16_t c, const Latin1Char* limit);
+
+template const char16_t*
+js_strchr_limit(const char16_t* s, char16_t c, const char16_t* limit);
+
+JS_PUBLIC_API(char*)
+js_strdup(const char* s)
+{
+    return DuplicateString(s).release();
+}
+
+int32_t
+js_fputs(const char16_t* s, FILE* f)
+{
+    while (*s != 0) {
+        if (fputwc(wchar_t(*s), f) == WEOF)
+            return WEOF;
+        s++;
+    }
+    return 1;
+}
+
+UniqueChars
+js::DuplicateString(JSContext* cx, const char* s)
+{
+    size_t n = strlen(s) + 1;
+    auto ret = cx->make_pod_array<char>(n);
+    if (!ret)
+        return ret;
+    PodCopy(ret.get(), s, n);
+    return ret;
+}
+
+UniqueTwoByteChars
+js::DuplicateString(JSContext* cx, const char16_t* s)
+{
+    size_t n = js_strlen(s) + 1;
+    auto ret = cx->make_pod_array<char16_t>(n);
+    if (!ret)
+        return ret;
+    PodCopy(ret.get(), s, n);
+    return ret;
+}
+
+UniqueChars
+js::DuplicateString(const char* s)
+{
+    size_t n = strlen(s) + 1;
+    UniqueChars ret(js_pod_malloc<char>(n));
+    if (!ret)
+        return ret;
+    PodCopy(ret.get(), s, n);
+    return ret;
+}
+
+UniqueChars
+js::DuplicateString(const char* s, size_t n)
+{
+    UniqueChars ret(js_pod_malloc<char>(n + 1));
+    if (!ret)
+        return nullptr;
+    PodCopy(ret.get(), s, n);
+    ret[n] = 0;
+    return ret;
+}
+
+UniqueTwoByteChars
+js::DuplicateString(const char16_t* s)
+{
+    return DuplicateString(s, js_strlen(s));
+}
+
+UniqueTwoByteChars
+js::DuplicateString(const char16_t* s, size_t n)
+{
+    UniqueTwoByteChars ret(js_pod_malloc<char16_t>(n + 1));
+    if (!ret)
+        return nullptr;
+    PodCopy(ret.get(), s, n);
+    ret[n] = 0;
+    return ret;
+}
+
+char16_t*
+js::InflateString(JSContext* cx, const char* bytes, size_t length)
+{
+    char16_t* chars = cx->pod_malloc<char16_t>(length + 1);
+    if (!chars)
+        return nullptr;
+    CopyAndInflateChars(chars, bytes, length);
+    chars[length] = 0;
+    return chars;
+}
+
+template <typename CharT>
+bool
+js::DeflateStringToBuffer(JSContext* maybecx, const CharT* src, size_t srclen,
+                          char* dst, size_t* dstlenp)
+{
+    size_t dstlen = *dstlenp;
+    if (srclen > dstlen) {
+        for (size_t i = 0; i < dstlen; i++)
+            dst[i] = char(src[i]);
+        if (maybecx) {
+            AutoSuppressGC suppress(maybecx);
+            JS_ReportErrorNumberASCII(maybecx, GetErrorMessage, nullptr,
+                                      JSMSG_BUFFER_TOO_SMALL);
+        }
+        return false;
+    }
+    for (size_t i = 0; i < srclen; i++)
+        dst[i] = char(src[i]);
+    *dstlenp = srclen;
+    return true;
+}
+
+template bool
+js::DeflateStringToBuffer(JSContext* maybecx, const Latin1Char* src, size_t srclen,
+                          char* dst, size_t* dstlenp);
+
+template bool
+js::DeflateStringToBuffer(JSContext* maybecx, const char16_t* src, size_t srclen,
+                          char* dst, size_t* dstlenp);
+
+/*
+ * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at
+ * least 4 bytes long.  Return the number of UTF-8 bytes of data written.
+ */
+uint32_t
+js::OneUcs4ToUtf8Char(uint8_t* utf8Buffer, uint32_t ucs4Char)
+{
+    MOZ_ASSERT(ucs4Char <= unicode::NonBMPMax);
+
+    if (ucs4Char < 0x80) {
+        utf8Buffer[0] = uint8_t(ucs4Char);
+        return 1;
+    }
+
+    uint32_t a = ucs4Char >> 11;
+    uint32_t utf8Length = 2;
+    while (a) {
+        a >>= 5;
+        utf8Length++;
+    }
+
+    MOZ_ASSERT(utf8Length <= 4);
+
+    uint32_t i = utf8Length;
+    while (--i) {
+        utf8Buffer[i] = uint8_t((ucs4Char & 0x3F) | 0x80);
+        ucs4Char >>= 6;
+    }
+
+    utf8Buffer[0] = uint8_t(0x100 - (1 << (8 - utf8Length)) + ucs4Char);
+    return utf8Length;
+}
+
+size_t
+js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, JSLinearString* str,
+                         uint32_t quote)
+{
+    size_t len = str->length();
+    AutoCheckCannotGC nogc;
+    return str->hasLatin1Chars()
+           ? PutEscapedStringImpl(buffer, bufferSize, out, str->latin1Chars(nogc), len, quote)
+           : PutEscapedStringImpl(buffer, bufferSize, out, str->twoByteChars(nogc), len, quote);
+}
+
+template <typename CharT>
+size_t
+js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const CharT* chars,
+                         size_t length, uint32_t quote)
+{
+    enum {
+        STOP, FIRST_QUOTE, LAST_QUOTE, CHARS, ESCAPE_START, ESCAPE_MORE
+    } state;
+
+    MOZ_ASSERT(quote == 0 || quote == '\'' || quote == '"');
+    MOZ_ASSERT_IF(!buffer, bufferSize == 0);
+    MOZ_ASSERT_IF(out, !buffer);
+
+    if (bufferSize == 0)
+        buffer = nullptr;
+    else
+        bufferSize--;
+
+    const CharT* charsEnd = chars + length;
+    size_t n = 0;
+    state = FIRST_QUOTE;
+    unsigned shift = 0;
+    unsigned hex = 0;
+    unsigned u = 0;
+    char c = 0;  /* to quell GCC warnings */
+
+    for (;;) {
+        switch (state) {
+          case STOP:
+            goto stop;
+          case FIRST_QUOTE:
+            state = CHARS;
+            goto do_quote;
+          case LAST_QUOTE:
+            state = STOP;
+          do_quote:
+            if (quote == 0)
+                continue;
+            c = (char)quote;
+            break;
+          case CHARS:
+            if (chars == charsEnd) {
+                state = LAST_QUOTE;
+                continue;
+            }
+            u = *chars++;
+            if (u < ' ') {
+                if (u != 0) {
+                    const char* escape = strchr(js_EscapeMap, (int)u);
+                    if (escape) {
+                        u = escape[1];
+                        goto do_escape;
+                    }
+                }
+                goto do_hex_escape;
+            }
+            if (u < 127) {
+                if (u == quote || u == '\\')
+                    goto do_escape;
+                c = (char)u;
+            } else if (u < 0x100) {
+                goto do_hex_escape;
+            } else {
+                shift = 16;
+                hex = u;
+                u = 'u';
+                goto do_escape;
+            }
+            break;
+          do_hex_escape:
+            shift = 8;
+            hex = u;
+            u = 'x';
+          do_escape:
+            c = '\\';
+            state = ESCAPE_START;
+            break;
+          case ESCAPE_START:
+            MOZ_ASSERT(' ' <= u && u < 127);
+            c = (char)u;
+            state = ESCAPE_MORE;
+            break;
+          case ESCAPE_MORE:
+            if (shift == 0) {
+                state = CHARS;
+                continue;
+            }
+            shift -= 4;
+            u = 0xF & (hex >> shift);
+            c = (char)(u + (u < 10 ? '0' : 'A' - 10));
+            break;
+        }
+        if (buffer) {
+            MOZ_ASSERT(n <= bufferSize);
+            if (n != bufferSize) {
+                buffer[n] = c;
+            } else {
+                buffer[n] = '\0';
+                buffer = nullptr;
+            }
+        } else if (out) {
+            if (!out->put(&c, 1))
+                return size_t(-1);
+        }
+        n++;
+    }
+  stop:
+    if (buffer)
+        buffer[n] = '\0';
+    return n;
+}
+
+template size_t
+js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const Latin1Char* chars,
+                         size_t length, uint32_t quote);
+
+template size_t
+js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const char* chars,
+                         size_t length, uint32_t quote);
+
+template size_t
+js::PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const char16_t* chars,
+                         size_t length, uint32_t quote);
+
+template size_t
+js::PutEscapedString(char* buffer, size_t bufferSize, const Latin1Char* chars, size_t length,
+                     uint32_t quote);
+
+template size_t
+js::PutEscapedString(char* buffer, size_t bufferSize, const char16_t* chars, size_t length,
+                     uint32_t quote);
new file mode 100644
--- /dev/null
+++ b/js/src/util/Text.h
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef util_Text_h
+#define util_Text_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+
+#include <ctype.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string>
+
+#include "jsutil.h"
+#include "NamespaceImports.h"
+
+#include "js/Utility.h"
+#include "util/Unicode.h"
+#include "vm/Printer.h"
+
+class JSLinearString;
+
+/*
+ * Shorthands for ASCII (7-bit) decimal and hex conversion.
+ * Manually inline isdigit and isxdigit for performance; MSVC doesn't do this for us.
+ */
+#define JS7_ISDEC(c)    ((((unsigned)(c)) - '0') <= 9)
+#define JS7_ISA2F(c)    ((((((unsigned)(c)) - 'a') <= 5) || (((unsigned)(c)) - 'A') <= 5))
+#define JS7_UNDEC(c)    ((c) - '0')
+#define JS7_ISOCT(c)    ((((unsigned)(c)) - '0') <= 7)
+#define JS7_UNOCT(c)    (JS7_UNDEC(c))
+#define JS7_ISHEX(c)    ((c) < 128 && (JS7_ISDEC(c) || JS7_ISA2F(c)))
+#define JS7_UNHEX(c)    (unsigned)(JS7_ISDEC(c) ? (c) - '0' : 10 + tolower(c) - 'a')
+#define JS7_ISLET(c)    ((c) < 128 && isalpha(c))
+
+static MOZ_ALWAYS_INLINE size_t
+js_strlen(const char16_t* s)
+{
+    return std::char_traits<char16_t>::length(s);
+}
+
+template <typename CharT>
+extern const CharT*
+js_strchr_limit(const CharT* s, char16_t c, const CharT* limit);
+
+extern int32_t
+js_fputs(const char16_t* s, FILE* f);
+
+namespace js {
+
+class StringBuffer;
+
+template <typename Char1, typename Char2>
+inline bool
+EqualChars(const Char1* s1, const Char2* s2, size_t len);
+
+template <typename Char1>
+inline bool
+EqualChars(const Char1* s1, const Char1* s2, size_t len)
+{
+    return mozilla::PodEqual(s1, s2, len);
+}
+
+template <typename Char1, typename Char2>
+inline bool
+EqualChars(const Char1* s1, const Char2* s2, size_t len)
+{
+    for (const Char1* s1end = s1 + len; s1 < s1end; s1++, s2++) {
+        if (*s1 != *s2)
+            return false;
+    }
+    return true;
+}
+
+// Return less than, equal to, or greater than zero depending on whether
+// s1 is less than, equal to, or greater than s2.
+template <typename Char1, typename Char2>
+inline int32_t
+CompareChars(const Char1* s1, size_t len1, const Char2* s2, size_t len2)
+{
+    size_t n = Min(len1, len2);
+    for (size_t i = 0; i < n; i++) {
+        if (int32_t cmp = s1[i] - s2[i])
+            return cmp;
+    }
+
+    return int32_t(len1 - len2);
+}
+
+// Return s advanced past any Unicode white space characters.
+template <typename CharT>
+static inline const CharT*
+SkipSpace(const CharT* s, const CharT* end)
+{
+    MOZ_ASSERT(s <= end);
+
+    while (s < end && unicode::IsSpace(*s))
+        s++;
+
+    return s;
+}
+
+extern UniqueChars
+DuplicateString(JSContext* cx, const char* s);
+
+extern UniqueTwoByteChars
+DuplicateString(JSContext* cx, const char16_t* s);
+
+/*
+ * These variants do not report OOMs, you must arrange for OOMs to be reported
+ * yourself.
+ */
+extern UniqueChars
+DuplicateString(const char* s);
+
+extern UniqueChars
+DuplicateString(const char* s, size_t n);
+
+extern UniqueTwoByteChars
+DuplicateString(const char16_t* s);
+
+extern UniqueTwoByteChars
+DuplicateString(const char16_t* s, size_t n);
+
+/*
+ * Inflate bytes in ASCII encoding to char16_t code units. Return null on error,
+ * otherwise return the char16_t buffer that was malloc'ed. A null char is
+ * appended.
+ */
+extern char16_t*
+InflateString(JSContext* cx, const char* bytes, size_t length);
+
+/*
+ * Inflate bytes to JS chars in an existing buffer. 'dst' must be large
+ * enough for 'srclen' char16_t code units. The buffer is NOT null-terminated.
+ */
+inline void
+CopyAndInflateChars(char16_t* dst, const char* src, size_t srclen)
+{
+    for (size_t i = 0; i < srclen; i++)
+        dst[i] = (unsigned char) src[i];
+}
+
+inline void
+CopyAndInflateChars(char16_t* dst, const JS::Latin1Char* src, size_t srclen)
+{
+    for (size_t i = 0; i < srclen; i++)
+        dst[i] = src[i];
+}
+
+/*
+ * Deflate JS chars to bytes into a buffer. 'bytes' must be large enough for
+ * 'length chars. The buffer is NOT null-terminated. The destination length
+ * must to be initialized with the buffer size and will contain on return the
+ * number of copied bytes.
+ */
+template <typename CharT>
+extern bool
+DeflateStringToBuffer(JSContext* maybecx, const CharT* chars,
+                      size_t charsLength, char* bytes, size_t* length);
+
+/*
+ * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at
+ * least 4 bytes long.  Return the number of UTF-8 bytes of data written.
+ */
+extern uint32_t
+OneUcs4ToUtf8Char(uint8_t* utf8Buffer, uint32_t ucs4Char);
+
+extern size_t
+PutEscapedStringImpl(char* buffer, size_t size, GenericPrinter* out, JSLinearString* str,
+                     uint32_t quote);
+
+template <typename CharT>
+extern size_t
+PutEscapedStringImpl(char* buffer, size_t bufferSize, GenericPrinter* out, const CharT* chars,
+                     size_t length, uint32_t quote);
+
+/*
+ * Write str into buffer escaping any non-printable or non-ASCII character
+ * using \escapes for JS string literals.
+ * Guarantees that a NUL is at the end of the buffer unless size is 0. Returns
+ * the length of the written output, NOT including the NUL. Thus, a return
+ * value of size or more means that the output was truncated. If buffer
+ * is null, just returns the length of the output. If quote is not 0, it must
+ * be a single or double quote character that will quote the output.
+*/
+inline size_t
+PutEscapedString(char* buffer, size_t size, JSLinearString* str, uint32_t quote)
+{
+    size_t n = PutEscapedStringImpl(buffer, size, nullptr, str, quote);
+
+    /* PutEscapedStringImpl can only fail with a file. */
+    MOZ_ASSERT(n != size_t(-1));
+    return n;
+}
+
+template <typename CharT>
+inline size_t
+PutEscapedString(char* buffer, size_t bufferSize, const CharT* chars, size_t length, uint32_t quote)
+{
+    size_t n = PutEscapedStringImpl(buffer, bufferSize, nullptr, chars, length, quote);
+
+    /* PutEscapedStringImpl can only fail with a file. */
+    MOZ_ASSERT(n != size_t(-1));
+    return n;
+}
+
+inline bool
+EscapedStringPrinter(GenericPrinter& out, JSLinearString* str, uint32_t quote)
+{
+    return PutEscapedStringImpl(nullptr, 0, &out, str, quote) != size_t(-1);
+}
+
+inline bool
+EscapedStringPrinter(GenericPrinter& out, const char* chars, size_t length, uint32_t quote)
+{
+    return PutEscapedStringImpl(nullptr, 0, &out, chars, length, quote) != size_t(-1);
+}
+
+/*
+ * Write str into file escaping any non-printable or non-ASCII character.
+ * If quote is not 0, it must be a single or double quote character that
+ * will quote the output.
+*/
+inline bool
+FileEscapedString(FILE* fp, JSLinearString* str, uint32_t quote)
+{
+    Fprinter out(fp);
+    bool res = EscapedStringPrinter(out, str, quote);
+    out.finish();
+    return res;
+}
+
+inline bool
+FileEscapedString(FILE* fp, const char* chars, size_t length, uint32_t quote)
+{
+    Fprinter out(fp);
+    bool res = EscapedStringPrinter(out, chars, length, quote);
+    out.finish();
+    return res;
+}
+
+bool
+EncodeURI(JSContext* cx, StringBuffer& sb, const char* chars, size_t length);
+
+} // namespace js
+
+#endif // util_Text_h
--- a/js/src/vm/CodeCoverage.cpp
+++ b/js/src/vm/CodeCoverage.cpp
@@ -13,16 +13,17 @@
 #include <stdio.h>
 #ifdef XP_WIN
 # include <process.h>
 # define getpid _getpid
 #else
 # include <unistd.h>
 #endif
 
+#include "util/Text.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSScript.h"
 #include "vm/Runtime.h"
 #include "vm/Time.h"
 
 // This file contains a few functions which are used to produce files understood
 // by lcov tools. A detailed description of the format is available in the man
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -39,16 +39,17 @@
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/Opcodes.h"
 #include "vm/Scope.h"
 #include "vm/Shape.h"
 #include "vm/Stopwatch.h"
+#include "vm/StringType.h"
 #include "vm/TraceLogging.h"
 
 #include "jsboolinlines.h"
 
 #include "jit/JitFrames-inl.h"
 #include "vm/Debugger-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/GeckoProfiler-inl.h"
--- a/js/src/vm/JSAtom.cpp
+++ b/js/src/vm/JSAtom.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/Unused.h"
 
 #include <string.h>
 
 #include "jstypes.h"
 
 #include "builtin/String.h"
 #include "gc/Marking.h"
+#include "util/Text.h"
 #include "vm/JSContext.h"
 #include "vm/SymbolType.h"
 #include "vm/Xdr.h"
 
 #include "gc/AtomMarking-inl.h"
 #include "vm/JSCompartment-inl.h"
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -32,16 +32,17 @@
 #include "frontend/BytecodeCompiler.h"
 #include "gc/Policy.h"
 #include "jit/BaselineJIT.h"
 #include "js/MemoryMetrics.h"
 #include "js/Proxy.h"
 #include "js/UbiNode.h"
 #include "js/UniquePtr.h"
 #include "js/Wrapper.h"
+#include "util/Text.h"
 #include "util/Windows.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -32,16 +32,17 @@
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonCode.h"
 #include "js/MemoryMetrics.h"
 #include "js/Printf.h"
 #include "js/Utility.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/Compression.h"
 #include "vm/Debugger.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -19,16 +19,17 @@
 #include "gc/HashUtil.h"
 #ifdef DEBUG
 #include "irregexp/RegExpBytecode.h"
 #endif
 #include "irregexp/RegExpParser.h"
 #include "util/StringBuffer.h"
 #include "vm/MatchPairs.h"
 #include "vm/RegExpStatics.h"
+#include "vm/StringType.h"
 #include "vm/TraceLogging.h"
 #ifdef DEBUG
 #include "util/Unicode.h"
 #endif
 #include "vm/Xdr.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/PodOperations.h"
 
 #include "gc/FreeOp.h"
 #include "gc/HashUtil.h"
 #include "gc/Policy.h"
 #include "gc/PublicIterators.h"
 #include "js/HashTable.h"
+#include "util/Text.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 
 #include "vm/Caches-inl.h"
 #include "vm/JSCompartment-inl.h"
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
--- a/js/src/vm/SharedImmutableStringsCache.cpp
+++ b/js/src/vm/SharedImmutableStringsCache.cpp
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/SharedImmutableStringsCache-inl.h"
 
 #include "builtin/String.h"
+#include "util/Text.h"
 
 namespace js {
 
 SharedImmutableString::SharedImmutableString(
     ExclusiveData<SharedImmutableStringsCache::Inner>::Guard& locked,
     SharedImmutableStringsCache::StringBox* box)
   : cache_(locked)
   , box_(box)
--- a/js/src/vm/StringType.cpp
+++ b/js/src/vm/StringType.cpp
@@ -1,34 +1,38 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/StringType-inl.h"
 
+#include "mozilla/FloatingPoint.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/RangedPtr.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Unused.h"
 
 #include "gc/Marking.h"
 #include "gc/Nursery.h"
 #include "js/UbiNode.h"
+#include "util/StringBuffer.h"
 #include "vm/GeckoProfiler.h"
 
 #include "vm/GeckoProfiler-inl.h"
 #include "vm/JSCompartment-inl.h"
 #include "vm/JSContext-inl.h"
+#include "vm/JSObject-inl.h"
 
 using namespace js;
 
+using mozilla::IsNegativeZero;
 using mozilla::IsSame;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::RangedPtr;
 using mozilla::RoundUpPow2;
 using mozilla::Unused;
 
 using JS::AutoCheckCannotGC;
@@ -751,16 +755,175 @@ JSDependentString::dumpRepresentation(js
     if (mozilla::Maybe<size_t> offset = baseOffset())
         out.printf("%*soffset: %zu\n", indent, "", *offset);
 
     out.printf("%*sbase: ", indent, "");
     base()->dumpRepresentation(out, indent);
 }
 #endif
 
+bool
+js::EqualChars(JSLinearString* str1, JSLinearString* str2)
+{
+    MOZ_ASSERT(str1->length() == str2->length());
+
+    size_t len = str1->length();
+
+    AutoCheckCannotGC nogc;
+    if (str1->hasTwoByteChars()) {
+        if (str2->hasTwoByteChars())
+            return PodEqual(str1->twoByteChars(nogc), str2->twoByteChars(nogc), len);
+
+        return EqualChars(str2->latin1Chars(nogc), str1->twoByteChars(nogc), len);
+    }
+
+    if (str2->hasLatin1Chars())
+        return PodEqual(str1->latin1Chars(nogc), str2->latin1Chars(nogc), len);
+
+    return EqualChars(str1->latin1Chars(nogc), str2->twoByteChars(nogc), len);
+}
+
+bool
+js::HasSubstringAt(JSLinearString* text, JSLinearString* pat, size_t start)
+{
+    MOZ_ASSERT(start + pat->length() <= text->length());
+
+    size_t patLen = pat->length();
+
+    AutoCheckCannotGC nogc;
+    if (text->hasLatin1Chars()) {
+        const Latin1Char* textChars = text->latin1Chars(nogc) + start;
+        if (pat->hasLatin1Chars())
+            return PodEqual(textChars, pat->latin1Chars(nogc), patLen);
+
+        return EqualChars(textChars, pat->twoByteChars(nogc), patLen);
+    }
+
+    const char16_t* textChars = text->twoByteChars(nogc) + start;
+    if (pat->hasTwoByteChars())
+        return PodEqual(textChars, pat->twoByteChars(nogc), patLen);
+
+    return EqualChars(pat->latin1Chars(nogc), textChars, patLen);
+}
+
+bool
+js::EqualStrings(JSContext* cx, JSString* str1, JSString* str2, bool* result)
+{
+    if (str1 == str2) {
+        *result = true;
+        return true;
+    }
+
+    size_t length1 = str1->length();
+    if (length1 != str2->length()) {
+        *result = false;
+        return true;
+    }
+
+    JSLinearString* linear1 = str1->ensureLinear(cx);
+    if (!linear1)
+        return false;
+    JSLinearString* linear2 = str2->ensureLinear(cx);
+    if (!linear2)
+        return false;
+
+    *result = EqualChars(linear1, linear2);
+    return true;
+}
+
+bool
+js::EqualStrings(JSLinearString* str1, JSLinearString* str2)
+{
+    if (str1 == str2)
+        return true;
+
+    size_t length1 = str1->length();
+    if (length1 != str2->length())
+        return false;
+
+    return EqualChars(str1, str2);
+}
+
+int32_t
+js::CompareChars(const char16_t* s1, size_t len1, JSLinearString* s2)
+{
+    AutoCheckCannotGC nogc;
+    return s2->hasLatin1Chars()
+           ? CompareChars(s1, len1, s2->latin1Chars(nogc), s2->length())
+           : CompareChars(s1, len1, s2->twoByteChars(nogc), s2->length());
+}
+
+static int32_t
+CompareStringsImpl(JSLinearString* str1, JSLinearString* str2)
+{
+    size_t len1 = str1->length();
+    size_t len2 = str2->length();
+
+    AutoCheckCannotGC nogc;
+    if (str1->hasLatin1Chars()) {
+        const Latin1Char* chars1 = str1->latin1Chars(nogc);
+        return str2->hasLatin1Chars()
+               ? CompareChars(chars1, len1, str2->latin1Chars(nogc), len2)
+               : CompareChars(chars1, len1, str2->twoByteChars(nogc), len2);
+    }
+
+    const char16_t* chars1 = str1->twoByteChars(nogc);
+    return str2->hasLatin1Chars()
+           ? CompareChars(chars1, len1, str2->latin1Chars(nogc), len2)
+           : CompareChars(chars1, len1, str2->twoByteChars(nogc), len2);
+}
+
+bool
+js::CompareStrings(JSContext* cx, JSString* str1, JSString* str2, int32_t* result)
+{
+    MOZ_ASSERT(str1);
+    MOZ_ASSERT(str2);
+
+    if (str1 == str2) {
+        *result = 0;
+        return true;
+    }
+
+    JSLinearString* linear1 = str1->ensureLinear(cx);
+    if (!linear1)
+        return false;
+
+    JSLinearString* linear2 = str2->ensureLinear(cx);
+    if (!linear2)
+        return false;
+
+    *result = CompareStringsImpl(linear1, linear2);
+    return true;
+}
+
+int32_t
+js::CompareAtoms(JSAtom* atom1, JSAtom* atom2)
+{
+    return CompareStringsImpl(atom1, atom2);
+}
+
+bool
+js::StringEqualsAscii(JSLinearString* str, const char* asciiBytes)
+{
+    size_t length = strlen(asciiBytes);
+#ifdef DEBUG
+    for (size_t i = 0; i != length; ++i)
+        MOZ_ASSERT(unsigned(asciiBytes[i]) <= 127);
+#endif
+    if (length != str->length())
+        return false;
+
+    const Latin1Char* latin1 = reinterpret_cast<const Latin1Char*>(asciiBytes);
+
+    AutoCheckCannotGC nogc;
+    return str->hasLatin1Chars()
+           ? PodEqual(latin1, str->latin1Chars(nogc), length)
+           : EqualChars(latin1, str->twoByteChars(nogc), length);
+}
+
 template <typename CharT>
 /* static */ bool
 JSFlatString::isIndexSlow(const CharT* s, size_t length, uint32_t* indexp)
 {
     CharT ch = *s;
 
     if (!JS7_ISDEC(ch))
         return false;
@@ -1727,8 +1890,151 @@ JSString::fillWithRepresentatives(JSCont
                                  CheckLatin1))
     {
         return false;
     }
 
     MOZ_ASSERT(index == 22);
     return true;
 }
+
+
+/*** Conversions *********************************************************************************/
+
+const char*
+js::ValueToPrintable(JSContext* cx, const Value& vArg, JSAutoByteString* bytes, bool asSource)
+{
+    RootedValue v(cx, vArg);
+    JSString* str;
+    if (asSource)
+        str = ValueToSource(cx, v);
+    else
+        str = ToString<CanGC>(cx, v);
+    if (!str)
+        return nullptr;
+    str = QuoteString(cx, str, 0);
+    if (!str)
+        return nullptr;
+    return bytes->encodeLatin1(cx, str);
+}
+
+template <AllowGC allowGC>
+JSString*
+js::ToStringSlow(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType arg)
+{
+    /* As with ToObjectSlow, callers must verify that |arg| isn't a string. */
+    MOZ_ASSERT(!arg.isString());
+
+    Value v = arg;
+    if (!v.isPrimitive()) {
+        MOZ_ASSERT(!cx->helperThread());
+        if (!allowGC)
+            return nullptr;
+        RootedValue v2(cx, v);
+        if (!ToPrimitive(cx, JSTYPE_STRING, &v2))
+            return nullptr;
+        v = v2;
+    }
+
+    JSString* str;
+    if (v.isString()) {
+        str = v.toString();
+    } else if (v.isInt32()) {
+        str = Int32ToString<allowGC>(cx, v.toInt32());
+    } else if (v.isDouble()) {
+        str = NumberToString<allowGC>(cx, v.toDouble());
+    } else if (v.isBoolean()) {
+        str = BooleanToString(cx, v.toBoolean());
+    } else if (v.isNull()) {
+        str = cx->names().null;
+    } else if (v.isSymbol()) {
+        MOZ_ASSERT(!cx->helperThread());
+        if (allowGC) {
+            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                                      JSMSG_SYMBOL_TO_STRING);
+        }
+        return nullptr;
+    } else {
+        MOZ_ASSERT(v.isUndefined());
+        str = cx->names().undefined;
+    }
+    return str;
+}
+
+template JSString*
+js::ToStringSlow<CanGC>(JSContext* cx, HandleValue arg);
+
+template JSString*
+js::ToStringSlow<NoGC>(JSContext* cx, const Value& arg);
+
+JS_PUBLIC_API(JSString*)
+js::ToStringSlow(JSContext* cx, HandleValue v)
+{
+    return ToStringSlow<CanGC>(cx, v);
+}
+
+static JSString*
+SymbolToSource(JSContext* cx, Symbol* symbol)
+{
+    RootedString desc(cx, symbol->description());
+    SymbolCode code = symbol->code();
+    if (code != SymbolCode::InSymbolRegistry && code != SymbolCode::UniqueSymbol) {
+        // Well-known symbol.
+        MOZ_ASSERT(uint32_t(code) < JS::WellKnownSymbolLimit);
+        return desc;
+    }
+
+    StringBuffer buf(cx);
+    if (code == SymbolCode::InSymbolRegistry ? !buf.append("Symbol.for(") : !buf.append("Symbol("))
+        return nullptr;
+    if (desc) {
+        desc = StringToSource(cx, desc);
+        if (!desc || !buf.append(desc))
+            return nullptr;
+    }
+    if (!buf.append(')'))
+        return nullptr;
+    return buf.finishString();
+}
+
+JSString*
+js::ValueToSource(JSContext* cx, HandleValue v)
+{
+    if (!CheckRecursionLimit(cx))
+        return nullptr;
+    assertSameCompartment(cx, v);
+
+    if (v.isUndefined())
+        return cx->names().void0;
+    if (v.isString())
+        return StringToSource(cx, v.toString());
+    if (v.isSymbol())
+        return SymbolToSource(cx, v.toSymbol());
+    if (v.isPrimitive()) {
+        /* Special case to preserve negative zero, _contra_ toString. */
+        if (v.isDouble() && IsNegativeZero(v.toDouble())) {
+            static const Latin1Char negativeZero[] = {'-', '0'};
+
+            return NewStringCopyN<CanGC>(cx, negativeZero, mozilla::ArrayLength(negativeZero));
+        }
+        return ToString<CanGC>(cx, v);
+    }
+
+    RootedValue fval(cx);
+    RootedObject obj(cx, &v.toObject());
+    if (!GetProperty(cx, obj, obj, cx->names().toSource, &fval))
+        return nullptr;
+    if (IsCallable(fval)) {
+        RootedValue v(cx);
+        if (!js::Call(cx, fval, obj, &v))
+            return nullptr;
+
+        return ToString<CanGC>(cx, v);
+    }
+
+    return ObjectToSource(cx, obj);
+}
+
+JSString*
+js::StringToSource(JSContext* cx, JSString* str)
+{
+    return QuoteString(cx, str, '"');
+}
--- a/js/src/vm/StringType.h
+++ b/js/src/vm/StringType.h
@@ -17,17 +17,17 @@
 #include "builtin/String.h"
 #include "gc/Barrier.h"
 #include "gc/Cell.h"
 #include "gc/Heap.h"
 #include "gc/Nursery.h"
 #include "gc/Rooting.h"
 #include "js/CharacterEncoding.h"
 #include "js/RootingAPI.h"
-
+#include "util/Text.h"
 #include "vm/Printer.h"
 
 class JSDependentString;
 class JSExtensibleString;
 class JSExternalString;
 class JSInlineString;
 class JSRope;
 
@@ -1486,16 +1486,147 @@ NewStringCopyUTF8Z(JSContext* cx, const 
 }
 
 JSString*
 NewMaybeExternalString(JSContext* cx, const char16_t* s, size_t n, const JSStringFinalizer* fin,
                        bool* allocatedExternal);
 
 JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
 
+template <AllowGC allowGC>
+extern JSString*
+ConcatStrings(JSContext* cx,
+              typename MaybeRooted<JSString*, allowGC>::HandleType left,
+              typename MaybeRooted<JSString*, allowGC>::HandleType right);
+
+/*
+ * Test if strings are equal. The caller can call the function even if str1
+ * or str2 are not GC-allocated things.
+ */
+extern bool
+EqualStrings(JSContext* cx, JSString* str1, JSString* str2, bool* result);
+
+/* Use the infallible method instead! */
+extern bool
+EqualStrings(JSContext* cx, JSLinearString* str1, JSLinearString* str2, bool* result) = delete;
+
+/* EqualStrings is infallible on linear strings. */
+extern bool
+EqualStrings(JSLinearString* str1, JSLinearString* str2);
+
+/**
+ * Compare two strings that are known to be the same length.
+ * Exposed for the JITs; for ordinary uses, EqualStrings() is more sensible.
+ *
+ * Precondition: str1->length() == str2->length().
+ */
+extern bool
+EqualChars(JSLinearString* str1, JSLinearString* str2);
+
+/*
+ * Return less than, equal to, or greater than zero depending on whether
+ * `s1[0..len1]` is less than, equal to, or greater than `s2`.
+ */
+extern int32_t
+CompareChars(const char16_t* s1, size_t len1, JSLinearString* s2);
+
+/*
+ * Compare two strings, like CompareChars, but store the result in `*result`.
+ * This flattens the strings and therefore can fail.
+ */
+extern bool
+CompareStrings(JSContext* cx, JSString* str1, JSString* str2, int32_t* result);
+
+/*
+ * Same as CompareStrings but for atoms.  Don't use this to just test
+ * for equality; use this when you need an ordering on atoms.
+ */
+extern int32_t
+CompareAtoms(JSAtom* atom1, JSAtom* atom2);
+
+/*
+ * Return true if the string matches the given sequence of ASCII bytes.
+ */
+extern bool
+StringEqualsAscii(JSLinearString* str, const char* asciiBytes);
+
+extern int
+StringFindPattern(JSLinearString* text, JSLinearString* pat, size_t start);
+
+/**
+ * Return true if the string contains a pattern at |start|.
+ *
+ * Precondition: `text` is long enough that this might be true;
+ * that is, it has at least `start + pat->length()` characters.
+ */
+extern bool
+HasSubstringAt(JSLinearString* text, JSLinearString* pat, size_t start);
+
+/*
+ * Computes |str|'s substring for the range [beginInt, beginInt + lengthInt).
+ * Negative, overlarge, swapped, etc. |beginInt| and |lengthInt| are forbidden
+ * and constitute API misuse.
+ */
+JSString*
+SubstringKernel(JSContext* cx, HandleString str, int32_t beginInt, int32_t lengthInt);
+
+
+/*** Conversions *********************************************************************************/
+
+/*
+ * Convert a value to a printable C string.
+ */
+extern const char*
+ValueToPrintable(JSContext* cx, const Value&, JSAutoByteString* bytes, bool asSource = false);
+
+/*
+ * Convert a non-string value to a string, returning null after reporting an
+ * error, otherwise returning a new string reference.
+ */
+template <AllowGC allowGC>
+extern JSString*
+ToStringSlow(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType arg);
+
+/*
+ * Convert the given value to a string.  This method includes an inline
+ * fast-path for the case where the value is already a string; if the value is
+ * known not to be a string, use ToStringSlow instead.
+ */
+template <AllowGC allowGC>
+static MOZ_ALWAYS_INLINE JSString*
+ToString(JSContext* cx, JS::HandleValue v)
+{
+    if (v.isString())
+        return v.toString();
+    return ToStringSlow<allowGC>(cx, v);
+}
+
+/*
+ * This function implements E-262-3 section 9.8, toString. Convert the given
+ * value to a string of characters appended to the given buffer. On error, the
+ * passed buffer may have partial results appended.
+ */
+inline bool
+ValueToStringBuffer(JSContext* cx, const Value& v, StringBuffer& sb);
+
+/*
+ * Convert a value to its source expression, returning null after reporting
+ * an error, otherwise returning a new string reference.
+ */
+extern JSString*
+ValueToSource(JSContext* cx, HandleValue v);
+
+/*
+ * Convert a JSString to its source expression; returns null after reporting an
+ * error, otherwise returns a new string reference. No Handle needed since the
+ * input is dead after the GC.
+ */
+extern JSString*
+StringToSource(JSContext* cx, JSString* str);
+
 } /* namespace js */
 
 // Addon IDs are interned atoms which are never destroyed. This detail is
 // not exposed outside the API.
 class JSAddonId : public JSAtom
 {};
 
 MOZ_ALWAYS_INLINE bool
--- a/js/src/vm/TraceLoggingGraph.cpp
+++ b/js/src/vm/TraceLoggingGraph.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ScopeExit.h"
 
 #include "builtin/String.h"
 
 #include "js/UniquePtr.h"
 #include "threading/LockGuard.h"
 #include "threading/Thread.h"
+#include "util/Text.h"
 #include "vm/TraceLogging.h"
 
 #ifndef DEFAULT_TRACE_LOG_DIR
 # if defined(_WIN32)
 #  define DEFAULT_TRACE_LOG_DIR "."
 # else
 #  define DEFAULT_TRACE_LOG_DIR "/tmp/"
 # endif
--- a/js/src/vm/TraceLoggingTypes.cpp
+++ b/js/src/vm/TraceLoggingTypes.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/TraceLoggingTypes.h"
 
+#include "vm/StringType.h"
+
 class JSLinearString;
 
 uint32_t
 TLStringToTextId(JSLinearString* str)
 {
 #define NAME(textId) if (js::StringEqualsAscii(str, #textId)) return TraceLogger_ ## textId;
     TRACELOGGER_TREE_ITEMS(NAME)
     TRACELOGGER_LOG_ITEMS(NAME)
--- a/js/src/vm/UbiNode.cpp
+++ b/js/src/vm/UbiNode.cpp
@@ -16,16 +16,17 @@
 #include "builtin/String.h"
 
 #include "jit/IonCode.h"
 #include "js/Debug.h"
 #include "js/TracingAPI.h"
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 #include "js/Vector.h"
+#include "util/Text.h"
 #include "vm/Debugger.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/Scope.h"
 #include "vm/Shape.h"
--- a/js/src/vm/UbiNodeCensus.cpp
+++ b/js/src/vm/UbiNodeCensus.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "js/UbiNodeCensus.h"
 
+#include "util/Text.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSContext.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
--- a/js/src/vm/UbiNodeShortestPaths.cpp
+++ b/js/src/vm/UbiNodeShortestPaths.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "js/UbiNodeShortestPaths.h"
 
 #include "mozilla/Maybe.h"
 #include "mozilla/Move.h"
 
 #include "builtin/String.h"
+#include "util/Text.h"
 
 namespace JS {
 namespace ubi {
 
 JS_PUBLIC_API(BackEdge::Ptr)
 BackEdge::clone() const
 {
     BackEdge::Ptr clone(js_new<BackEdge>());
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -30,16 +30,17 @@
 #include "builtin/String.h"
 #include "frontend/Parser.h"
 #include "gc/Policy.h"
 #include "jit/AtomicOperations.h"
 #include "js/MemoryMetrics.h"
 #include "js/Printf.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "vm/ErrorReporting.h"
 #include "vm/SelfHosting.h"
 #include "vm/Time.h"
 #include "vm/TypedArrayObject.h"
 #include "wasm/WasmCompile.h"
 #include "wasm/WasmGenerator.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmIonCompile.h"
--- a/js/src/wasm/WasmCompile.cpp
+++ b/js/src/wasm/WasmCompile.cpp
@@ -17,16 +17,17 @@
  */
 
 #include "wasm/WasmCompile.h"
 
 #include "mozilla/Maybe.h"
 #include "mozilla/Unused.h"
 
 #include "jit/ProcessExecutableMemory.h"
+#include "util/Text.h"
 #include "wasm/WasmBaselineCompile.h"
 #include "wasm/WasmBinaryIterator.h"
 #include "wasm/WasmGenerator.h"
 #include "wasm/WasmIonCompile.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmValidate.h"
 
 using namespace js;
--- a/js/src/wasm/WasmDebug.cpp
+++ b/js/src/wasm/WasmDebug.cpp
@@ -20,16 +20,17 @@
 
 #include "mozilla/BinarySearch.h"
 
 #include "ds/Sort.h"
 #include "gc/FreeOp.h"
 #include "jit/ExecutableAllocator.h"
 #include "jit/MacroAssembler.h"
 #include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "vm/Debugger.h"
 #include "wasm/WasmBinaryToText.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmValidate.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -19,16 +19,17 @@
 #include "wasm/WasmGenerator.h"
 
 #include "mozilla/CheckedInt.h"
 #include "mozilla/EnumeratedRange.h"
 #include "mozilla/SHA1.h"
 
 #include <algorithm>
 
+#include "util/Text.h"
 #include "wasm/WasmBaselineCompile.h"
 #include "wasm/WasmCompile.h"
 #include "wasm/WasmIonCompile.h"
 #include "wasm/WasmStubs.h"
 
 #include "jit/MacroAssembler-inl.h"
 
 using namespace js;
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -24,16 +24,17 @@
 #include "mozilla/RangedPtr.h"
 
 #include "builtin/Promise.h"
 #include "gc/FreeOp.h"
 #include "jit/AtomicOperations.h"
 #include "jit/JitOptions.h"
 #include "js/Printf.h"
 #include "util/StringBuffer.h"
+#include "util/Text.h"
 #include "vm/Interpreter.h"
 #include "vm/StringType.h"
 #include "wasm/WasmBaselineCompile.h"
 #include "wasm/WasmCompile.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmIonCompile.h"
 #include "wasm/WasmModule.h"
 #include "wasm/WasmSignalHandlers.h"