Bug 1052579 - Move StringBuffer::finishString() and update all usage sites r=sfink
authorChris Martin <cmartin@mozilla.com>
Wed, 10 Apr 2019 16:42:17 +0000
changeset 468829 9ad896485f8948c03866e0cbdd3ed48b878f5b2e
parent 468828 53c0f17ba52cf0d0841d25320e06c386f78435a7
child 468832 2e24668fe392b448a6a9f001bf9195cf29726498
push id112755
push userdvarga@mozilla.com
push dateWed, 10 Apr 2019 22:06:41 +0000
treeherdermozilla-inbound@606f85641d0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1052579
milestone68.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 1052579 - Move StringBuffer::finishString() and update all usage sites r=sfink Any code that calls StringBuffer::finishString() is creating a JSFlatString from a StringBuffer's backing buffer. That backing buffer should always be allocated on the new String Arena heap. This new class will ensure that this behavior is statically enforced by the compiler. Differential Revision: https://phabricator.services.mozilla.com/D25707
js/src/builtin/Array.cpp
js/src/builtin/Boolean.cpp
js/src/builtin/JSON.cpp
js/src/builtin/Object.cpp
js/src/builtin/RegExp.cpp
js/src/builtin/String.cpp
js/src/jsapi-tests/testErrorInterceptor.cpp
js/src/jsdate.cpp
js/src/jsexn.cpp
js/src/jsnum.cpp
js/src/shell/OSObject.cpp
js/src/shell/jsshell.cpp
js/src/util/StringBuffer.cpp
js/src/util/StringBuffer.h
js/src/vm/Interpreter.cpp
js/src/vm/JSFunction.cpp
js/src/vm/JSONParser.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/SavedStacks.cpp
js/src/vm/StringType.cpp
js/src/vm/SymbolType.cpp
js/src/wasm/AsmJS.cpp
js/src/wasm/WasmInstance.cpp
js/src/wasm/WasmJS.cpp
--- a/js/src/builtin/Array.cpp
+++ b/js/src/builtin/Array.cpp
@@ -1183,17 +1183,17 @@ static bool array_toSource(JSContext* cx
   Rooted<JSObject*> obj(cx, &args.thisv().toObject());
   RootedValue elt(cx);
 
   AutoCycleDetector detector(cx, obj);
   if (!detector.init()) {
     return false;
   }
 
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
 
   if (detector.foundCycle()) {
     if (!sb.append("[]")) {
       return false;
     }
     goto make_string;
   }
 
@@ -1443,17 +1443,17 @@ bool js::array_join(JSContext* cx, unsig
       if (elem0.isString()) {
         args.rval().set(elem0);
         return true;
       }
     }
   }
 
   // Step 5.
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
   if (sepstr->hasTwoByteChars() && !sb.ensureTwoByteChars()) {
     return false;
   }
 
   // The separator will be added |length - 1| times, reserve space for that
   // so that we don't have to unnecessarily grow the buffer.
   size_t seplen = sepstr->length();
   if (seplen > 0) {
--- a/js/src/builtin/Boolean.cpp
+++ b/js/src/builtin/Boolean.cpp
@@ -37,17 +37,17 @@ MOZ_ALWAYS_INLINE bool IsBoolean(HandleV
 
 MOZ_ALWAYS_INLINE bool bool_toSource_impl(JSContext* cx, const CallArgs& args) {
   HandleValue thisv = args.thisv();
   MOZ_ASSERT(IsBoolean(thisv));
 
   bool b = thisv.isBoolean() ? thisv.toBoolean()
                              : thisv.toObject().as<BooleanObject>().unbox();
 
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
   if (!sb.append("(new Boolean(") || !BooleanToStringBuffer(b, sb) ||
       !sb.append("))")) {
     return false;
   }
 
   JSString* str = sb.finishString();
   if (!str) {
     return false;
--- a/js/src/builtin/JSON.cpp
+++ b/js/src/builtin/JSON.cpp
@@ -1070,17 +1070,17 @@ static bool json_parse(JSContext* cx, un
 bool json_stringify(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   RootedObject replacer(cx,
                         args.get(1).isObject() ? &args[1].toObject() : nullptr);
   RootedValue value(cx, args.get(0));
   RootedValue space(cx, args.get(2));
 
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
   if (!Stringify(cx, &value, replacer, space, sb, StringifyBehavior::Normal)) {
     return false;
   }
 
   // XXX This can never happen to nsJSON.cpp, but the JSON object
   // needs to support returning undefined. So this is a little awkward
   // for the API, because we want to support streaming writers.
   if (!sb.empty()) {
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -232,17 +232,17 @@ JSString* js::ObjectToSource(JSContext* 
   AutoCycleDetector detector(cx, obj);
   if (!detector.init()) {
     return nullptr;
   }
   if (detector.foundCycle()) {
     return NewStringCopyZ<CanGC>(cx, "{}");
   }
 
-  StringBuffer buf(cx);
+  JSStringBuilder buf(cx);
   if (outermost && !buf.append('(')) {
     return nullptr;
   }
   if (!buf.append('{')) {
     return nullptr;
   }
 
   RootedIdVector idv(cx);
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -1497,17 +1497,17 @@ bool js::RegExpGetSubstitution(JSContext
 
   // Step 11.
   size_t reserveLength;
   if (!FindReplaceLength(cx, matched, string, position, tailPos, captures,
                          replacement, firstDollarIndex, &reserveLength)) {
     return false;
   }
 
-  StringBuffer result(cx);
+  JSStringBuilder result(cx);
   if (NeedTwoBytes(string, replacement, matched, captures)) {
     if (!result.ensureTwoByteChars()) {
       return false;
     }
   }
 
   if (!result.reserve(reserveLength)) {
     return false;
--- a/js/src/builtin/String.cpp
+++ b/js/src/builtin/String.cpp
@@ -319,17 +319,17 @@ static bool str_unescape(JSContext* cx, 
 
   // Step 1.
   RootedLinearString str(cx, ArgToLinearString(cx, args, 0));
   if (!str) {
     return false;
   }
 
   // Step 3.
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
   if (str->hasTwoByteChars() && !sb.ensureTwoByteChars()) {
     return false;
   }
 
   // Steps 2, 4-5.
   if (str->hasLatin1Chars()) {
     AutoCheckCannotGC nogc;
     if (!Unescape(sb, str->latin1Range(nogc))) {
@@ -493,17 +493,17 @@ MOZ_ALWAYS_INLINE bool str_toSource_impl
     return false;
   }
 
   UniqueChars quoted = QuoteString(cx, str, '"');
   if (!quoted) {
     return false;
   }
 
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
   if (!sb.append("(new String(") ||
       !sb.append(quoted.get(), strlen(quoted.get())) || !sb.append("))")) {
     return false;
   }
 
   JSString* result = sb.finishString();
   if (!result) {
     return false;
@@ -2811,17 +2811,17 @@ static JSLinearString* InterpretDollarRe
 
   /*
    * Most probably:
    *
    *      len(newstr) >= len(orig) - len(match) + len(replacement)
    *
    * Note that dollar vars _could_ make the resulting text smaller than this.
    */
-  StringBuffer newReplaceChars(cx);
+  JSStringBuilder newReplaceChars(cx);
   if (repstr->hasTwoByteChars() && !newReplaceChars.ensureTwoByteChars()) {
     return nullptr;
   }
 
   if (!newReplaceChars.reserve(textstr->length() - patternLength +
                                repstr->length())) {
     return nullptr;
   }
@@ -2931,17 +2931,17 @@ JSString* js::StringFlatReplaceString(JS
     return nullptr;
   }
 
   RootedLinearString linearStr(cx, string->ensureLinear(cx));
   if (!linearStr) {
     return nullptr;
   }
 
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
   if (linearStr->hasTwoByteChars()) {
     if (!sb.ensureTwoByteChars()) {
       return nullptr;
     }
     if (linearRepl->hasTwoByteChars()) {
       if (!StrFlatReplaceGlobal<char16_t, char16_t>(cx, linearStr, linearPat,
                                                     linearRepl, sb)) {
         return nullptr;
@@ -3796,17 +3796,17 @@ static const bool js_isUriUnescaped[] = 
 /* 10 */ true, true, true, true, true, true, true, true, true, true,
 /* 11 */ true, true, true, true, true, true, true, true, true, true,
 /* 12 */ true, true, true, ____, ____, ____, true, ____
     // clang-format on
 };
 
 #undef ____
 
-static inline bool TransferBufferToString(StringBuffer& sb, JSString* str,
+static inline bool TransferBufferToString(JSStringBuilder& sb, JSString* str,
                                           MutableHandleValue rval) {
   if (!sb.empty()) {
     str = sb.finishString();
     if (!str) {
       return false;
     }
   }
   rval.setString(str);
@@ -3924,17 +3924,17 @@ static MOZ_ALWAYS_INLINE bool Encode(JSC
                                      const bool* unescapedSet,
                                      MutableHandleValue rval) {
   size_t length = str->length();
   if (length == 0) {
     rval.setString(cx->runtime()->emptyString);
     return true;
   }
 
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
 
   EncodeResult res;
   if (str->hasLatin1Chars()) {
     AutoCheckCannotGC nogc;
     res = Encode(sb, str->latin1Chars(nogc), str->length(), unescapedSet);
   } else {
     AutoCheckCannotGC nogc;
     res = Encode(sb, str->twoByteChars(nogc), str->length(), unescapedSet);
@@ -4069,17 +4069,17 @@ static DecodeResult Decode(StringBuffer&
 static bool Decode(JSContext* cx, HandleLinearString str,
                    const bool* reservedSet, MutableHandleValue rval) {
   size_t length = str->length();
   if (length == 0) {
     rval.setString(cx->runtime()->emptyString);
     return true;
   }
 
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
 
   DecodeResult res;
   if (str->hasLatin1Chars()) {
     AutoCheckCannotGC nogc;
     res = Decode(sb, str->latin1Chars(nogc), str->length(), reservedSet);
   } else {
     AutoCheckCannotGC nogc;
     res = Decode(sb, str->twoByteChars(nogc), str->length(), reservedSet);
@@ -4134,17 +4134,17 @@ static bool str_encodeURI_Component(JSCo
   if (!str) {
     return false;
   }
 
   return Encode(cx, str, nullptr, args.rval());
 }
 
 JSString* js::EncodeURI(JSContext* cx, const char* chars, size_t length) {
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
   EncodeResult result = Encode(sb, reinterpret_cast<const Latin1Char*>(chars),
                                length, js_isUriReservedPlusPound);
   if (result == EncodeResult::Encode_Failure) {
     return nullptr;
   }
   if (result == EncodeResult::Encode_BadUri) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_URI);
     return nullptr;
--- a/js/src/jsapi-tests/testErrorInterceptor.cpp
+++ b/js/src/jsapi-tests/testErrorInterceptor.cpp
@@ -7,17 +7,17 @@
 // Tests for JS_GetErrorInterceptorCallback and JS_SetErrorInterceptorCallback.
 
 namespace {
 static JS::PersistentRootedString gLatestMessage;
 
 // An interceptor that stores the error in `gLatestMessage`.
 struct SimpleInterceptor : JSErrorInterceptor {
   virtual void interceptError(JSContext* cx, JS::HandleValue val) override {
-    js::StringBuffer buffer(cx);
+    js::JSStringBuilder buffer(cx);
     if (!ValueToStringBuffer(cx, val, buffer)) {
       MOZ_CRASH("Could not convert to string buffer");
     }
     gLatestMessage = buffer.finishString();
     if (!gLatestMessage) {
       MOZ_CRASH("Could not convert to string");
     }
   }
@@ -98,17 +98,17 @@ BEGIN_TEST(testErrorInterceptor) {
     CHECK(gLatestMessage != nullptr);
     CHECK(js::StringEqualsAscii(&gLatestMessage->asLinear(), TO_STRING[i]));
 
     // Check the final error.
     JS::RootedValue exn(cx);
     CHECK(JS_GetPendingException(cx, &exn));
     JS_ClearPendingException(cx);
 
-    js::StringBuffer buffer(cx);
+    js::JSStringBuilder buffer(cx);
     CHECK(ValueToStringBuffer(cx, exn, buffer));
     JS::Rooted<JSFlatString*> flat(cx, buffer.finishString());
     CHECK(equalStrings(cx, flat, gLatestMessage));
 
     // Cleanup.
     gLatestMessage = nullptr;
   }
 
@@ -123,17 +123,17 @@ BEGIN_TEST(testErrorInterceptor) {
     // Check that the callback wasn't called.
     CHECK(gLatestMessage == nullptr);
 
     // Check the final error.
     JS::RootedValue exn(cx);
     CHECK(JS_GetPendingException(cx, &exn));
     JS_ClearPendingException(cx);
 
-    js::StringBuffer buffer(cx);
+    js::JSStringBuilder buffer(cx);
     CHECK(ValueToStringBuffer(cx, exn, buffer));
     JS::Rooted<JSFlatString*> flat(cx, buffer.finishString());
     CHECK(js::StringEqualsAscii(flat, TO_STRING[i]));
 
     // Cleanup.
     gLatestMessage = nullptr;
   }
 
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -2998,17 +2998,17 @@ MOZ_ALWAYS_INLINE bool date_toDateString
 }
 
 static bool date_toDateString(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   return CallNonGenericMethod<IsDate, date_toDateString_impl>(cx, args);
 }
 
 MOZ_ALWAYS_INLINE bool date_toSource_impl(JSContext* cx, const CallArgs& args) {
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
   if (!sb.append("(new Date(") ||
       !NumberValueToStringBuffer(
           cx, args.thisv().toObject().as<DateObject>().UTCTime(), sb) ||
       !sb.append("))")) {
     return false;
   }
 
   JSString* str = sb.finishString();
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -500,17 +500,17 @@ static bool exn_toSource(JSContext* cx, 
 
   RootedValue linenoVal(cx);
   uint32_t lineno;
   if (!GetProperty(cx, obj, obj, cx->names().lineNumber, &linenoVal) ||
       !ToUint32(cx, linenoVal, &lineno)) {
     return false;
   }
 
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
   if (!sb.append("(new ") || !sb.append(name) || !sb.append("(")) {
     return false;
   }
 
   if (!sb.append(message)) {
     return false;
   }
 
@@ -1052,17 +1052,17 @@ const char* js::ValueToSourceForError(JS
 
   AutoClearPendingException acpe(cx);
 
   RootedString str(cx, JS_ValueToSource(cx, val));
   if (!str) {
     return "<<error converting value to string>>";
   }
 
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
   if (val.isObject()) {
     RootedObject valObj(cx, val.toObjectOrNull());
     ESClass cls;
     if (!GetBuiltinClass(cx, valObj, &cls)) {
       return "<<error determining class of value>>";
     }
     const char* s;
     if (cls == ESClass::Array) {
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -581,17 +581,17 @@ static inline double Extract(const Value
     return v.toNumber();
   }
   return v.toObject().as<NumberObject>().unbox();
 }
 
 MOZ_ALWAYS_INLINE bool num_toSource_impl(JSContext* cx, const CallArgs& args) {
   double d = Extract(args.thisv());
 
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
   if (!sb.append("(new Number(") ||
       !NumberValueToStringBuffer(cx, NumberValue(d), sb) || !sb.append("))")) {
     return false;
   }
 
   JSString* str = sb.finishString();
   if (!str) {
     return false;
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -669,17 +669,17 @@ static bool ospath_join(JSContext* cx, u
     JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr,
                               JSSMSG_INVALID_ARGS, "join");
     return false;
   }
 
   // This function doesn't take into account some aspects of Windows paths,
   // e.g. the drive letter is always reset when an absolute path is appended.
 
-  StringBuffer buffer(cx);
+  JSStringBuilder buffer(cx);
 
   for (unsigned i = 0; i < args.length(); i++) {
     if (!args[i].isString()) {
       JS_ReportErrorASCII(cx, "join expects string arguments only");
       return false;
     }
 
     UniqueChars path = JS_EncodeStringToLatin1(cx, args[i].toString());
--- a/js/src/shell/jsshell.cpp
+++ b/js/src/shell/jsshell.cpp
@@ -33,17 +33,17 @@ namespace shell {
 // strings, if they have them, else with just their names.
 //
 bool GenerateInterfaceHelp(JSContext* cx, HandleObject obj, const char* name) {
   RootedIdVector idv(cx);
   if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &idv)) {
     return false;
   }
 
-  StringBuffer buf(cx);
+  JSStringBuilder buf(cx);
   int numEntries = 0;
   for (size_t i = 0; i < idv.length(); i++) {
     RootedId id(cx, idv[i]);
     RootedValue v(cx);
     if (!JS_GetPropertyById(cx, obj, id, &v)) {
       return false;
     }
     if (!v.isObject()) {
--- a/js/src/util/StringBuffer.cpp
+++ b/js/src/util/StringBuffer.cpp
@@ -98,17 +98,17 @@ JSFlatString* StringBuffer::finishString
    * The allocation was made on a TempAllocPolicy, so account for the string
    * data on the string's zone.
    */
   cx->updateMallocCounter(sizeof(CharT) * len);
 
   return str;
 }
 
-JSFlatString* StringBuffer::finishString() {
+JSFlatString* JSStringBuilder::finishString() {
   size_t len = length();
   if (len == 0) {
     return cx->names().empty;
   }
 
   if (!JSString::validateLength(cx, len)) {
     return nullptr;
   }
--- a/js/src/util/StringBuffer.h
+++ b/js/src/util/StringBuffer.h
@@ -23,16 +23,17 @@ namespace js {
  * Any operation which would exceed the maximum string length causes an
  * exception report on the context and results in a failed return value.
  *
  * Well-sized extractions (which waste no more than 1/4 of their char
  * buffer space) are guaranteed for strings built by this interface.
  * See |extractWellSized|.
  */
 class StringBuffer {
+ protected:
   template <typename CharT>
   using BufferType = Vector<CharT, 64 / sizeof(CharT)>;
 
   /*
    * The Vector's buffer may be either stolen or copied, so we need to use
    * TempAllocPolicy and account for the memory manually when stealing.
    */
   using Latin1CharBuffer = BufferType<Latin1Char>;
@@ -259,33 +260,39 @@ class StringBuffer {
   const char16_t* rawTwoByteBegin() const { return begin<char16_t>(); }
   const char16_t* rawTwoByteEnd() const { return end<char16_t>(); }
 
   Latin1Char* rawLatin1Begin() { return begin<Latin1Char>(); }
   Latin1Char* rawLatin1End() { return end<Latin1Char>(); }
   const Latin1Char* rawLatin1Begin() const { return begin<Latin1Char>(); }
   const Latin1Char* rawLatin1End() const { return end<Latin1Char>(); }
 
-  /*
-   * Creates a string from the characters in this buffer, then (regardless
-   * whether string creation succeeded or failed) empties the buffer.
-   */
-  JSFlatString* finishString();
-
   /* Identical to finishString() except that an atom is created. */
   JSAtom* finishAtom();
 
   /*
    * Creates a raw string from the characters in this buffer.  The string is
    * exactly the characters in this buffer (inflated to TwoByte), it is *not*
    * null-terminated unless the last appended character was '\0'.
    */
   char16_t* stealChars();
 };
 
+class JSStringBuilder : public StringBuffer {
+ public:
+  explicit JSStringBuilder(JSContext* cx)
+      : StringBuffer(cx) {}
+
+  /*
+   * Creates a string from the characters in this buffer, then (regardless
+   * whether string creation succeeded or failed) empties the buffer.
+   */
+  JSFlatString* finishString();
+};
+
 inline bool StringBuffer::append(const char16_t* begin, const char16_t* end) {
   MOZ_ASSERT(begin <= end);
   if (isLatin1()) {
     while (true) {
       if (begin >= end) {
         return true;
       }
       if (*begin > JSString::MAX_LATIN1_CHAR) {
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1612,17 +1612,17 @@ class ReservedRooted : public RootedBase
 
 void js::ReportInNotObjectError(JSContext* cx, HandleValue lref, int lindex,
                                 HandleValue rref, int rindex) {
   auto uniqueCharsFromString = [](JSContext* cx,
                                   HandleValue ref) -> UniqueChars {
     static const size_t MaxStringLength = 16;
     RootedString str(cx, ref.toString());
     if (str->length() > MaxStringLength) {
-      StringBuffer buf(cx);
+      JSStringBuilder buf(cx);
       if (!buf.appendSubstring(str, 0, MaxStringLength)) {
         return nullptr;
       }
       if (!buf.append("...")) {
         return nullptr;
       }
       str = buf.finishString();
       if (!str) {
--- a/js/src/vm/JSFunction.cpp
+++ b/js/src/vm/JSFunction.cpp
@@ -889,17 +889,17 @@ JSString* js::FunctionToString(JSContext
     if (!str) {
       return nullptr;
     }
 
     cache.put(script, str);
     return str;
   }
 
-  StringBuffer out(cx);
+  JSStringBuilder out(cx);
   if (addParentheses) {
     if (!out.append('(')) {
       return nullptr;
     }
   }
 
   if (haveSource) {
     if (!script->appendSourceDataForToString(cx, out)) {
@@ -1337,17 +1337,17 @@ JSLinearString* JSFunction::getBoundFunc
   }
 
   static constexpr char boundWithSpaceChars[] = "bound ";
   static constexpr size_t boundWithSpaceCharsLength =
       ArrayLength(boundWithSpaceChars) - 1;  // No trailing '\0'.
   MOZ_ASSERT(
       StringEqualsAscii(cx->names().boundWithSpace, boundWithSpaceChars));
 
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
   if (name->hasTwoByteChars() && !sb.ensureTwoByteChars()) {
     return nullptr;
   }
 
   CheckedInt<size_t> len(boundTargets);
   len *= boundWithSpaceCharsLength;
   len += name->length();
   if (!len.isValid()) {
@@ -1796,17 +1796,17 @@ static bool CreateDynamicFunction(JSCont
   CompileOptions options(cx);
   options.setMutedErrors(mutedErrors)
       .setFileAndLine(filename, 1)
       .setNoScriptRval(false)
       .setIntroductionInfo(introducerFilename, introductionType, lineno,
                            maybeScript, pcOffset)
       .setScriptOrModule(maybeScript);
 
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
 
   if (isAsync) {
     if (!sb.append("async ")) {
       return false;
     }
   }
   if (!sb.append("function")) {
     return false;
--- a/js/src/vm/JSONParser.cpp
+++ b/js/src/vm/JSONParser.cpp
@@ -139,17 +139,17 @@ JSONParserBase::Token JSONParser<CharT>:
     }
   }
 
   /*
    * Slow case: string contains escaped characters.  Copy a maximal sequence
    * of unescaped characters into a temporary buffer, then an escaped
    * character, and repeat until the entire string is consumed.
    */
-  StringBuffer buffer(cx);
+  JSStringBuilder buffer(cx);
   do {
     if (start < current && !buffer.append(start.get(), current.get())) {
       return token(OOM);
     }
 
     if (current >= end) {
       break;
     }
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -490,17 +490,17 @@ JSFlatString* RegExpObject::toString(JSC
   // Steps 3-4.
   RootedAtom src(cx, getSource());
   if (!src) {
     return nullptr;
   }
   RootedAtom escapedSrc(cx, EscapeRegExpPattern(cx, src));
 
   // Step 7.
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
   size_t len = escapedSrc->length();
   if (!sb.reserve(len + 2)) {
     return nullptr;
   }
   sb.infallibleAppend('/');
   if (!sb.append(escapedSrc)) {
     return nullptr;
   }
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -1009,17 +1009,17 @@ static bool FormatV8StackFrame(JSContext
 JS_PUBLIC_API bool BuildStackString(JSContext* cx, JSPrincipals* principals,
                                     HandleObject stack,
                                     MutableHandleString stringp, size_t indent,
                                     js::StackFormat format) {
   js::AssertHeapIsIdle();
   CHECK_THREAD(cx);
   MOZ_RELEASE_ASSERT(cx->realm());
 
-  js::StringBuffer sb(cx);
+  js::JSStringBuilder sb(cx);
 
   if (format == js::StackFormat::Default) {
     format = cx->runtime()->stackFormat();
   }
   MOZ_ASSERT(format != js::StackFormat::Default);
 
   // Enter a new block to constrain the scope of possibly entering the stack's
   // realm. This ensures that when we finish the StringBuffer, we are back in
--- a/js/src/vm/StringType.cpp
+++ b/js/src/vm/StringType.cpp
@@ -2298,17 +2298,17 @@ static JSString* SymbolToSource(JSContex
   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);
+  JSStringBuilder buf(cx);
   if (code == SymbolCode::InSymbolRegistry ? !buf.append("Symbol.for(")
                                            : !buf.append("Symbol(")) {
     return nullptr;
   }
   if (desc) {
     UniqueChars quoted = QuoteString(cx, desc, '"');
     if (!quoted || !buf.append(quoted.get(), strlen(quoted.get()))) {
       return nullptr;
--- a/js/src/vm/SymbolType.cpp
+++ b/js/src/vm/SymbolType.cpp
@@ -107,17 +107,17 @@ void Symbol::dump(js::GenericPrinter& ou
     out.printf("<Invalid Symbol code=%u>", unsigned(code_));
   }
 }
 #endif  // defined(DEBUG) || defined(JS_JITSPEW)
 
 bool js::SymbolDescriptiveString(JSContext* cx, Symbol* sym,
                                  MutableHandleValue result) {
   // steps 2-5
-  StringBuffer sb(cx);
+  JSStringBuilder sb(cx);
   if (!sb.append("Symbol(")) {
     return false;
   }
   RootedString str(cx, sym->description());
   if (str) {
     if (!sb.append(str)) {
       return false;
     }
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -7207,17 +7207,17 @@ JSString* js::AsmJSModuleToString(JSCont
   MOZ_ASSERT(IsAsmJSModule(fun));
 
   const AsmJSMetadata& metadata =
       AsmJSModuleFunctionToModule(fun).metadata().asAsmJS();
   uint32_t begin = metadata.toStringStart;
   uint32_t end = metadata.srcEndAfterCurly();
   ScriptSource* source = metadata.scriptSource.get();
 
-  StringBuffer out(cx);
+  JSStringBuilder out(cx);
 
   if (isToSource && fun->isLambda() && !out.append("(")) {
     return nullptr;
   }
 
   bool haveSource = source->hasSourceText();
   if (!haveSource && !JSScript::loadSource(cx, source, &haveSource)) {
     return nullptr;
@@ -7258,17 +7258,17 @@ JSString* js::AsmJSFunctionToString(JSCo
       ExportedFunctionToInstance(fun).metadata().asAsmJS();
   const AsmJSExport& f =
       metadata.lookupAsmJSExport(ExportedFunctionToFuncIndex(fun));
 
   uint32_t begin = metadata.srcStart + f.startOffsetInModule();
   uint32_t end = metadata.srcStart + f.endOffsetInModule();
 
   ScriptSource* source = metadata.scriptSource.get();
-  StringBuffer out(cx);
+  JSStringBuilder out(cx);
 
   if (!out.append("function ")) {
     return nullptr;
   }
 
   bool haveSource = source->hasSourceText();
   if (!haveSource && !JSScript::loadSource(cx, source, &haveSource)) {
     return nullptr;
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -1816,17 +1816,17 @@ JSString* Instance::createDisplayURL(JSC
     return NewStringCopyZ<CanGC>(cx, metadata().filename.get());
   }
 
   // Otherwise, build wasm module URL from following parts:
   // - "wasm:" as protocol;
   // - URI encoded filename from metadata (if can be encoded), plus ":";
   // - 64-bit hash of the module bytes (as hex dump).
 
-  StringBuffer result(cx);
+  JSStringBuilder result(cx);
   if (!result.append("wasm:")) {
     return nullptr;
   }
 
   if (const char* filename = metadata().filename.get()) {
     // EncodeURI returns false due to invalid chars or OOM -- fail only
     // during OOM.
     JSString* filenamePrefix = EncodeURI(cx, filename, strlen(filename));
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -755,17 +755,17 @@ static JSString* KindToString(JSContext*
     case DefinitionKind::Global:
       return cx->names().global;
   }
 
   MOZ_CRASH("invalid kind");
 }
 
 static JSString* FuncTypeToString(JSContext* cx, const FuncType& funcType) {
-  StringBuffer buf(cx);
+  JSStringBuilder buf(cx);
   if (!buf.append('(')) {
     return nullptr;
   }
 
   bool first = true;
   for (ValType arg : funcType.args()) {
     if (!first && !buf.append(", ", strlen(", "))) {
       return nullptr;