Backed out 14 changesets (bug 1485066) for build bustages on MessageManagerFuzzer. CLOSED TREE
authorCosmin Sabou <csabou@mozilla.com>
Wed, 05 Sep 2018 15:54:03 +0300
changeset 490578 3d23c2f43b8a5ccd1dd21f1240689cea1566deed
parent 490577 e841c95ba671adc63c814de4e37f2413d6201cbf
child 490579 36c326e6058bc0e23520f0275b91638231b8ddb3
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1485066
milestone64.0a1
backs oute40f67f15bf12802b07690301ad9a4eeedcb2b14
f09bc4d5fdcc36f4dccd2cc399819469c5c657f2
939e27aa2d591fd8aa16c4c1488e2277d053f8f8
d50fcf82556c2c259ba818d51dc08aadfcea2ef4
5cbc0ae0117a56746918bc1ecb23ceadf8ee7894
09b5382e0bafb893a4d0c76e25d05d0b635f6dbf
6676e8fedcb375e0147a8cede0345d1990735f37
28e7e61c11ec9f5cea435c98023be275b25b31e7
b08b0cfc1dbe100feece9c2085cb73d8577735ed
8defc9eabfac7b9d73551c99dcefb0da52438001
bf167b0a3af38a895690054de642027af9aafef3
4f89260d5e30f7b77ddd05e15414be8036b1937e
c22fc17c9d87281c0c074ccb2c2f21fb9a3bf4dd
d35bb63dbc1d7c5892ee32c1f20aaab17ceaf9f5
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
Backed out 14 changesets (bug 1485066) for build bustages on MessageManagerFuzzer. CLOSED TREE Backed out changeset e40f67f15bf1 (bug 1485066) Backed out changeset f09bc4d5fdcc (bug 1485066) Backed out changeset 939e27aa2d59 (bug 1485066) Backed out changeset d50fcf82556c (bug 1485066) Backed out changeset 5cbc0ae0117a (bug 1485066) Backed out changeset 09b5382e0baf (bug 1485066) Backed out changeset 6676e8fedcb3 (bug 1485066) Backed out changeset 28e7e61c11ec (bug 1485066) Backed out changeset b08b0cfc1dbe (bug 1485066) Backed out changeset 8defc9eabfac (bug 1485066) Backed out changeset bf167b0a3af3 (bug 1485066) Backed out changeset 4f89260d5e30 (bug 1485066) Backed out changeset c22fc17c9d87 (bug 1485066) Backed out changeset d35bb63dbc1d (bug 1485066)
dom/base/ChromeUtils.cpp
dom/base/nsJSUtils.h
dom/bindings/BindingUtils.h
dom/bindings/CallbackInterface.cpp
ipc/testshell/XPCShellEnvironment.cpp
js/public/AutoByteString.h
js/public/CharacterEncoding.h
js/public/Proxy.h
js/rust/build.rs
js/rust/src/glue.rs
js/rust/src/jsglue.cpp
js/rust/tests/callback.rs
js/src/builtin/Object.cpp
js/src/builtin/Profilers.cpp
js/src/builtin/ReflectParse.cpp
js/src/builtin/Stream.cpp
js/src/builtin/String.cpp
js/src/builtin/TestingFunctions.cpp
js/src/builtin/TypedObject.cpp
js/src/builtin/intl/Collator.cpp
js/src/builtin/intl/CommonFunctions.cpp
js/src/builtin/intl/CommonFunctions.h
js/src/builtin/intl/DateTimeFormat.cpp
js/src/builtin/intl/IntlObject.cpp
js/src/builtin/intl/NumberFormat.cpp
js/src/builtin/intl/PluralRules.cpp
js/src/builtin/intl/RelativeTimeFormat.cpp
js/src/ctypes/CTypes.cpp
js/src/ctypes/Library.cpp
js/src/frontend/EmitterScope.cpp
js/src/frontend/NameFunctions.cpp
js/src/frontend/Parser.cpp
js/src/gc/Statistics.cpp
js/src/jit-test/tests/self-test/readlineBuf.js
js/src/jsapi-tests/tests.h
js/src/jsapi.cpp
js/src/jsexn.cpp
js/src/jsexn.h
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsnum.cpp
js/src/moz.build
js/src/perf/jsperf.cpp
js/src/proxy/Proxy.cpp
js/src/proxy/ScriptedProxyHandler.cpp
js/src/proxy/SecurityWrapper.cpp
js/src/shell/OSObject.cpp
js/src/shell/js.cpp
js/src/vm/BytecodeUtil.cpp
js/src/vm/BytecodeUtil.h
js/src/vm/Debugger.cpp
js/src/vm/EnvironmentObject.cpp
js/src/vm/ErrorObject.cpp
js/src/vm/ForOfIterator.cpp
js/src/vm/Interpreter.cpp
js/src/vm/JSAtom.cpp
js/src/vm/JSAtom.h
js/src/vm/JSContext.cpp
js/src/vm/JSFunction-inl.h
js/src/vm/JSFunction.cpp
js/src/vm/JSObject.cpp
js/src/vm/JSObject.h
js/src/vm/NativeObject.cpp
js/src/vm/Printer.cpp
js/src/vm/Printer.h
js/src/vm/Probes.cpp
js/src/vm/SavedStacks.cpp
js/src/vm/SavedStacks.h
js/src/vm/Scope.cpp
js/src/vm/SelfHosting.cpp
js/src/vm/StringType.cpp
js/src/vm/StringType.h
js/src/vm/TypedArrayObject.cpp
js/src/vm/UbiNodeCensus.cpp
js/src/wasm/AsmJS.cpp
js/xpconnect/loader/mozJSComponentLoader.cpp
js/xpconnect/src/Sandbox.cpp
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/XPCConvert.cpp
js/xpconnect/src/XPCDebug.cpp
js/xpconnect/src/XPCShellImpl.cpp
js/xpconnect/src/XPCThrower.cpp
js/xpconnect/src/XPCWrappedNativeJSOps.cpp
toolkit/recordreplay/ipc/JSControl.cpp
tools/fuzzing/messagemanager/MessageManagerFuzzer.cpp
xpcom/base/CycleCollectedJSRuntime.cpp
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "ChromeUtils.h"
 
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/SavedFrameAPI.h"
 #include "jsfriendapi.h"
 #include "WrapperFactory.h"
 
 #include "mozilla/Base64.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/PerformanceMetricsCollector.h"
@@ -462,21 +462,21 @@ namespace module_getter {
     JS::Rooted<JSObject*> thisObj(aCx);
     JS::Rooted<jsid> id(aCx);
     if (!ExtractArgs(aCx, args, &callee, &thisObj, &id)) {
       return false;
     }
 
     JS::Rooted<JSString*> moduleURI(
       aCx, js::GetFunctionNativeReserved(callee, SLOT_URI).toString());
-    JS::UniqueChars bytes = JS_EncodeStringToUTF8(aCx, moduleURI);
-    if (!bytes) {
+    JSAutoByteString bytes;
+    if (!bytes.encodeUtf8(aCx, moduleURI)) {
       return false;
     }
-    nsDependentCString uri(bytes.get());
+    nsDependentCString uri(bytes.ptr());
 
     RefPtr<mozJSComponentLoader> moduleloader = mozJSComponentLoader::Get();
     MOZ_ASSERT(moduleloader);
 
     JS::Rooted<JSObject*> moduleGlobal(aCx);
     JS::Rooted<JSObject*> moduleExports(aCx);
     nsresult rv = moduleloader->Import(aCx, uri, &moduleGlobal, &moduleExports);
     if (NS_FAILED(rv)) {
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -14,16 +14,17 @@
  * the generated code itself.
  */
 
 #include "mozilla/Assertions.h"
 
 #include "GeckoProfiler.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "js/AutoByteString.h"
 #include "js/Conversions.h"
 #include "js/StableStringChars.h"
 #include "nsString.h"
 
 class nsIScriptContext;
 class nsIScriptElement;
 class nsIScriptGlobalObject;
 class nsXBLPrototypeBinding;
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -3,17 +3,17 @@
 /* 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 mozilla_dom_BindingUtils_h__
 #define mozilla_dom_BindingUtils_h__
 
 #include "jsfriendapi.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/Wrapper.h"
 #include "js/Conversions.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Alignment.h"
 #include "mozilla/Array.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/DeferredFinalize.h"
 #include "mozilla/dom/BindingDeclarations.h"
@@ -1308,22 +1308,22 @@ EnumValueNotFound<false>(JSContext* cx, 
   return true;
 }
 
 template<>
 inline bool
 EnumValueNotFound<true>(JSContext* cx, JS::HandleString str, const char* type,
                         const char* sourceDescription)
 {
-  JS::UniqueChars deflated = JS_EncodeStringToUTF8(cx, str);
-  if (!deflated) {
+  JSAutoByteString deflated;
+  if (!deflated.encodeUtf8(cx, str)) {
     return false;
   }
   return ThrowErrorMessage(cx, MSG_INVALID_ENUM_VALUE, sourceDescription,
-                           deflated.get(), type);
+                           deflated.ptr(), type);
 }
 
 template<typename CharT>
 inline int
 FindEnumStringIndexImpl(const CharT* chars, size_t length, const EnumEntry* values)
 {
   int i = 0;
   for (const EnumEntry* value = values; value->value; ++value, ++i) {
--- a/dom/bindings/CallbackInterface.cpp
+++ b/dom/bindings/CallbackInterface.cpp
@@ -1,35 +1,35 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "mozilla/dom/CallbackInterface.h"
 #include "jsapi.h"
-#include "js/CharacterEncoding.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "nsPrintfCString.h"
 
 namespace mozilla {
 namespace dom {
 
 bool
 CallbackInterface::GetCallableProperty(JSContext* cx, JS::Handle<jsid> aPropId,
                                        JS::MutableHandle<JS::Value> aCallable)
 {
   if (!JS_GetPropertyById(cx, CallbackKnownNotGray(), aPropId, aCallable)) {
     return false;
   }
   if (!aCallable.isObject() ||
       !JS::IsCallable(&aCallable.toObject())) {
-    JS::RootedString propId(cx, JS_FORGET_STRING_FLATNESS(JSID_TO_FLAT_STRING(aPropId)));
-    JS::UniqueChars propName = JS_EncodeStringToUTF8(cx, propId);
-    nsPrintfCString description("Property '%s'", propName.get());
+    char* propName =
+      JS_EncodeString(cx, JS_FORGET_STRING_FLATNESS(JSID_TO_FLAT_STRING(aPropId)));
+    nsPrintfCString description("Property '%s'", propName);
+    JS_free(cx, propName);
     ThrowErrorMessage(cx, MSG_NOT_CALLABLE, description.get());
     return false;
   }
 
   return true;
 }
 
 } // namespace dom
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -11,17 +11,17 @@
 #endif
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>     /* for isatty() */
 #endif
 
 #include "base/basictypes.h"
 
 #include "jsapi.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/SourceBufferHolder.h"
 
 #include "xpcpublic.h"
 
 #include "XPCShellEnvironment.h"
 
 #include "mozilla/XPCOM.h"
@@ -78,20 +78,20 @@ static bool
 Print(JSContext *cx, unsigned argc, JS::Value *vp)
 {
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
 
     for (unsigned i = 0; i < args.length(); i++) {
         JSString *str = JS::ToString(cx, args[i]);
         if (!str)
             return false;
-        JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, str);
+        JSAutoByteString bytes(cx, str);
         if (!bytes)
             return false;
-        fprintf(stdout, "%s%s", i ? " " : "", bytes.get());
+        fprintf(stdout, "%s%s", i ? " " : "", bytes.ptr());
         fflush(stdout);
     }
     fputc('\n', stdout);
     args.rval().setUndefined();
     return true;
 }
 
 static bool
@@ -114,21 +114,21 @@ Dump(JSContext *cx, unsigned argc, JS::V
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
 
     if (!args.length())
         return true;
 
     JSString *str = JS::ToString(cx, args[0]);
     if (!str)
         return false;
-    JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, str);
+    JSAutoByteString bytes(cx, str);
     if (!bytes)
       return false;
 
-    fputs(bytes.get(), stdout);
+    fputs(bytes.ptr(), stdout);
     fflush(stdout);
     return true;
 }
 
 static bool
 Load(JSContext *cx,
      unsigned argc,
      JS::Value *vp)
@@ -142,30 +142,30 @@ Load(JSContext *cx,
         JS_ReportErrorASCII(cx, "Trying to load() into a non-global object");
         return false;
     }
 
     for (unsigned i = 0; i < args.length(); i++) {
         JS::Rooted<JSString*> str(cx, JS::ToString(cx, args[i]));
         if (!str)
             return false;
-        JS::UniqueChars filename = JS_EncodeStringToLatin1(cx, str);
+        JSAutoByteString filename(cx, str);
         if (!filename)
             return false;
-        FILE *file = fopen(filename.get(), "r");
+        FILE *file = fopen(filename.ptr(), "r");
         if (!file) {
-            filename = JS_EncodeStringToUTF8(cx, str);
-            if (!filename)
+            filename.clear();
+            if (!filename.encodeUtf8(cx, str))
                 return false;
-            JS_ReportErrorUTF8(cx, "cannot open file '%s' for reading", filename.get());
+            JS_ReportErrorUTF8(cx, "cannot open file '%s' for reading", filename.ptr());
             return false;
         }
         JS::CompileOptions options(cx);
         options.setUTF8(true)
-               .setFileAndLine(filename.get(), 1);
+               .setFileAndLine(filename.ptr(), 1);
         JS::Rooted<JSScript*> script(cx);
         bool ok = JS::Compile(cx, options, file, &script);
         fclose(file);
         if (!ok)
             return false;
 
         if (!JS_ExecuteScript(cx, script)) {
             return false;
@@ -341,23 +341,23 @@ XPCShellEnvironment::ProcessFile(JSConte
         if (JS_CompileScript(cx, buffer, strlen(buffer), options, &script)) {
             JS::WarningReporter older;
 
             ok = JS_ExecuteScript(cx, script, &result);
             if (ok && !result.isUndefined()) {
                 /* Suppress warnings from JS::ToString(). */
                 older = JS::SetWarningReporter(cx, nullptr);
                 str = JS::ToString(cx, result);
-                JS::UniqueChars bytes;
+                JSAutoByteString bytes;
                 if (str)
-                    bytes = JS_EncodeStringToLatin1(cx, str);
+                    bytes.encodeLatin1(cx, str);
                 JS::SetWarningReporter(cx, older);
 
                 if (!!bytes)
-                    fprintf(stdout, "%s\n", bytes.get());
+                    fprintf(stdout, "%s\n", bytes.ptr());
                 else
                     ok = false;
             }
         }
     } while (!hitEOF && !env->IsQuitting());
 
     fprintf(stdout, "\n");
 }
new file mode 100644
--- /dev/null
+++ b/js/public/AutoByteString.h
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+/*
+ * DEPRECATED functions and classes for heap-allocating copies of a JSString's
+ * data.
+ */
+
+#ifndef js_AutoByteString_h
+#define js_AutoByteString_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+#include "mozilla/Attributes.h" // MOZ_RAII, MOZ_GUARD*
+
+#include <string.h> // strlen
+
+#include "jstypes.h" // JS_PUBLIC_API
+
+#include "js/MemoryFunctions.h" // JS_free
+#include "js/RootingAPI.h" // JS::Handle
+#include "js/TypeDecls.h" // JSContext, JSString
+#include "js/Utility.h" // js_free, JS::UniqueChars
+
+/**
+ * DEPRECATED
+ *
+ * Allocate memory sufficient to contain the characters of |str| truncated to
+ * Latin-1 and a trailing null terminator, fill the memory with the characters
+ * interpreted in that manner plus the null terminator, and return a pointer to
+ * the memory.  The memory must be freed using JS_free to avoid leaking.
+ *
+ * This function *loses information* when it copies the characters of |str| if
+ * |str| contains code units greater than 0xFF.  Additionally, users that
+ * depend on null-termination will misinterpret the copied characters if |str|
+ * contains any nulls.  Avoid using this function if possible, because it will
+ * eventually be removed.
+ */
+extern JS_PUBLIC_API(char*)
+JS_EncodeString(JSContext* cx, JSString* str);
+
+/**
+ * DEPRECATED
+ *
+ * Same behavior as JS_EncodeString(), but encode into a UTF-8 string.
+ *
+ * This function *loses information* when it copies the characters of |str| if
+ * |str| contains invalid UTF-16: U+FFFD REPLACEMENT CHARACTER will be copied
+ * instead.
+ *
+ * The returned string is also subject to misinterpretation if |str| contains
+ * any nulls (which are faithfully transcribed into the returned string, but
+ * which will implicitly truncate the string if it's passed to functions that
+ * expect null-terminated strings).
+ *
+ * Avoid using this function if possible, because we'll remove it once we can
+ * devise a better API for the task.
+ */
+extern JS_PUBLIC_API(char*)
+JS_EncodeStringToUTF8(JSContext* cx, JS::Handle<JSString*> str);
+
+/**
+ * DEPRECATED
+ *
+ * A lightweight RAII helper class around the various JS_Encode* functions
+ * above, subject to the same pitfalls noted above.  Avoid using this class if
+ * possible, because as with the functions above, it too needs to be replaced
+ * with a better, safer API.
+ */
+class MOZ_RAII JSAutoByteString final
+{
+  private:
+    char* mBytes;
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+
+  private:
+    JSAutoByteString(const JSAutoByteString& another) = delete;
+    void operator=(const JSAutoByteString& another) = delete;
+
+  public:
+    JSAutoByteString(JSContext* cx, JSString* str
+                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : mBytes(JS_EncodeString(cx, str))
+    {
+        MOZ_ASSERT(cx);
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+    explicit JSAutoByteString(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
+      : mBytes(nullptr)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+    ~JSAutoByteString() {
+        JS_free(nullptr, mBytes);
+    }
+
+    /* Take ownership of the given byte array. */
+    void initBytes(JS::UniqueChars&& bytes) {
+        MOZ_ASSERT(!mBytes);
+        mBytes = bytes.release();
+    }
+
+    char* encodeLatin1(JSContext* cx, JSString* str) {
+        MOZ_ASSERT(!mBytes);
+        MOZ_ASSERT(cx);
+        mBytes = JS_EncodeString(cx, str);
+        return mBytes;
+    }
+
+    char* encodeUtf8(JSContext* cx, JS::Handle<JSString*> str) {
+        MOZ_ASSERT(!mBytes);
+        MOZ_ASSERT(cx);
+        mBytes = JS_EncodeStringToUTF8(cx, str);
+        return mBytes;
+    }
+
+    void clear() {
+        js_free(mBytes);
+        mBytes = nullptr;
+    }
+
+    char* ptr() const {
+        return mBytes;
+    }
+
+    bool operator!() const {
+        return !mBytes;
+    }
+
+    size_t length() const {
+        if (!mBytes)
+            return 0;
+        return strlen(mBytes);
+    }
+};
+
+#endif /* js_AutoByteString_h */
--- a/js/public/CharacterEncoding.h
+++ b/js/public/CharacterEncoding.h
@@ -112,17 +112,17 @@ class UTF8CharsZ : public mozilla::Range
 
     char* c_str() { return reinterpret_cast<char*>(get()); }
 };
 
 /*
  * A wrapper for a "const char*" that is encoded using UTF-8.
  * This class does not manage ownership of the data; that is left
  * to others.  This differs from UTF8CharsZ in that the chars are
- * const and it disallows assignment.
+ * const and it allows assignment.
  */
 class JS_PUBLIC_API(ConstUTF8CharsZ)
 {
     const char* data_;
 
   public:
     using CharT = unsigned char;
 
@@ -326,46 +326,9 @@ LossyUTF8CharsToNewLatin1CharsZ(JSContex
 extern JS_PUBLIC_API(bool)
 StringIsASCII(const char* s);
 
 } // namespace JS
 
 inline void JS_free(JS::Latin1CharsZ& ptr) { js_free((void*)ptr.get()); }
 inline void JS_free(JS::UTF8CharsZ& ptr) { js_free((void*)ptr.get()); }
 
-/**
- * DEPRECATED
- *
- * Allocate memory sufficient to contain the characters of |str| truncated to
- * Latin-1 and a trailing null terminator, fill the memory with the characters
- * interpreted in that manner plus the null terminator, and return a pointer to
- * the memory.
- *
- * This function *loses information* when it copies the characters of |str| if
- * |str| contains code units greater than 0xFF.  Additionally, users that
- * depend on null-termination will misinterpret the copied characters if |str|
- * contains any nulls.  Avoid using this function if possible, because it will
- * eventually be removed.
- */
-extern JS_PUBLIC_API(JS::UniqueChars)
-JS_EncodeStringToLatin1(JSContext* cx, JSString* str);
-
-/**
- * DEPRECATED
- *
- * Same behavior as JS_EncodeStringToLatin1(), but encode into a UTF-8 string.
- *
- * This function *loses information* when it copies the characters of |str| if
- * |str| contains invalid UTF-16: U+FFFD REPLACEMENT CHARACTER will be copied
- * instead.
- *
- * The returned string is also subject to misinterpretation if |str| contains
- * any nulls (which are faithfully transcribed into the returned string, but
- * which will implicitly truncate the string if it's passed to functions that
- * expect null-terminated strings).
- *
- * Avoid using this function if possible, because we'll remove it once we can
- * devise a better API for the task.
- */
-extern JS_PUBLIC_API(JS::UniqueChars)
-JS_EncodeStringToUTF8(JSContext* cx, JS::Handle<JSString*> str);
-
 #endif /* js_CharacterEncoding_h */
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -620,17 +620,17 @@ class JS_FRIEND_API(AutoEnterPolicy)
   protected:
     // no-op constructor for subclass
     AutoEnterPolicy()
 #ifdef JS_DEBUG
         : context(nullptr)
         , enteredAction(BaseProxyHandler::NONE)
 #endif
         {}
-    void reportErrorIfExceptionIsNotPending(JSContext* cx, HandleId id);
+    void reportErrorIfExceptionIsNotPending(JSContext* cx, jsid id);
     bool allow;
     bool rv;
 
 #ifdef JS_DEBUG
     JSContext* context;
     mozilla::Maybe<HandleObject> enteredProxy;
     mozilla::Maybe<HandleId> enteredId;
     Action                   enteredAction;
--- a/js/rust/build.rs
+++ b/js/rust/build.rs
@@ -314,16 +314,17 @@ const WHITELIST_FUNCTIONS: &'static [&'s
     "JS_DefineProperties",
     "JS_DefineProperty",
     "JS_DefinePropertyById",
     "JS_DefineUCProperty",
     "JS::detail::InitWithFailureDiagnostic",
     "JS_DestroyContext",
     "JS::DisableIncrementalGC",
     "js::Dump.*",
+    "JS_EncodeStringToUTF8",
     "JS::EnterRealm",
     "JS_EnumerateStandardClasses",
     "JS_ErrorFromException",
     "JS_FireOnNewGlobalObject",
     "JS_free",
     "JS_GC",
     "JS_GetArrayBufferData",
     "JS_GetArrayBufferViewType",
--- a/js/rust/src/glue.rs
+++ b/js/rust/src/glue.rs
@@ -335,19 +335,15 @@ extern "C" {
     pub fn DeleteJSAutoStructuredCloneBuffer(buf: *mut JSAutoStructuredCloneBuffer);
     pub fn GetLengthOfJSStructuredCloneData(data: *mut JSStructuredCloneData) -> usize;
     pub fn CopyJSStructuredCloneData(src: *mut JSStructuredCloneData, dest: *mut u8);
     pub fn WriteBytesToJSStructuredCloneData(src: *const u8,
                                              len: usize,
                                              dest: *mut JSStructuredCloneData)
                                              -> bool;
 
-    pub fn JSEncodeStringToUTF8(cx: *mut JSContext,
-                                string: JS::HandleString)
-                                -> *mut ::libc::c_char;
-
     pub fn IsDebugBuild() -> bool;
 }
 
 #[test]
 fn jsglue_cpp_configured_correctly() {
     assert_eq!(cfg!(feature = "debugmozjs"), unsafe { IsDebugBuild() });
 }
--- a/js/rust/src/jsglue.cpp
+++ b/js/rust/src/jsglue.cpp
@@ -10,17 +10,16 @@
 #ifdef JS_DEBUG
 // A hack for MFBT. Guard objects need this to work.
 #define DEBUG 1
 #endif
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/Proxy.h"
-#include "js/CharacterEncoding.h"
 #include "js/Class.h"
 #include "js/MemoryMetrics.h"
 #include "js/Principals.h"
 #include "js/StructuredClone.h"
 #include "js/Wrapper.h"
 #include "assert.h"
 
 struct ProxyTraps {
@@ -916,15 +915,9 @@ bool
 WriteBytesToJSStructuredCloneData(const uint8_t* src, size_t len, JSStructuredCloneData* dest)
 {
     assert(src != nullptr);
     assert(dest != nullptr);
 
     return dest->AppendBytes(reinterpret_cast<const char*>(src), len);
 }
 
-char*
-JSEncodeStringToUTF8(JSContext* cx, JS::HandleString string)
-{
-    return JS_EncodeStringToUTF8(cx, string).release();
-}
-
 } // extern "C"
--- a/js/rust/tests/callback.rs
+++ b/js/rust/tests/callback.rs
@@ -2,21 +2,21 @@
  * 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/. */
 
 #[macro_use]
 extern crate js;
 extern crate libc;
 
 use js::ar::AutoRealm;
-use js::glue::JSEncodeStringToUTF8;
 use js::jsapi::root::JS::CallArgs;
 use js::jsapi::root::JS::RealmOptions;
 use js::jsapi::root::JSContext;
 use js::jsapi::root::JS_DefineFunction;
+use js::jsapi::root::JS_EncodeStringToUTF8;
 use js::jsapi::root::JS_NewGlobalObject;
 use js::jsapi::root::JS_ReportErrorASCII;
 use js::jsapi::root::JS::OnNewGlobalHookOption;
 use js::jsapi::root::JS::Value;
 use js::jsval::UndefinedValue;
 use js::rust::{Runtime, SIMPLE_GLOBAL_CLASS};
 
 use std::ffi::CStr;
@@ -50,15 +50,15 @@ unsafe extern "C" fn puts(context: *mut 
     if args._base.argc_ != 1 {
         JS_ReportErrorASCII(context, b"puts() requires exactly 1 argument\0".as_ptr() as *const libc::c_char);
         return false;
     }
 
     let arg = args.get(0);
     let js = js::rust::ToString(context, arg);
     rooted!(in(context) let message_root = js);
-    let message = JSEncodeStringToUTF8(context, message_root.handle());
+    let message = JS_EncodeStringToUTF8(context, message_root.handle());
     let message = CStr::from_ptr(message);
     println!("{}", str::from_utf8(message.to_bytes()).unwrap());
 
     args.rval().set(UndefinedValue());
     return true;
 }
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -266,20 +266,17 @@ js::ObjectToSource(JSContext* cx, Handle
             /*
              * If id is a string that's not an identifier, or if it's a
              * negative integer, then it must be quoted.
              */
             if (JSID_IS_ATOM(id)
                 ? !IsIdentifier(JSID_TO_ATOM(id))
                 : JSID_TO_INT(id) < 0)
             {
-                UniqueChars quotedId = QuoteString(cx, idstr, '\'');
-                if (!quotedId)
-                    return false;
-                idstr = NewStringCopyZ<CanGC>(cx, quotedId.get());
+                idstr = QuoteString(cx, idstr, char16_t('\''));
                 if (!idstr)
                     return false;
             }
         }
 
         RootedString valsource(cx, ValueToSource(cx, val));
         if (!valsource)
             return false;
@@ -1048,22 +1045,23 @@ js::obj_create(JSContext* cx, unsigned a
     // Step 1.
     if (args.length() == 0) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
                                   "Object.create", "0", "s");
         return false;
     }
 
     if (!args[0].isObjectOrNull()) {
-        UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, args[0], nullptr);
+        RootedValue v(cx, args[0]);
+        UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
         if (!bytes)
             return false;
 
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
-                                 bytes.get(), "not an object or null");
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
+                                   bytes.get(), "not an object or null");
         return false;
     }
 
     // Step 2.
     RootedObject proto(cx, args[0].toObjectOrNull());
     RootedPlainObject obj(cx, ObjectCreateImpl(cx, proto));
     if (!obj)
         return false;
--- a/js/src/builtin/Profilers.cpp
+++ b/js/src/builtin/Profilers.cpp
@@ -23,17 +23,16 @@
 #endif
 #endif
 
 #ifdef XP_WIN
 # include <process.h>
 # define getpid _getpid
 #endif
 
-#include "js/CharacterEncoding.h"
 #include "js/Utility.h"
 #include "util/Text.h"
 #include "vm/Probes.h"
 
 #include "vm/JSContext-inl.h"
 
 using namespace js;
 
@@ -199,17 +198,17 @@ RequiredStringArg(JSContext* cx, const C
         return nullptr;
     }
 
     if (!args[argi].isString()) {
         JS_ReportErrorASCII(cx, "%s: invalid arguments (string expected)", caller);
         return nullptr;
     }
 
-    return JS_EncodeStringToLatin1(cx, args[argi].toString());
+    return UniqueChars(JS_EncodeString(cx, args[argi].toString()));
 }
 
 static bool
 StartProfiling(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 0) {
         args.rval().setBoolean(JS_StartProfiling(nullptr, getpid()));
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -3389,17 +3389,17 @@ reflect_parse(JSContext* cx, uint32_t ar
             if (!GetPropertyDefault(cx, config, sourceId, nullVal, &prop))
                 return false;
 
             if (!prop.isNullOrUndefined()) {
                 RootedString str(cx, ToString<CanGC>(cx, prop));
                 if (!str)
                     return false;
 
-                filename = EncodeLatin1(cx, str);
+                filename.reset(JS_EncodeString(cx, str));
                 if (!filename)
                     return false;
             }
 
             /* config.line */
             RootedId lineId(cx, NameToId(cx->names().line));
             RootedValue oneValue(cx, Int32Value(1));
             if (!GetPropertyDefault(cx, config, lineId, oneValue, &prop) ||
--- a/js/src/builtin/Stream.cpp
+++ b/js/src/builtin/Stream.cpp
@@ -272,18 +272,18 @@ PromiseRejectedWithPendingError(JSContex
 
 static void
 ReportArgTypeError(JSContext* cx, const char* funName, const char* expectedType, HandleValue arg)
 {
     UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, arg, nullptr);
     if (!bytes)
         return;
 
-    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, funName,
-                             expectedType, bytes.get());
+    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, funName,
+                               expectedType, bytes.get());
 }
 
 static MOZ_MUST_USE bool
 RejectWithPendingError(JSContext* cx, Handle<PromiseObject*> promise) {
     // Not much we can do about uncatchable exceptions, just bail.
     RootedValue exn(cx);
     if (!GetAndClearException(cx, &exn))
         return false;
--- a/js/src/builtin/String.cpp
+++ b/js/src/builtin/String.cpp
@@ -609,36 +609,32 @@ IsString(HandleValue v)
     return v.isString() || (v.isObject() && v.toObject().is<StringObject>());
 }
 
 MOZ_ALWAYS_INLINE bool
 str_toSource_impl(JSContext* cx, const CallArgs& args)
 {
     MOZ_ASSERT(IsString(args.thisv()));
 
-    JSString* str = ToString<CanGC>(cx, args.thisv());
+    Rooted<JSString*> str(cx, ToString<CanGC>(cx, args.thisv()));
     if (!str)
         return false;
 
-    UniqueChars quoted = QuoteString(cx, str, '"');
-    if (!quoted)
+    str = QuoteString(cx, str, '"');
+    if (!str)
         return false;
 
     StringBuffer sb(cx);
-    if (!sb.append("(new String(") ||
-        !sb.append(quoted.get(), strlen(quoted.get())) ||
-        !sb.append("))"))
-    {
+    if (!sb.append("(new String(") || !sb.append(str) || !sb.append("))"))
         return false;
-    }
-
-    JSString* result = sb.finishString();
-    if (!result)
+
+    str = sb.finishString();
+    if (!str)
         return false;
-    args.rval().setString(result);
+    args.rval().setString(str);
     return true;
 }
 
 static bool
 str_toSource(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsString, str_toSource_impl>(cx, args);
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -36,17 +36,17 @@
 #include "irregexp/RegExpAST.h"
 #include "irregexp/RegExpEngine.h"
 #include "irregexp/RegExpParser.h"
 #endif
 #include "gc/Heap.h"
 #include "jit/BaselineJIT.h"
 #include "jit/InlinableNatives.h"
 #include "jit/JitRealm.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/CompileOptions.h"
 #include "js/Debug.h"
 #include "js/HashTable.h"
 #include "js/LocaleSensitive.h"
 #include "js/SourceBufferHolder.h"
 #include "js/StableStringChars.h"
 #include "js/StructuredClone.h"
@@ -1409,23 +1409,23 @@ CallFunctionWithAsyncStack(JSContext* cx
     if (!args[2].isString() || args[2].toString()->empty()) {
         JS_ReportErrorASCII(cx, "The third argument should be a non-empty string.");
         return false;
     }
 
     RootedObject function(cx, &args[0].toObject());
     RootedObject stack(cx, &args[1].toObject());
     RootedString asyncCause(cx, args[2].toString());
-    UniqueChars utf8Cause = JS_EncodeStringToUTF8(cx, asyncCause);
-    if (!utf8Cause) {
+    JSAutoByteString utf8Cause;
+    if (!utf8Cause.encodeUtf8(cx, asyncCause)) {
         MOZ_ASSERT(cx->isExceptionPending());
         return false;
     }
 
-    JS::AutoSetAsyncStackForNewCalls sas(cx, stack, utf8Cause.get(),
+    JS::AutoSetAsyncStackForNewCalls sas(cx, stack, utf8Cause.ptr(),
                                          JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT);
     return Call(cx, UndefinedHandleValue, function,
                 JS::HandleValueArray::empty(), args.rval());
 }
 
 static bool
 EnableTrackAllocations(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -2189,25 +2189,26 @@ DumpHeap(JSContext* cx, unsigned argc, V
         }
     }
 
     if (args.length() > i) {
         Value v = args[i];
         if (v.isString()) {
             if (!fuzzingSafe) {
                 RootedString str(cx, v.toString());
-                UniqueChars fileNameBytes = JS_EncodeStringToLatin1(cx, str);
-                if (!fileNameBytes)
+                JSAutoByteString fileNameBytes;
+                if (!fileNameBytes.encodeLatin1(cx, str))
                     return false;
-                dumpFile = fopen(fileNameBytes.get(), "w");
+                const char* fileName = fileNameBytes.ptr();
+                dumpFile = fopen(fileName, "w");
                 if (!dumpFile) {
-                    fileNameBytes = JS_EncodeStringToUTF8(cx, str);
-                    if (!fileNameBytes)
+                    fileNameBytes.clear();
+                    if (!fileNameBytes.encodeUtf8(cx, str))
                         return false;
-                    JS_ReportErrorUTF8(cx, "can't open %s", fileNameBytes.get());
+                    JS_ReportErrorUTF8(cx, "can't open %s", fileNameBytes.ptr());
                     return false;
                 }
             }
             ++i;
         }
     }
 
     if (i != args.length()) {
@@ -2740,50 +2741,48 @@ class CloneBufferObject : public NativeO
         js_delete(data());
         setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
     }
 
     static bool
     setCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
         Rooted<CloneBufferObject*> obj(cx, &args.thisv().toObject().as<CloneBufferObject>());
 
-        const char* data = nullptr;
-        UniqueChars dataOwner;
+        uint8_t* data = nullptr;
+        UniquePtr<uint8_t[], JS::FreePolicy> dataOwner;
         uint32_t nbytes;
 
         if (args.get(0).isObject() && args[0].toObject().is<ArrayBufferObject>()) {
             ArrayBufferObject* buffer = &args[0].toObject().as<ArrayBufferObject>();
             bool isSharedMemory;
-            uint8_t* dataBytes = nullptr;
-            js::GetArrayBufferLengthAndData(buffer, &nbytes, &isSharedMemory, &dataBytes);
+            js::GetArrayBufferLengthAndData(buffer, &nbytes, &isSharedMemory, &data);
             MOZ_ASSERT(!isSharedMemory);
-            data = reinterpret_cast<char*>(dataBytes);
         } else {
             JSString* str = JS::ToString(cx, args.get(0));
             if (!str)
                 return false;
-            dataOwner = JS_EncodeStringToLatin1(cx, str);
-            if (!dataOwner)
+            data = reinterpret_cast<uint8_t*>(JS_EncodeString(cx, str));
+            if (!data)
                 return false;
-            data = dataOwner.get();
+            dataOwner.reset(data);
             nbytes = JS_GetStringLength(str);
         }
 
         if (nbytes == 0 || (nbytes % sizeof(uint64_t) != 0)) {
             JS_ReportErrorASCII(cx, "Invalid length for clonebuffer data");
             return false;
         }
 
         auto buf = js::MakeUnique<JSStructuredCloneData>(JS::StructuredCloneScope::DifferentProcess);
         if (!buf || !buf->Init(nbytes)) {
             ReportOutOfMemory(cx);
             return false;
         }
 
-        MOZ_ALWAYS_TRUE(buf->AppendBytes(data, nbytes));
+        MOZ_ALWAYS_TRUE(buf->AppendBytes((const char*)data, nbytes));
         obj->discard();
         obj->setData(buf.release(), true);
 
         args.rval().setUndefined();
         return true;
     }
 
     static bool
@@ -2910,27 +2909,27 @@ const JSPropertySpec CloneBufferObject::
     JS_PS_END
 };
 
 static mozilla::Maybe<JS::StructuredCloneScope>
 ParseCloneScope(JSContext* cx, HandleString str)
 {
     mozilla::Maybe<JS::StructuredCloneScope> scope;
 
-    JSLinearString* scopeStr = str->ensureLinear(cx);
+    JSAutoByteString scopeStr(cx, str);
     if (!scopeStr)
         return scope;
 
-    if (StringEqualsAscii(scopeStr, "SameProcessSameThread"))
+    if (strcmp(scopeStr.ptr(), "SameProcessSameThread") == 0)
         scope.emplace(JS::StructuredCloneScope::SameProcessSameThread);
-    else if (StringEqualsAscii(scopeStr, "SameProcessDifferentThread"))
+    else if (strcmp(scopeStr.ptr(), "SameProcessDifferentThread") == 0)
         scope.emplace(JS::StructuredCloneScope::SameProcessDifferentThread);
-    else if (StringEqualsAscii(scopeStr, "DifferentProcess"))
+    else if (strcmp(scopeStr.ptr(), "DifferentProcess") == 0)
         scope.emplace(JS::StructuredCloneScope::DifferentProcess);
-    else if (StringEqualsAscii(scopeStr, "DifferentProcessForIndexedDB"))
+    else if (strcmp(scopeStr.ptr(), "DifferentProcessForIndexedDB") == 0)
         scope.emplace(JS::StructuredCloneScope::DifferentProcessForIndexedDB);
 
     return scope;
 }
 
 static bool
 Serialize(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -2947,23 +2946,23 @@ Serialize(JSContext* cx, unsigned argc, 
         RootedValue v(cx);
         if (!JS_GetProperty(cx, opts, "SharedArrayBuffer", &v))
             return false;
 
         if (!v.isUndefined()) {
             JSString* str = JS::ToString(cx, v);
             if (!str)
                 return false;
-            JSLinearString* poli = str->ensureLinear(cx);
+            JSAutoByteString poli(cx, str);
             if (!poli)
                 return false;
 
-            if (StringEqualsAscii(poli, "allow")) {
+            if (strcmp(poli.ptr(), "allow") == 0) {
                 // default
-            } else if (StringEqualsAscii(poli, "deny")) {
+            } else if (strcmp(poli.ptr(), "deny") == 0) {
                 policy.denySharedArrayBuffer();
             } else {
                 JS_ReportErrorASCII(cx, "Invalid policy value for 'SharedArrayBuffer'");
                 return false;
             }
         }
 
         if (!JS_GetProperty(cx, opts, "scope", &v))
@@ -3300,27 +3299,21 @@ GetBacktrace(JSContext* cx, unsigned arg
             return false;
         showLocals = ToBoolean(v);
 
         if (!JS_GetProperty(cx, cfg, "thisprops", &v))
             return false;
         showThisProps = ToBoolean(v);
     }
 
-    JS::UniqueChars buf = JS::FormatStackDump(cx, showArgs, showLocals, showThisProps);
+    JS::UniqueChars buf = JS::FormatStackDump(cx, nullptr, showArgs, showLocals, showThisProps);
     if (!buf)
         return false;
 
-    JS::ConstUTF8CharsZ utf8chars(buf.get(), strlen(buf.get()));
-    JSString* str = NewStringCopyUTF8Z<CanGC>(cx, utf8chars);
-    if (!str)
-        return false;
-
-    args.rval().setString(str);
-    return true;
+    return ReturnStringCopy(cx, args, buf.get());
 }
 
 static bool
 ReportOutOfMemory(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ReportOutOfMemory(cx);
     cx->clearPendingException();
@@ -4123,41 +4116,41 @@ SetGCCallback(JSContext* cx, unsigned ar
 
     RootedValue v(cx);
     if (!JS_GetProperty(cx, opts, "action", &v))
         return false;
 
     JSString* str = JS::ToString(cx, v);
     if (!str)
         return false;
-    RootedLinearString action(cx, str->ensureLinear(cx));
+    JSAutoByteString action(cx, str);
     if (!action)
         return false;
 
     int32_t phases = 0;
-    if (StringEqualsAscii(action, "minorGC") || StringEqualsAscii(action, "majorGC")) {
+    if ((strcmp(action.ptr(), "minorGC") == 0) || (strcmp(action.ptr(), "majorGC") == 0)) {
         if (!JS_GetProperty(cx, opts, "phases", &v))
             return false;
         if (v.isUndefined()) {
             phases = (1 << JSGC_END);
         } else {
             JSString* str = JS::ToString(cx, v);
             if (!str)
                 return false;
-            JSLinearString* phasesStr = str->ensureLinear(cx);
+            JSAutoByteString phasesStr(cx, str);
             if (!phasesStr)
                 return false;
 
-            if (StringEqualsAscii(phasesStr, "begin")) {
+            if (strcmp(phasesStr.ptr(), "begin") == 0)
                 phases = (1 << JSGC_BEGIN);
-            } else if (StringEqualsAscii(phasesStr, "end")) {
+            else if (strcmp(phasesStr.ptr(), "end") == 0)
                 phases = (1 << JSGC_END);
-            } else if (StringEqualsAscii(phasesStr, "both")) {
+            else if (strcmp(phasesStr.ptr(), "both") == 0)
                 phases = (1 << JSGC_BEGIN) | (1 << JSGC_END);
-            } else {
+            else {
                 JS_ReportErrorASCII(cx, "Invalid callback phase");
                 return false;
             }
         }
     }
 
     if (gcCallback::prevMajorGC) {
         JS_SetGCCallback(cx, nullptr, nullptr);
@@ -4166,27 +4159,27 @@ SetGCCallback(JSContext* cx, unsigned ar
     }
 
     if (gcCallback::prevMinorGC) {
         JS_SetGCCallback(cx, nullptr, nullptr);
         js_delete<gcCallback::MinorGC>(gcCallback::prevMinorGC);
         gcCallback::prevMinorGC = nullptr;
     }
 
-    if (StringEqualsAscii(action, "minorGC")) {
+    if (strcmp(action.ptr(), "minorGC") == 0) {
         auto info = js_new<gcCallback::MinorGC>();
         if (!info) {
             ReportOutOfMemory(cx);
             return false;
         }
 
         info->phases = phases;
         info->active = true;
         JS_SetGCCallback(cx, gcCallback::minorGC, info);
-    } else if (StringEqualsAscii(action, "majorGC")) {
+    } else if (strcmp(action.ptr(), "majorGC") == 0) {
         if (!JS_GetProperty(cx, opts, "depth", &v))
             return false;
         int32_t depth = 1;
         if (!v.isUndefined()) {
             if (!ToInt32(cx, v, &depth))
                 return false;
         }
         if (depth < 0) {
@@ -4736,21 +4729,21 @@ SetTimeZone(JSContext* cx, unsigned argc
 #if defined(_WIN32)
         return _putenv_s("TZ", "") == 0;
 #else
         return unsetenv("TZ") == 0;
 #endif /* _WIN32 */
     };
 
     if (args[0].isString() && !args[0].toString()->empty()) {
-        UniqueChars timeZone = JS_EncodeStringToLatin1(cx, args[0].toString());
-        if (!timeZone)
+        JSAutoByteString timeZone;
+        if (!timeZone.encodeLatin1(cx, args[0].toString()))
             return false;
 
-        if (!setTimeZone(timeZone.get())) {
+        if (!setTimeZone(timeZone.ptr())) {
             JS_ReportErrorASCII(cx, "Failed to set 'TZ' environment variable");
             return false;
         }
     } else {
         if (!unsetTimeZone()) {
             JS_ReportErrorASCII(cx, "Failed to unset 'TZ' environment variable");
             return false;
         }
@@ -4826,21 +4819,21 @@ SetDefaultLocale(JSContext* cx, unsigned
                             : containsOnlyValidBCP47Characters(str->twoByteChars(nogc), length);
         }
 
         if (!hasValidChars) {
             ReportUsageErrorASCII(cx, callee, "First argument should be BCP47 language tag");
             return false;
         }
 
-        UniqueChars locale = JS_EncodeStringToLatin1(cx, str);
-        if (!locale)
+        JSAutoByteString locale;
+        if (!locale.encodeLatin1(cx, str))
             return false;
 
-        if (!JS_SetDefaultLocale(cx->runtime(), locale.get())) {
+        if (!JS_SetDefaultLocale(cx->runtime(), locale.ptr())) {
             ReportOutOfMemory(cx);
             return false;
         }
     } else {
         JS_ResetDefaultLocale(cx->runtime());
     }
 
     args.rval().setUndefined();
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -7,17 +7,16 @@
 #include "builtin/TypedObject-inl.h"
 
 #include "mozilla/Casting.h"
 #include "mozilla/CheckedInt.h"
 
 #include "jsutil.h"
 
 #include "gc/Marking.h"
-#include "js/CharacterEncoding.h"
 #include "js/Vector.h"
 #include "util/StringBuffer.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSFunction.h"
 #include "vm/Realm.h"
 #include "vm/SelfHosting.h"
 #include "vm/StringType.h"
 #include "vm/TypedArrayObject.h"
@@ -1587,16 +1586,31 @@ TypedObject::createZeroed(JSContext* cx,
     buffer = ArrayBufferObject::create(cx, totalSize);
     if (!buffer)
         return nullptr;
     descr->initInstances(cx->runtime(), buffer->dataPointer(), 1);
     obj->attach(cx, *buffer, 0);
     return obj;
 }
 
+static bool
+ReportTypedObjTypeError(JSContext* cx,
+                        const unsigned errorNumber,
+                        HandleTypedObject obj)
+{
+    // Serialize type string of obj
+    RootedAtom typeReprAtom(cx, &obj->typeDescr().stringRepr());
+    UniqueChars typeReprStr(JS_EncodeStringToUTF8(cx, typeReprAtom));
+    if (!typeReprStr)
+        return false;
+
+    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, typeReprStr.get());
+    return false;
+}
+
 /* static */ void
 OutlineTypedObject::obj_trace(JSTracer* trc, JSObject* object)
 {
     OutlineTypedObject& typedObj = object->as<OutlineTypedObject>();
 
     TraceEdge(trc, typedObj.shapePtr(), "OutlineTypedObject_shape");
 
     if (!typedObj.owner_)
@@ -1676,30 +1690,41 @@ TypedObject::obj_lookupProperty(JSContex
         objp.set(nullptr);
         propp.setNotFound();
         return true;
     }
 
     return LookupProperty(cx, proto, id, objp, propp);
 }
 
+static bool
+ReportPropertyError(JSContext* cx,
+                    const unsigned errorNumber,
+                    HandleId id)
+{
+    RootedValue idVal(cx, IdToValue(id));
+    RootedString str(cx, ValueToSource(cx, idVal));
+    if (!str)
+        return false;
+
+    UniqueChars propName(JS_EncodeStringToUTF8(cx, str));
+    if (!propName)
+        return false;
+
+    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, propName.get());
+    return false;
+}
+
 bool
 TypedObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
                                 Handle<PropertyDescriptor> desc,
                                 ObjectOpResult& result)
 {
-    // Serialize the type string of |obj|.
-    RootedAtom typeReprAtom(cx, &obj->as<TypedObject>().typeDescr().stringRepr());
-    UniqueChars typeReprStr = StringToNewUTF8CharsZ(cx, *typeReprAtom);
-    if (!typeReprStr)
-        return false;
-
-    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_OBJECT_NOT_EXTENSIBLE,
-                             typeReprStr.get());
-    return false;
+    Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
+    return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj);
 }
 
 bool
 TypedObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
 {
     Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
     switch (typedObj->typeDescr().kind()) {
       case type::Scalar:
@@ -1984,24 +2009,18 @@ IsOwnId(JSContext* cx, HandleObject obj,
     }
 
     return false;
 }
 
 bool
 TypedObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
 {
-    if (IsOwnId(cx, obj, id)) {
-        UniqueChars propName = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
-        if (!propName)
-            return false;
-
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_CANT_DELETE, propName.get());
-        return false;
-    }
+    if (IsOwnId(cx, obj, id))
+        return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
 
     RootedObject proto(cx, obj->staticPrototype());
     if (!proto)
         return result.succeed();
 
     return DeleteProperty(cx, proto, id, result);
 }
 
--- a/js/src/builtin/intl/Collator.cpp
+++ b/js/src/builtin/intl/Collator.cpp
@@ -12,17 +12,17 @@
 
 #include "jsapi.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/intl/SharedIntlData.h"
 #include "gc/FreeOp.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/StableStringChars.h"
 #include "js/TypeDecls.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/Runtime.h"
 #include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
@@ -195,21 +195,21 @@ js::intl_Collator_availableLocales(JSCon
 
 bool
 js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
     MOZ_ASSERT(args[0].isString());
 
-    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
+    JSAutoByteString locale(cx, args[0].toString());
     if (!locale)
         return false;
     UErrorCode status = U_ZERO_ERROR;
-    UEnumeration* values = ucol_getKeywordValuesForLocale("co", locale.get(), false, &status);
+    UEnumeration* values = ucol_getKeywordValuesForLocale("co", locale.ptr(), false, &status);
     if (U_FAILURE(status)) {
         ReportInternalError(cx);
         return false;
     }
     ScopedICUObject<UEnumeration, uenum_close> toClose(values);
 
     uint32_t count = uenum_count(values, &status);
     if (U_FAILURE(status)) {
@@ -272,17 +272,17 @@ NewUCollator(JSContext* cx, Handle<Colla
     RootedValue value(cx);
 
     RootedObject internals(cx, intl::GetInternalsObject(cx, collator));
     if (!internals)
         return nullptr;
 
     if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
         return nullptr;
-    UniqueChars locale = intl::EncodeLocale(cx, value.toString());
+    JSAutoByteString locale(cx, value.toString());
     if (!locale)
         return nullptr;
 
     // UCollator options with default values.
     UColAttributeValue uStrength = UCOL_DEFAULT;
     UColAttributeValue uCaseLevel = UCOL_OFF;
     UColAttributeValue uAlternate = UCOL_DEFAULT;
     UColAttributeValue uNumeric = UCOL_OFF;
@@ -295,17 +295,17 @@ NewUCollator(JSContext* cx, Handle<Colla
 
     {
         JSLinearString* usage = value.toString()->ensureLinear(cx);
         if (!usage)
             return nullptr;
         if (StringEqualsAscii(usage, "search")) {
             // ICU expects search as a Unicode locale extension on locale.
             // Unicode locale extensions must occur before private use extensions.
-            const char* oldLocale = locale.get();
+            const char* oldLocale = locale.ptr();
             const char* p;
             size_t index;
             size_t localeLen = strlen(oldLocale);
             if ((p = strstr(oldLocale, "-x-")))
                 index = p - oldLocale;
             else
                 index = localeLen;
 
@@ -318,17 +318,18 @@ NewUCollator(JSContext* cx, Handle<Colla
             }
             size_t insertLen = strlen(insert);
             char* newLocale = cx->pod_malloc<char>(localeLen + insertLen + 1);
             if (!newLocale)
                 return nullptr;
             memcpy(newLocale, oldLocale, index);
             memcpy(newLocale + index, insert, insertLen);
             memcpy(newLocale + index + insertLen, oldLocale + index, localeLen - index + 1); // '\0'
-            locale = JS::UniqueChars(newLocale);
+            locale.clear();
+            locale.initBytes(JS::UniqueChars(newLocale));
         } else {
             MOZ_ASSERT(StringEqualsAscii(usage, "sort"));
         }
     }
 
     // We don't need to look at the collation property - it can only be set
     // via the Unicode locale extension and is therefore already set on
     // locale.
@@ -380,17 +381,17 @@ NewUCollator(JSContext* cx, Handle<Colla
             uCaseFirst = UCOL_LOWER_FIRST;
         } else {
             MOZ_ASSERT(StringEqualsAscii(caseFirst, "false"));
             uCaseFirst = UCOL_OFF;
         }
     }
 
     UErrorCode status = U_ZERO_ERROR;
-    UCollator* coll = ucol_open(IcuLocale(locale.get()), &status);
+    UCollator* coll = ucol_open(IcuLocale(locale.ptr()), &status);
     if (U_FAILURE(status)) {
         ReportInternalError(cx);
         return nullptr;
     }
 
     ucol_setAttribute(coll, UCOL_STRENGTH, uStrength, &status);
     ucol_setAttribute(coll, UCOL_CASE_LEVEL, uCaseLevel, &status);
     ucol_setAttribute(coll, UCOL_ALTERNATE_HANDLING, uAlternate, &status);
--- a/js/src/builtin/intl/CommonFunctions.cpp
+++ b/js/src/builtin/intl/CommonFunctions.cpp
@@ -4,19 +4,16 @@
  * 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/. */
 
 /* Operations used to implement multiple Intl.* classes. */
 
 #include "builtin/intl/CommonFunctions.h"
 
 #include "mozilla/Assertions.h"
-#include "mozilla/TextUtils.h"
-
-#include <algorithm>
 
 #include "jsfriendapi.h" // for GetErrorMessage, JSMSG_INTERNAL_INTL_ERROR
 
 #include "js/Value.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/SelfHosting.h"
 #include "vm/Stack.h"
@@ -80,42 +77,16 @@ js::intl::GetInternalsObject(JSContext* 
 }
 
 void
 js::intl::ReportInternalError(JSContext* cx)
 {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
 }
 
-js::UniqueChars
-js::intl::EncodeLocale(JSContext* cx, JSString* locale)
-{
-#ifdef DEBUG
-    auto containsOnlyValidBCP47Chars = [](auto* chars, size_t length) {
-        return length > 0 &&
-               mozilla::IsAsciiAlpha(chars[0]) &&
-               std::all_of(chars, chars + length, [](auto c) {
-                   return mozilla::IsAsciiAlphanumeric(c) || c == '-';
-               });
-    };
-
-    if (JSLinearString* linear = locale->ensureLinear(cx)) {
-        JS::AutoCheckCannotGC nogc;
-        MOZ_ASSERT(linear->hasLatin1Chars()
-                   ? containsOnlyValidBCP47Chars(linear->latin1Chars(nogc), linear->length())
-                   : containsOnlyValidBCP47Chars(linear->twoByteChars(nogc), linear->length()));
-    } else {
-        // Ignore OOM when only performing a debug assertion.
-        cx->recoverFromOutOfMemory();
-    }
-#endif
-
-    return EncodeLatin1(cx, locale);
-}
-
 bool
 js::intl::GetAvailableLocales(JSContext* cx, CountAvailable countAvailable,
                               GetAvailable getAvailable, JS::MutableHandle<JS::Value> result)
 {
     RootedObject locales(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr));
     if (!locales)
         return false;
 
--- a/js/src/builtin/intl/CommonFunctions.h
+++ b/js/src/builtin/intl/CommonFunctions.h
@@ -67,19 +67,16 @@ static inline const char*
 IcuLocale(const char* locale)
 {
     if (StringsAreEqual(locale, "und"))
         return ""; // ICU root locale
 
     return locale;
 }
 
-extern UniqueChars
-EncodeLocale(JSContext* cx, JSString* locale);
-
 // Starting with ICU 59, UChar defaults to char16_t.
 static_assert(mozilla::IsSame<UChar, char16_t>::value,
               "SpiderMonkey doesn't support redefining UChar to a different type");
 
 // The inline capacity we use for a Vector<char16_t>.  Use this to ensure that
 // our uses of ICU string functions, below and elsewhere, will try to fill the
 // buffer's entire inline capacity before growing it and heap-allocating.
 constexpr size_t INITIAL_CHAR_BUFFER_SIZE = 32;
--- a/js/src/builtin/intl/DateTimeFormat.cpp
+++ b/js/src/builtin/intl/DateTimeFormat.cpp
@@ -14,17 +14,17 @@
 #include "jsfriendapi.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/intl/SharedIntlData.h"
 #include "builtin/intl/TimeZoneDataGenerated.h"
 #include "gc/FreeOp.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/StableStringChars.h"
 #include "vm/DateTime.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/Runtime.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
@@ -231,20 +231,20 @@ js::intl_DateTimeFormat_availableLocales
     RootedValue result(cx);
     if (!GetAvailableLocales(cx, udat_countAvailable, udat_getAvailable, &result))
         return false;
     args.rval().set(result);
     return true;
 }
 
 static bool
-DefaultCalendar(JSContext* cx, const UniqueChars& locale, MutableHandleValue rval)
+DefaultCalendar(JSContext* cx, const JSAutoByteString& locale, MutableHandleValue rval)
 {
     UErrorCode status = U_ZERO_ERROR;
-    UCalendar* cal = ucal_open(nullptr, 0, locale.get(), UCAL_DEFAULT, &status);
+    UCalendar* cal = ucal_open(nullptr, 0, locale.ptr(), UCAL_DEFAULT, &status);
 
     // This correctly handles nullptr |cal| when opening failed.
     ScopedICUObject<UCalendar, ucal_close> closeCalendar(cal);
 
     const char* calendar = ucal_getType(cal, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return false;
@@ -278,17 +278,17 @@ const CalendarAlias calendarAliases[] = 
 
 bool
 js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
     MOZ_ASSERT(args[0].isString());
 
-    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
+    JSAutoByteString locale(cx, args[0].toString());
     if (!locale)
         return false;
 
     RootedObject calendars(cx, NewDenseEmptyArray(cx));
     if (!calendars)
         return false;
     uint32_t index = 0;
 
@@ -297,17 +297,17 @@ js::intl_availableCalendars(JSContext* c
     if (!DefaultCalendar(cx, locale, &element))
         return false;
 
     if (!DefineDataElement(cx, calendars, index++, element))
         return false;
 
     // Now get the calendars that "would make a difference", i.e., not the default.
     UErrorCode status = U_ZERO_ERROR;
-    UEnumeration* values = ucal_getKeywordValuesForLocale("ca", locale.get(), false, &status);
+    UEnumeration* values = ucal_getKeywordValuesForLocale("ca", locale.ptr(), false, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return false;
     }
     ScopedICUObject<UEnumeration, uenum_close> toClose(values);
 
     uint32_t count = uenum_count(values, &status);
     if (U_FAILURE(status)) {
@@ -355,17 +355,17 @@ js::intl_availableCalendars(JSContext* c
 
 bool
 js::intl_defaultCalendar(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
     MOZ_ASSERT(args[0].isString());
 
-    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
+    JSAutoByteString locale(cx, args[0].toString());
     if (!locale)
         return false;
 
     return DefaultCalendar(cx, locale, args.rval());
 }
 
 bool
 js::intl_IsValidTimeZoneName(JSContext* cx, unsigned argc, Value* vp)
@@ -522,28 +522,28 @@ js::intl_isDefaultTimeZone(JSContext* cx
 bool
 js::intl_patternForSkeleton(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(args[0].isString());
     MOZ_ASSERT(args[1].isString());
 
-    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
+    JSAutoByteString locale(cx, args[0].toString());
     if (!locale)
         return false;
 
     AutoStableStringChars skeleton(cx);
     if (!skeleton.initTwoByte(cx, args[1].toString()))
         return false;
 
     mozilla::Range<const char16_t> skelChars = skeleton.twoByteRange();
 
     UErrorCode status = U_ZERO_ERROR;
-    UDateTimePatternGenerator* gen = udatpg_open(IcuLocale(locale.get()), &status);
+    UDateTimePatternGenerator* gen = udatpg_open(IcuLocale(locale.ptr()), &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return false;
     }
     ScopedICUObject<UDateTimePatternGenerator, udatpg_close> toClose(gen);
 
     JSString* str =
         CallICU(cx, [gen, &skelChars](UChar* chars, uint32_t size, UErrorCode* status) {
@@ -559,17 +559,17 @@ js::intl_patternForSkeleton(JSContext* c
 
 bool
 js::intl_patternForStyle(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 4);
     MOZ_ASSERT(args[0].isString());
 
-    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
+    JSAutoByteString locale(cx, args[0].toString());
     if (!locale)
         return false;
 
     UDateFormatStyle dateStyle = UDAT_NONE;
     UDateFormatStyle timeStyle = UDAT_NONE;
 
     if (args[1].isString()) {
         JSLinearString* dateStyleStr = args[1].toString()->ensureLinear(cx);
@@ -607,17 +607,17 @@ js::intl_patternForStyle(JSContext* cx, 
 
     AutoStableStringChars timeZone(cx);
     if (!timeZone.initTwoByte(cx, args[3].toString()))
         return false;
 
     mozilla::Range<const char16_t> timeZoneChars = timeZone.twoByteRange();
 
     UErrorCode status = U_ZERO_ERROR;
-    UDateFormat* df = udat_open(timeStyle, dateStyle, IcuLocale(locale.get()),
+    UDateFormat* df = udat_open(timeStyle, dateStyle, IcuLocale(locale.ptr()),
                                 timeZoneChars.begin().get(), timeZoneChars.length(),
                                 nullptr, -1, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return false;
     }
     ScopedICUObject<UDateFormat, udat_close> toClose(df);
 
@@ -640,17 +640,17 @@ NewUDateFormat(JSContext* cx, Handle<Dat
     RootedValue value(cx);
 
     RootedObject internals(cx, intl::GetInternalsObject(cx, dateTimeFormat));
     if (!internals)
        return nullptr;
 
     if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
         return nullptr;
-    UniqueChars locale = intl::EncodeLocale(cx, value.toString());
+    JSAutoByteString locale(cx, value.toString());
     if (!locale)
         return nullptr;
 
     // We don't need to look at calendar and numberingSystem - they can only be
     // set via the Unicode locale extension and are therefore already set on
     // locale.
 
     if (!GetProperty(cx, internals, internals, cx->names().timeZone, &value))
@@ -668,17 +668,17 @@ NewUDateFormat(JSContext* cx, Handle<Dat
     AutoStableStringChars pattern(cx);
     if (!pattern.initTwoByte(cx, value.toString()))
         return nullptr;
 
     mozilla::Range<const char16_t> patternChars = pattern.twoByteRange();
 
     UErrorCode status = U_ZERO_ERROR;
     UDateFormat* df =
-        udat_open(UDAT_PATTERN, UDAT_PATTERN, IcuLocale(locale.get()),
+        udat_open(UDAT_PATTERN, UDAT_PATTERN, IcuLocale(locale.ptr()),
                   timeZoneChars.begin().get(), timeZoneChars.length(),
                   patternChars.begin().get(), patternChars.length(), &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return nullptr;
     }
 
     // ECMAScript requires the Gregorian calendar to be used from the beginning
--- a/js/src/builtin/intl/IntlObject.cpp
+++ b/js/src/builtin/intl/IntlObject.cpp
@@ -16,17 +16,17 @@
 
 #include "builtin/intl/Collator.h"
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/DateTimeFormat.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/NumberFormat.h"
 #include "builtin/intl/PluralRules.h"
 #include "builtin/intl/ScopedICUObject.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/Class.h"
 #include "js/StableStringChars.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
@@ -45,24 +45,24 @@ using js::intl::IcuLocale;
 /******************** Intl ********************/
 
 bool
 js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
 
-    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
+    JSAutoByteString locale(cx, args[0].toString());
     if (!locale)
         return false;
 
     UErrorCode status = U_ZERO_ERROR;
     const UChar* uTimeZone = nullptr;
     int32_t uTimeZoneLength = 0;
-    UCalendar* cal = ucal_open(uTimeZone, uTimeZoneLength, locale.get(), UCAL_DEFAULT, &status);
+    UCalendar* cal = ucal_open(uTimeZone, uTimeZoneLength, locale.ptr(), UCAL_DEFAULT, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return false;
     }
     ScopedICUObject<UCalendar, ucal_close> toClose(cal);
 
     RootedObject info(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!info)
@@ -365,18 +365,19 @@ ComputeSingleDisplayName(JSContext* cx, 
 
 bool
 js::intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 3);
 
     // 1. Assert: locale is a string.
-    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
-    if (!locale)
+    RootedString str(cx, args[0].toString());
+    JSAutoByteString locale;
+    if (!locale.encodeUtf8(cx, str))
         return false;
 
     // 2. Assert: style is a string.
     DisplayNameStyle dnStyle;
     {
         JSLinearString* style = args[1].toString()->ensureLinear(cx);
         if (!style)
             return false;
@@ -399,27 +400,27 @@ js::intl_ComputeDisplayNames(JSContext* 
     // 4. Let result be ArrayCreate(0).
     RootedArrayObject result(cx, NewDenseUnallocatedArray(cx, keys->length()));
     if (!result)
         return false;
 
     UErrorCode status = U_ZERO_ERROR;
 
     UDateFormat* fmt =
-        udat_open(UDAT_DEFAULT, UDAT_DEFAULT, IcuLocale(locale.get()),
+        udat_open(UDAT_DEFAULT, UDAT_DEFAULT, IcuLocale(locale.ptr()),
         nullptr, 0, nullptr, 0, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return false;
     }
     ScopedICUObject<UDateFormat, udat_close> datToClose(fmt);
 
     // UDateTimePatternGenerator will be needed for translations of date and
     // time fields like "month", "week", "day" etc.
-    UDateTimePatternGenerator* dtpg = udatpg_open(IcuLocale(locale.get()), &status);
+    UDateTimePatternGenerator* dtpg = udatpg_open(IcuLocale(locale.ptr()), &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return false;
     }
     ScopedICUObject<UDateTimePatternGenerator, udatpg_close> datPgToClose(dtpg);
 
     // 5. For each element of keys,
     RootedString keyValStr(cx);
@@ -455,28 +456,28 @@ js::intl_ComputeDisplayNames(JSContext* 
 }
 
 bool
 js::intl_GetLocaleInfo(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
 
-    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
+    JSAutoByteString locale(cx, args[0].toString());
     if (!locale)
         return false;
 
     RootedObject info(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!info)
         return false;
 
     if (!DefineDataProperty(cx, info, cx->names().locale, args[0]))
         return false;
 
-    bool rtl = uloc_isRightToLeft(IcuLocale(locale.get()));
+    bool rtl = uloc_isRightToLeft(IcuLocale(locale.ptr()));
 
     RootedValue dir(cx, StringValue(rtl ? cx->names().rtl : cx->names().ltr));
 
     if (!DefineDataProperty(cx, info, cx->names().direction, dir))
         return false;
 
     args.rval().setObject(*info);
     return true;
--- a/js/src/builtin/intl/NumberFormat.cpp
+++ b/js/src/builtin/intl/NumberFormat.cpp
@@ -15,17 +15,16 @@
 #include <stddef.h>
 #include <stdint.h>
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "ds/Sort.h"
 #include "gc/FreeOp.h"
-#include "js/CharacterEncoding.h"
 #include "js/RootingAPI.h"
 #include "js/StableStringChars.h"
 #include "js/TypeDecls.h"
 #include "vm/JSContext.h"
 #include "vm/SelfHosting.h"
 #include "vm/Stack.h"
 
 #include "vm/JSObject-inl.h"
@@ -209,22 +208,22 @@ js::intl_NumberFormat_availableLocales(J
 
 bool
 js::intl_numberingSystem(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
     MOZ_ASSERT(args[0].isString());
 
-    UniqueChars locale = intl::EncodeLocale(cx, args[0].toString());
+    JSAutoByteString locale(cx, args[0].toString());
     if (!locale)
         return false;
 
     UErrorCode status = U_ZERO_ERROR;
-    UNumberingSystem* numbers = unumsys_open(IcuLocale(locale.get()), &status);
+    UNumberingSystem* numbers = unumsys_open(IcuLocale(locale.ptr()), &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return false;
     }
 
     ScopedICUObject<UNumberingSystem, unumsys_close> toClose(numbers);
 
     const char* name = unumsys_getName(numbers);
@@ -251,17 +250,17 @@ NewUNumberFormat(JSContext* cx, Handle<N
     RootedValue value(cx);
 
     RootedObject internals(cx, intl::GetInternalsObject(cx, numberFormat));
     if (!internals)
        return nullptr;
 
     if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
         return nullptr;
-    UniqueChars locale = intl::EncodeLocale(cx, value.toString());
+    JSAutoByteString locale(cx, value.toString());
     if (!locale)
         return nullptr;
 
     // UNumberFormat options with default values
     UNumberFormatStyle uStyle = UNUM_DECIMAL;
     const UChar* uCurrency = nullptr;
     uint32_t uMinimumIntegerDigits = 1;
     uint32_t uMinimumFractionDigits = 0;
@@ -343,17 +342,17 @@ NewUNumberFormat(JSContext* cx, Handle<N
         uMaximumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
     }
 
     if (!GetProperty(cx, internals, internals, cx->names().useGrouping, &value))
         return nullptr;
     uUseGrouping = value.toBoolean();
 
     UErrorCode status = U_ZERO_ERROR;
-    UNumberFormat* nf = unum_open(uStyle, nullptr, 0, IcuLocale(locale.get()), nullptr, &status);
+    UNumberFormat* nf = unum_open(uStyle, nullptr, 0, IcuLocale(locale.ptr()), nullptr, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return nullptr;
     }
     ScopedICUObject<UNumberFormat, unum_close> toClose(nf);
 
     if (uCurrency) {
         unum_setTextAttribute(nf, UNUM_CURRENCY_CODE, uCurrency, 3, &status);
--- a/js/src/builtin/intl/PluralRules.cpp
+++ b/js/src/builtin/intl/PluralRules.cpp
@@ -10,17 +10,17 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "gc/FreeOp.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
@@ -191,17 +191,17 @@ NewUNumberFormatForPluralRules(JSContext
     RootedObject internals(cx, intl::GetInternalsObject(cx, pluralRules));
     if (!internals)
        return nullptr;
 
     RootedValue value(cx);
 
     if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
         return nullptr;
-    UniqueChars locale = intl::EncodeLocale(cx, value.toString());
+    JSAutoByteString locale(cx, value.toString());
     if (!locale)
         return nullptr;
 
     uint32_t uMinimumIntegerDigits = 1;
     uint32_t uMinimumFractionDigits = 0;
     uint32_t uMaximumFractionDigits = 3;
     int32_t uMinimumSignificantDigits = -1;
     int32_t uMaximumSignificantDigits = -1;
@@ -229,17 +229,17 @@ NewUNumberFormatForPluralRules(JSContext
 
         if (!GetProperty(cx, internals, internals, cx->names().maximumFractionDigits, &value))
             return nullptr;
         uMaximumFractionDigits = AssertedCast<uint32_t>(value.toInt32());
     }
 
     UErrorCode status = U_ZERO_ERROR;
     UNumberFormat* nf =
-        unum_open(UNUM_DECIMAL, nullptr, 0, IcuLocale(locale.get()), nullptr, &status);
+        unum_open(UNUM_DECIMAL, nullptr, 0, IcuLocale(locale.ptr()), nullptr, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return nullptr;
     }
     ScopedICUObject<UNumberFormat, unum_close> toClose(nf);
 
     if (uMinimumSignificantDigits != -1) {
         unum_setAttribute(nf, UNUM_SIGNIFICANT_DIGITS_USED, true);
@@ -264,17 +264,17 @@ NewUPluralRules(JSContext* cx, Handle<Pl
     RootedObject internals(cx, intl::GetInternalsObject(cx, pluralRules));
     if (!internals)
         return nullptr;
 
     RootedValue value(cx);
 
     if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
         return nullptr;
-    UniqueChars locale = intl::EncodeLocale(cx, value.toString());
+    JSAutoByteString locale(cx, value.toString());
     if (!locale)
         return nullptr;
 
     if (!GetProperty(cx, internals, internals, cx->names().type, &value))
         return nullptr;
 
     UPluralType category;
     {
@@ -286,17 +286,17 @@ NewUPluralRules(JSContext* cx, Handle<Pl
             category = UPLURAL_TYPE_CARDINAL;
         } else {
             MOZ_ASSERT(StringEqualsAscii(type, "ordinal"));
             category = UPLURAL_TYPE_ORDINAL;
         }
     }
 
     UErrorCode status = U_ZERO_ERROR;
-    UPluralRules* pr = uplrules_openForType(IcuLocale(locale.get()), category, &status);
+    UPluralRules* pr = uplrules_openForType(IcuLocale(locale.ptr()), category, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return nullptr;
     }
     return pr;
 }
 
 bool
--- a/js/src/builtin/intl/RelativeTimeFormat.cpp
+++ b/js/src/builtin/intl/RelativeTimeFormat.cpp
@@ -10,17 +10,17 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/FloatingPoint.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "gc/FreeOp.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using mozilla::IsNegativeZero;
@@ -219,17 +219,17 @@ NewURelativeDateTimeFormatter(JSContext*
     RootedObject internals(cx, intl::GetInternalsObject(cx, relativeTimeFormat));
     if (!internals)
         return nullptr;
 
     RootedValue value(cx);
 
     if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
         return nullptr;
-    UniqueChars locale = intl::EncodeLocale(cx, value.toString());
+    JSAutoByteString locale(cx, value.toString());
     if (!locale)
         return nullptr;
 
     if (!GetProperty(cx, internals, internals, cx->names().style, &value))
         return nullptr;
 
     UDateRelativeDateTimeFormatterStyle relDateTimeStyle;
     {
@@ -244,17 +244,17 @@ NewURelativeDateTimeFormatter(JSContext*
         } else {
             MOZ_ASSERT(StringEqualsAscii(style, "long"));
             relDateTimeStyle = UDAT_STYLE_LONG;
         }
     }
 
     UErrorCode status = U_ZERO_ERROR;
     URelativeDateTimeFormatter* rtf =
-        ureldatefmt_open(IcuLocale(locale.get()), nullptr, relDateTimeStyle,
+        ureldatefmt_open(IcuLocale(locale.ptr()), nullptr, relDateTimeStyle,
                          UDISPCTX_CAPITALIZATION_FOR_STANDALONE, &status);
     if (U_FAILURE(status)) {
         intl::ReportInternalError(cx);
         return nullptr;
     }
     return rtf;
 }
 
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -34,20 +34,19 @@
 #include "jsexn.h"
 #include "jsnum.h"
 
 #include "builtin/TypedObject.h"
 #include "ctypes/Library.h"
 #include "gc/FreeOp.h"
 #include "gc/Policy.h"
 #include "jit/AtomicOperations.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/StableStringChars.h"
 #include "js/UniquePtr.h"
-#include "js/Utility.h"
 #include "js/Vector.h"
 #include "util/Windows.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 
 #include "vm/JSObject-inl.h"
 
 using namespace std;
@@ -977,32 +976,31 @@ static const JSErrorFormatString ErrorFo
 static const JSErrorFormatString*
 GetErrorMessage(void* userRef, const unsigned errorNumber)
 {
   if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT)
     return &ErrorFormatString[errorNumber];
   return nullptr;
 }
 
-static JS::UniqueChars
-EncodeLatin1(JSContext* cx, AutoString& str)
-{
-  return JS_EncodeStringToLatin1(cx, NewUCString(cx, str.finish()));
+static const char*
+EncodeLatin1(JSContext* cx, AutoString& str, JSAutoByteString& bytes)
+{
+  return bytes.encodeLatin1(cx, NewUCString(cx, str.finish()));
 }
 
 static const char*
-CTypesToSourceForError(JSContext* cx, HandleValue val, JS::UniqueChars& bytes)
+CTypesToSourceForError(JSContext* cx, HandleValue val, JSAutoByteString& bytes)
 {
   if (val.isObject()) {
       RootedObject obj(cx, &val.toObject());
       if (CType::IsCType(obj) || CData::IsCDataMaybeUnwrap(&obj)) {
           RootedValue v(cx, ObjectValue(*obj));
           RootedString str(cx, JS_ValueToSource(cx, v));
-          bytes = JS_EncodeStringToLatin1(cx, str);
-          return bytes.get();
+          return bytes.encodeLatin1(cx, str);
       }
   }
   return ValueToSourceForError(cx, val, bytes);
 }
 
 static void
 BuildCStyleFunctionTypeSource(JSContext* cx, HandleObject typeObj,
                               HandleString nameStr, unsigned ptrCount,
@@ -1176,130 +1174,138 @@ BuildTypeSource(JSContext* cx, JSObject*
                 AutoString& result);
 
 static bool
 ConvError(JSContext* cx, const char* expectedStr, HandleValue actual,
           ConversionType convType,
           HandleObject funObj = nullptr, unsigned argIndex = 0,
           HandleObject arrObj = nullptr, unsigned arrIndex = 0)
 {
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
   if (arrObj) {
     MOZ_ASSERT(CType::IsCType(arrObj));
 
     switch (CType::GetTypeCode(arrObj)) {
     case TYPE_array: {
       MOZ_ASSERT(!funObj);
 
       char indexStr[16];
       SprintfLiteral(indexStr, "%u", arrIndex);
 
       AutoString arrSource;
+      JSAutoByteString arrBytes;
       BuildTypeSource(cx, arrObj, true, arrSource);
       if (!arrSource)
           return false;
-      JS::UniqueChars arrStr = EncodeLatin1(cx, arrSource);
+      const char* arrStr = EncodeLatin1(cx, arrSource, arrBytes);
       if (!arrStr)
         return false;
 
       JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                                  CTYPESMSG_CONV_ERROR_ARRAY,
-                                 valStr, indexStr, arrStr.get());
+                                 valStr, indexStr, arrStr);
       break;
     }
     case TYPE_struct: {
       JSFlatString* name = GetFieldName(arrObj, arrIndex);
       MOZ_ASSERT(name);
-      JS::UniqueChars nameStr = JS_EncodeStringToLatin1(cx, name);
+      JSAutoByteString nameBytes;
+      const char* nameStr = nameBytes.encodeLatin1(cx, name);
       if (!nameStr)
         return false;
 
       AutoString structSource;
+      JSAutoByteString structBytes;
       BuildTypeSource(cx, arrObj, true, structSource);
       if (!structSource)
           return false;
-      JS::UniqueChars structStr = EncodeLatin1(cx, structSource);
+      const char* structStr = EncodeLatin1(cx, structSource, structBytes);
       if (!structStr)
         return false;
 
-      JS::UniqueChars posStr;
+      JSAutoByteString posBytes;
+      const char* posStr;
       if (funObj) {
         AutoString posSource;
         BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
         if (!posSource)
             return false;
-        posStr = EncodeLatin1(cx, posSource);
+        posStr = EncodeLatin1(cx, posSource, posBytes);
         if (!posStr)
           return false;
+      } else {
+        posStr = "";
       }
 
       JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                                  CTYPESMSG_CONV_ERROR_STRUCT,
-                                 valStr, nameStr.get(), expectedStr, structStr.get(),
-                                 (posStr ? posStr.get() : ""));
+                                 valStr, nameStr, expectedStr, structStr, posStr);
       break;
     }
     default:
       MOZ_CRASH("invalid arrObj value");
     }
     return false;
   }
 
   switch (convType) {
   case ConversionType::Argument: {
     MOZ_ASSERT(funObj);
 
     char indexStr[16];
     SprintfLiteral(indexStr, "%u", argIndex + 1);
 
     AutoString funSource;
+    JSAutoByteString funBytes;
     BuildFunctionTypeSource(cx, funObj, funSource);
     if (!funSource)
         return false;
-    JS::UniqueChars funStr = EncodeLatin1(cx, funSource);
+    const char* funStr = EncodeLatin1(cx, funSource, funBytes);
     if (!funStr)
       return false;
 
     JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                                CTYPESMSG_CONV_ERROR_ARG,
-                               valStr, indexStr, funStr.get());
+                               valStr, indexStr, funStr);
     break;
   }
   case ConversionType::Finalizer: {
     MOZ_ASSERT(funObj);
 
     AutoString funSource;
+    JSAutoByteString funBytes;
     BuildFunctionTypeSource(cx, funObj, funSource);
     if (!funSource)
         return false;
-    JS::UniqueChars funStr = EncodeLatin1(cx, funSource);
+    const char* funStr = EncodeLatin1(cx, funSource, funBytes);
     if (!funStr)
       return false;
 
     JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                               CTYPESMSG_CONV_ERROR_FIN, valStr, funStr.get());
+                               CTYPESMSG_CONV_ERROR_FIN, valStr, funStr);
     break;
   }
   case ConversionType::Return: {
     MOZ_ASSERT(funObj);
 
     AutoString funSource;
+    JSAutoByteString funBytes;
     BuildFunctionTypeSource(cx, funObj, funSource);
     if (!funSource)
         return false;
-    JS::UniqueChars funStr = EncodeLatin1(cx, funSource);
+    const char* funStr = EncodeLatin1(cx, funSource, funBytes);
     if (!funStr)
       return false;
 
     JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                               CTYPESMSG_CONV_ERROR_RET, valStr, funStr.get());
+                               CTYPESMSG_CONV_ERROR_RET, valStr, funStr);
     break;
   }
   case ConversionType::Setter:
   case ConversionType::Construct:
     MOZ_ASSERT(!funObj);
 
     JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                                CTYPESMSG_CONV_ERROR_SET, valStr, expectedStr);
@@ -1313,32 +1319,33 @@ static bool
 ConvError(JSContext* cx, HandleObject expectedType, HandleValue actual,
           ConversionType convType,
           HandleObject funObj = nullptr, unsigned argIndex = 0,
           HandleObject arrObj = nullptr, unsigned arrIndex = 0)
 {
   MOZ_ASSERT(CType::IsCType(expectedType));
 
   AutoString expectedSource;
+  JSAutoByteString expectedBytes;
   BuildTypeSource(cx, expectedType, true, expectedSource);
   if (!expectedSource)
       return false;
-  JS::UniqueChars expectedStr = EncodeLatin1(cx, expectedSource);
+  const char* expectedStr = EncodeLatin1(cx, expectedSource, expectedBytes);
   if (!expectedStr)
     return false;
 
-  return ConvError(cx, expectedStr.get(), actual, convType, funObj, argIndex,
+  return ConvError(cx, expectedStr, actual, convType, funObj, argIndex,
                    arrObj, arrIndex);
 }
 
 static bool
 ArgumentConvError(JSContext* cx, HandleValue actual, const char* funStr,
                   unsigned argIndex)
 {
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
   char indexStr[16];
   SprintfLiteral(indexStr, "%u", argIndex + 1);
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
@@ -1357,68 +1364,70 @@ ArgumentLengthError(JSContext* cx, const
 
 static bool
 ArrayLengthMismatch(JSContext* cx, unsigned expectedLength, HandleObject arrObj,
                     unsigned actualLength, HandleValue actual,
                     ConversionType convType)
 {
   MOZ_ASSERT(arrObj && CType::IsCType(arrObj));
 
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
   char expectedLengthStr[16];
   SprintfLiteral(expectedLengthStr, "%u", expectedLength);
   char actualLengthStr[16];
   SprintfLiteral(actualLengthStr, "%u", actualLength);
 
   AutoString arrSource;
+  JSAutoByteString arrBytes;
   BuildTypeSource(cx, arrObj, true, arrSource);
   if (!arrSource)
       return false;
-  JS::UniqueChars arrStr = EncodeLatin1(cx, arrSource);
+  const char* arrStr = EncodeLatin1(cx, arrSource, arrBytes);
   if (!arrStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_ARRAY_MISMATCH,
-                             valStr, arrStr.get(), expectedLengthStr, actualLengthStr);
+                             valStr, arrStr, expectedLengthStr, actualLengthStr);
   return false;
 }
 
 static bool
 ArrayLengthOverflow(JSContext* cx, unsigned expectedLength, HandleObject arrObj,
                     unsigned actualLength, HandleValue actual,
                     ConversionType convType)
 {
   MOZ_ASSERT(arrObj && CType::IsCType(arrObj));
 
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
   char expectedLengthStr[16];
   SprintfLiteral(expectedLengthStr, "%u", expectedLength);
   char actualLengthStr[16];
   SprintfLiteral(actualLengthStr, "%u", actualLength);
 
   AutoString arrSource;
+  JSAutoByteString arrBytes;
   BuildTypeSource(cx, arrObj, true, arrSource);
   if (!arrSource)
       return false;
-  JS::UniqueChars arrStr = EncodeLatin1(cx, arrSource);
+  const char* arrStr = EncodeLatin1(cx, arrSource, arrBytes);
   if (!arrStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_ARRAY_OVERFLOW,
-                             valStr, arrStr.get(), expectedLengthStr, actualLengthStr);
+                             valStr, arrStr, expectedLengthStr, actualLengthStr);
   return false;
 }
 
 static bool
 ArgumentRangeMismatch(JSContext* cx, const char* func, const char* range)
 {
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                             CTYPESMSG_ARG_RANGE_MISMATCH, func, range);
@@ -1440,288 +1449,301 @@ CannotConstructError(JSContext* cx, cons
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                             CTYPESMSG_CANNOT_CONSTRUCT, type);
   return false;
 }
 
 static bool
 DuplicateFieldError(JSContext* cx, Handle<JSFlatString*> name)
 {
-  JS::UniqueChars nameStr = JS_EncodeStringToLatin1(cx, name);
+  JSAutoByteString nameBytes;
+  const char* nameStr = nameBytes.encodeLatin1(cx, name);
   if (!nameStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_DUPLICATE_FIELD, nameStr.get());
+                             CTYPESMSG_DUPLICATE_FIELD, nameStr);
   return false;
 }
 
 static bool
 EmptyFinalizerCallError(JSContext* cx, const char* funName)
 {
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                             CTYPESMSG_EMPTY_FIN_CALL, funName);
   return false;
 }
 
 static bool
 EmptyFinalizerError(JSContext* cx, ConversionType convType,
                     HandleObject funObj = nullptr, unsigned argIndex = 0)
 {
-  JS::UniqueChars posStr;
+  JSAutoByteString posBytes;
+  const char* posStr;
   if (funObj) {
     AutoString posSource;
     BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
     if (!posSource)
         return false;
-    posStr = EncodeLatin1(cx, posSource);
+    posStr = EncodeLatin1(cx, posSource, posBytes);
     if (!posStr)
       return false;
+  } else {
+    posStr = "";
   }
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_EMPTY_FIN, (posStr ? posStr.get() : ""));
+                             CTYPESMSG_EMPTY_FIN, posStr);
   return false;
 }
 
 static bool
 FieldCountMismatch(JSContext* cx,
                    unsigned expectedCount, HandleObject structObj,
                    unsigned actualCount, HandleValue actual,
                    ConversionType convType,
                    HandleObject funObj = nullptr, unsigned argIndex = 0)
 {
   MOZ_ASSERT(structObj && CType::IsCType(structObj));
 
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
   AutoString structSource;
+  JSAutoByteString structBytes;
   BuildTypeSource(cx, structObj, true, structSource);
   if (!structSource)
       return false;
-  JS::UniqueChars structStr = EncodeLatin1(cx, structSource);
+  const char* structStr = EncodeLatin1(cx, structSource, structBytes);
   if (!structStr)
     return false;
 
   char expectedCountStr[16];
   SprintfLiteral(expectedCountStr, "%u", expectedCount);
   char actualCountStr[16];
   SprintfLiteral(actualCountStr, "%u", actualCount);
 
-  JS::UniqueChars posStr;
+  JSAutoByteString posBytes;
+  const char* posStr;
   if (funObj) {
     AutoString posSource;
     BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
     if (!posSource)
         return false;
-    posStr = EncodeLatin1(cx, posSource);
+    posStr = EncodeLatin1(cx, posSource, posBytes);
     if (!posStr)
       return false;
+  } else {
+    posStr = "";
   }
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_FIELD_MISMATCH,
-                             valStr, structStr.get(), expectedCountStr, actualCountStr,
-                             (posStr ? posStr.get() : ""));
+                             valStr, structStr, expectedCountStr, actualCountStr,
+                             posStr);
   return false;
 }
 
 static bool
 FieldDescriptorCountError(JSContext* cx, HandleValue typeVal, size_t length)
 {
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
   if (!valStr)
     return false;
 
   char lengthStr[16];
   SprintfLiteral(lengthStr, "%zu", length);
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_FIELD_DESC_COUNT, valStr, lengthStr);
   return false;
 }
 
 static bool
 FieldDescriptorNameError(JSContext* cx, HandleId id)
 {
-  JS::UniqueChars idBytes;
+  JSAutoByteString idBytes;
   RootedValue idVal(cx, IdToValue(id));
   const char* propStr = CTypesToSourceForError(cx, idVal, idBytes);
   if (!propStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_FIELD_DESC_NAME, propStr);
   return false;
 }
 
 static bool
 FieldDescriptorSizeError(JSContext* cx, HandleObject typeObj, HandleId id)
 {
   RootedValue typeVal(cx, ObjectValue(*typeObj));
-  JS::UniqueChars typeBytes;
+  JSAutoByteString typeBytes;
   const char* typeStr = CTypesToSourceForError(cx, typeVal, typeBytes);
   if (!typeStr)
     return false;
 
   RootedString idStr(cx, IdToString(cx, id));
-  JS::UniqueChars propStr = JS_EncodeStringToLatin1(cx, idStr);
+  JSAutoByteString idBytes;
+  const char* propStr = idBytes.encodeLatin1(cx, idStr);
   if (!propStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_FIELD_DESC_SIZE, typeStr, propStr.get());
+                             CTYPESMSG_FIELD_DESC_SIZE, typeStr, propStr);
   return false;
 }
 
 static bool
 FieldDescriptorNameTypeError(JSContext* cx, HandleValue typeVal)
 {
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_FIELD_DESC_NAMETYPE, valStr);
   return false;
 }
 
 static bool
 FieldDescriptorTypeError(JSContext* cx, HandleValue poroVal, HandleId id)
 {
-  JS::UniqueChars typeBytes;
+  JSAutoByteString typeBytes;
   const char* typeStr = CTypesToSourceForError(cx, poroVal, typeBytes);
   if (!typeStr)
     return false;
 
   RootedString idStr(cx, IdToString(cx, id));
-  JS::UniqueChars propStr = JS_EncodeStringToLatin1(cx, idStr);
+  JSAutoByteString idBytes;
+  const char* propStr = idBytes.encodeLatin1(cx, idStr);
   if (!propStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_FIELD_DESC_TYPE, typeStr, propStr.get());
+                             CTYPESMSG_FIELD_DESC_TYPE, typeStr, propStr);
   return false;
 }
 
 static bool
 FieldMissingError(JSContext* cx, JSObject* typeObj, JSFlatString* name_)
 {
-  JS::UniqueChars typeBytes;
+  JSAutoByteString typeBytes;
   RootedString name(cx, name_);
   RootedValue typeVal(cx, ObjectValue(*typeObj));
   const char* typeStr = CTypesToSourceForError(cx, typeVal, typeBytes);
   if (!typeStr)
     return false;
 
-  JS::UniqueChars nameStr = JS_EncodeStringToLatin1(cx, name);
+  JSAutoByteString nameBytes;
+  const char* nameStr = nameBytes.encodeLatin1(cx, name);
   if (!nameStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_FIELD_MISSING, typeStr, nameStr.get());
+                             CTYPESMSG_FIELD_MISSING, typeStr, nameStr);
   return false;
 }
 
 static bool
 FinalizerSizeError(JSContext* cx, HandleObject funObj, HandleValue actual)
 {
   MOZ_ASSERT(CType::IsCType(funObj));
 
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
   AutoString funSource;
+  JSAutoByteString funBytes;
   BuildFunctionTypeSource(cx, funObj, funSource);
   if (!funSource)
       return false;
-  JS::UniqueChars funStr = EncodeLatin1(cx, funSource);
+  const char* funStr = EncodeLatin1(cx, funSource, funBytes);
   if (!funStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_FIN_SIZE_ERROR, funStr.get(), valStr);
+                             CTYPESMSG_FIN_SIZE_ERROR, funStr, valStr);
   return false;
 }
 
 static bool
 FunctionArgumentLengthMismatch(JSContext* cx,
                                unsigned expectedCount, unsigned actualCount,
                                HandleObject funObj, HandleObject typeObj,
                                bool isVariadic)
 {
   AutoString funSource;
+  JSAutoByteString funBytes;
   Value slot = JS_GetReservedSlot(funObj, SLOT_REFERENT);
   if (!slot.isUndefined() && Library::IsLibrary(&slot.toObject())) {
     BuildFunctionTypeSource(cx, funObj, funSource);
   } else {
     BuildFunctionTypeSource(cx, typeObj, funSource);
   }
   if (!funSource)
       return false;
-  JS::UniqueChars funStr = EncodeLatin1(cx, funSource);
+  const char* funStr = EncodeLatin1(cx, funSource, funBytes);
   if (!funStr)
     return false;
 
   char expectedCountStr[16];
   SprintfLiteral(expectedCountStr, "%u", expectedCount);
   char actualCountStr[16];
   SprintfLiteral(actualCountStr, "%u", actualCount);
 
   const char* variadicStr = isVariadic ? " or more": "";
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_ARG_COUNT_MISMATCH,
-                             funStr.get(), expectedCountStr, variadicStr,
+                             funStr, expectedCountStr, variadicStr,
                              actualCountStr);
   return false;
 }
 
 static bool
 FunctionArgumentTypeError(JSContext* cx,
                           uint32_t index, HandleValue typeVal, const char* reason)
 {
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
   if (!valStr)
     return false;
 
   char indexStr[16];
   SprintfLiteral(indexStr, "%u", index + 1);
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_ARG_TYPE_ERROR,
                              indexStr, reason, valStr);
   return false;
 }
 
 static bool
 FunctionReturnTypeError(JSContext* cx, HandleValue type, const char* reason)
 {
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, type, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_RET_TYPE_ERROR, reason, valStr);
   return false;
 }
 
 static bool
 IncompatibleCallee(JSContext* cx, const char* funName, HandleObject actualObj)
 {
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   RootedValue val(cx, ObjectValue(*actualObj));
   const char* valStr = CTypesToSourceForError(cx, val, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_INCOMPATIBLE_CALLEE, funName, valStr);
   return false;
@@ -1735,17 +1757,17 @@ IncompatibleThisProto(JSContext* cx, con
                              CTYPESMSG_INCOMPATIBLE_THIS,
                              funName, actualType);
   return false;
 }
 
 static bool
 IncompatibleThisProto(JSContext* cx, const char* funName, HandleValue actualVal)
 {
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, actualVal, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_INCOMPATIBLE_THIS_VAL,
                              funName, "incompatible object", valStr);
   return false;
@@ -1759,31 +1781,31 @@ IncompatibleThisType(JSContext* cx, cons
                             funName, actualType);
   return false;
 }
 
 static bool
 IncompatibleThisType(JSContext* cx, const char* funName, const char* actualType,
                      HandleValue actualVal)
 {
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, actualVal, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_INCOMPATIBLE_THIS_VAL,
                              funName, actualType, valStr);
   return false;
 }
 
 static bool
 InvalidIndexError(JSContext* cx, HandleValue val)
 {
-  JS::UniqueChars idBytes;
+  JSAutoByteString idBytes;
   const char* indexStr = CTypesToSourceForError(cx, val, idBytes);
   if (!indexStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_INVALID_INDEX, indexStr);
   return false;
 }
@@ -1810,189 +1832,195 @@ InvalidIndexRangeError(JSContext* cx, si
 }
 
 static bool
 NonPrimitiveError(JSContext* cx, HandleObject typeObj)
 {
   MOZ_ASSERT(CType::IsCType(typeObj));
 
   AutoString typeSource;
+  JSAutoByteString typeBytes;
   BuildTypeSource(cx, typeObj, true, typeSource);
   if (!typeSource)
       return false;
-  JS::UniqueChars typeStr = EncodeLatin1(cx, typeSource);
+  const char* typeStr = EncodeLatin1(cx, typeSource, typeBytes);
   if (!typeStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_NON_PRIMITIVE, typeStr.get());
+                             CTYPESMSG_NON_PRIMITIVE, typeStr);
   return false;
 }
 
 static bool
 NonStringBaseError(JSContext* cx, HandleValue thisVal)
 {
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, thisVal, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_NON_STRING_BASE, valStr);
   return false;
 }
 
 static bool
 NullPointerError(JSContext* cx, const char* action, HandleObject obj)
 {
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   RootedValue val(cx, ObjectValue(*obj));
   const char* valStr = CTypesToSourceForError(cx, val, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_NULL_POINTER, action, valStr);
   return false;
 }
 
 static bool
 PropNameNonStringError(JSContext* cx, HandleId id, HandleValue actual,
                        ConversionType convType,
                        HandleObject funObj = nullptr, unsigned argIndex = 0)
 {
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
-  JS::UniqueChars idBytes;
+  JSAutoByteString idBytes;
   RootedValue idVal(cx, IdToValue(id));
   const char* propStr = CTypesToSourceForError(cx, idVal, idBytes);
   if (!propStr)
     return false;
 
-  JS::UniqueChars posStr;
+  JSAutoByteString posBytes;
+  const char* posStr;
   if (funObj) {
     AutoString posSource;
     BuildConversionPosition(cx, convType, funObj, argIndex, posSource);
     if (!posSource)
         return false;
-    posStr = EncodeLatin1(cx, posSource);
+    posStr = EncodeLatin1(cx, posSource, posBytes);
     if (!posStr)
       return false;
+  } else {
+    posStr = "";
   }
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_PROP_NONSTRING, propStr, valStr,
-                             (posStr ? posStr.get() : ""));
+                             CTYPESMSG_PROP_NONSTRING, propStr, valStr, posStr);
   return false;
 }
 
 static bool
 SizeOverflow(JSContext* cx, const char* name, const char* limit)
 {
   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                             CTYPESMSG_SIZE_OVERFLOW, name, limit);
   return false;
 }
 
 static bool
 TypeError(JSContext* cx, const char* expected, HandleValue actual)
 {
-  JS::UniqueChars bytes;
+  JSAutoByteString bytes;
   const char* src = CTypesToSourceForError(cx, actual, bytes);
   if (!src)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_TYPE_ERROR, expected, src);
   return false;
 }
 
 static bool
 TypeOverflow(JSContext* cx, const char* expected, HandleValue actual)
 {
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_TYPE_OVERFLOW, valStr, expected);
   return false;
 }
 
 static bool
 UndefinedSizeCastError(JSContext* cx, HandleObject targetTypeObj)
 {
   AutoString targetTypeSource;
+  JSAutoByteString targetTypeBytes;
   BuildTypeSource(cx, targetTypeObj, true, targetTypeSource);
   if (!targetTypeSource)
       return false;
-  JS::UniqueChars targetTypeStr = EncodeLatin1(cx, targetTypeSource);
+  const char* targetTypeStr = EncodeLatin1(cx, targetTypeSource, targetTypeBytes);
   if (!targetTypeStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
-                             CTYPESMSG_UNDEFINED_SIZE_CAST, targetTypeStr.get());
+                             CTYPESMSG_UNDEFINED_SIZE_CAST, targetTypeStr);
   return false;
 }
 
 static bool
 SizeMismatchCastError(JSContext* cx,
                       HandleObject sourceTypeObj, HandleObject targetTypeObj,
                       size_t sourceSize, size_t targetSize)
 {
   AutoString sourceTypeSource;
+  JSAutoByteString sourceTypeBytes;
   BuildTypeSource(cx, sourceTypeObj, true, sourceTypeSource);
   if (!sourceTypeSource)
       return false;
-  JS::UniqueChars sourceTypeStr = EncodeLatin1(cx, sourceTypeSource);
+  const char* sourceTypeStr = EncodeLatin1(cx, sourceTypeSource, sourceTypeBytes);
   if (!sourceTypeStr)
     return false;
 
   AutoString targetTypeSource;
+  JSAutoByteString targetTypeBytes;
   BuildTypeSource(cx, targetTypeObj, true, targetTypeSource);
   if (!targetTypeSource)
       return false;
-  JS::UniqueChars targetTypeStr = EncodeLatin1(cx, targetTypeSource);
+  const char* targetTypeStr = EncodeLatin1(cx, targetTypeSource, targetTypeBytes);
   if (!targetTypeStr)
     return false;
 
   char sourceSizeStr[16];
   char targetSizeStr[16];
   SprintfLiteral(sourceSizeStr, "%zu", sourceSize);
   SprintfLiteral(targetSizeStr, "%zu", targetSize);
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_SIZE_MISMATCH_CAST,
-                             targetTypeStr.get(), sourceTypeStr.get(),
+                             targetTypeStr, sourceTypeStr,
                              targetSizeStr, sourceSizeStr);
   return false;
 }
 
 static bool
 UndefinedSizePointerError(JSContext* cx, const char* action, HandleObject obj)
 {
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   RootedValue val(cx, ObjectValue(*obj));
   const char* valStr = CTypesToSourceForError(cx, val, valBytes);
   if (!valStr)
     return false;
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
                              CTYPESMSG_UNDEFINED_SIZE, action, valStr);
   return false;
 }
 
 static bool
 VariadicArgumentTypeError(JSContext* cx, uint32_t index, HandleValue actual)
 {
-  JS::UniqueChars valBytes;
+  JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
 
   char indexStr[16];
   SprintfLiteral(indexStr, "%u", index + 1);
 
   JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ctypes/Library.h"
 
 #include "prerror.h"
 #include "prlink.h"
 
 #include "ctypes/CTypes.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/StableStringChars.h"
 
 using JS::AutoStableStringChars;
 
 namespace js {
 namespace ctypes {
 
 /*******************************************************************************
@@ -164,21 +164,23 @@ Library::Create(JSContext* cx, HandleVal
 #define MAX_ERROR_LEN 1024
     char error[MAX_ERROR_LEN] = "Cannot get error from NSPR.";
     uint32_t errorLen = PR_GetErrorTextLength();
     if (errorLen && errorLen < MAX_ERROR_LEN)
       PR_GetErrorText(error);
 #undef MAX_ERROR_LEN
 
     if (JS::StringIsASCII(error)) {
-      if (JS::UniqueChars pathCharsUTF8 = JS_EncodeStringToUTF8(cx, pathStr))
-        JS_ReportErrorUTF8(cx, "couldn't open library %s: %s", pathCharsUTF8.get(), error);
+      JSAutoByteString pathCharsUTF8;
+      if (pathCharsUTF8.encodeUtf8(cx, pathStr))
+        JS_ReportErrorUTF8(cx, "couldn't open library %s: %s", pathCharsUTF8.ptr(), error);
     } else {
-      if (JS::UniqueChars pathCharsLatin1 = JS_EncodeStringToLatin1(cx, pathStr))
-        JS_ReportErrorLatin1(cx, "couldn't open library %s: %s", pathCharsLatin1.get(), error);
+      JSAutoByteString pathCharsLatin1;
+      if (pathCharsLatin1.encodeLatin1(cx, pathStr))
+        JS_ReportErrorLatin1(cx, "couldn't open library %s: %s", pathCharsLatin1.ptr(), error);
     }
     return nullptr;
   }
 
   // stash the library
   JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PrivateValue(library));
 
   return libraryObj;
--- a/js/src/frontend/EmitterScope.cpp
+++ b/js/src/frontend/EmitterScope.cpp
@@ -3,16 +3,17 @@
  * 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 "frontend/EmitterScope.h"
 
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/TDZCheckCache.h"
+#include "js/AutoByteString.h"
 
 #include "vm/GlobalObject.h"
 
 using namespace js;
 using namespace js::frontend;
 
 using mozilla::DebugOnly;
 using mozilla::Maybe;
@@ -394,23 +395,23 @@ EmitterScope::deadZoneFrameSlotRange(Byt
 void
 EmitterScope::dump(BytecodeEmitter* bce)
 {
     fprintf(stdout, "EmitterScope [%s] %p\n", ScopeKindString(scope(bce)->kind()), this);
 
     for (NameLocationMap::Range r = nameCache_->all(); !r.empty(); r.popFront()) {
         const NameLocation& l = r.front().value();
 
-        UniqueChars bytes = AtomToPrintableString(bce->cx, r.front().key());
-        if (!bytes)
+        JSAutoByteString bytes;
+        if (!AtomToPrintableString(bce->cx, r.front().key(), &bytes))
             return;
         if (l.kind() != NameLocation::Kind::Dynamic)
-            fprintf(stdout, "  %s %s ", BindingKindString(l.bindingKind()), bytes.get());
+            fprintf(stdout, "  %s %s ", BindingKindString(l.bindingKind()), bytes.ptr());
         else
-            fprintf(stdout, "  %s ", bytes.get());
+            fprintf(stdout, "  %s ", bytes.ptr());
 
         switch (l.kind()) {
           case NameLocation::Kind::Dynamic:
             fprintf(stdout, "dynamic\n");
             break;
           case NameLocation::Kind::Global:
             fprintf(stdout, "global\n");
             break;
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -45,21 +45,18 @@ class NameResolver
      * given code like a["b c"], the front end will produce a ParseNodeKind::Dot
      * with a ParseNodeKind::Name child whose name contains spaces.
      */
     bool appendPropertyReference(JSAtom* name) {
         if (IsIdentifier(name))
             return buf->append('.') && buf->append(name);
 
         /* Quote the string as needed. */
-        UniqueChars source = QuoteString(cx, name, '"');
-        return source &&
-               buf->append('[') &&
-               buf->append(source.get(), strlen(source.get())) &&
-               buf->append(']');
+        JSString* source = QuoteString(cx, name, '"');
+        return source && buf->append('[') && buf->append(source) && buf->append(']');
     }
 
     /* Append a number to buf. */
     bool appendNumber(double n) {
         char number[30];
         int digits = SprintfLiteral(number, "%g", n);
         return buf->append(number, digits);
     }
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -32,16 +32,17 @@
 #include "jstypes.h"
 
 #include "builtin/ModuleObject.h"
 #include "builtin/SelfHostingDefines.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FoldConstants.h"
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpParser.h"
+#include "js/AutoByteString.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"
@@ -165,23 +166,23 @@ void
 ParseContext::Scope::dump(ParseContext* pc)
 {
     JSContext* cx = pc->sc()->context;
 
     fprintf(stdout, "ParseScope %p", this);
 
     fprintf(stdout, "\n  decls:\n");
     for (DeclaredNameMap::Range r = declared_->all(); !r.empty(); r.popFront()) {
-        UniqueChars bytes = AtomToPrintableString(cx, r.front().key());
-        if (!bytes)
+        JSAutoByteString bytes;
+        if (!AtomToPrintableString(cx, r.front().key(), &bytes))
             return;
         DeclaredNameInfo& info = r.front().value().wrapped;
         fprintf(stdout, "    %s %s%s\n",
                 DeclarationKindString(info.kind()),
-                bytes.get(),
+                bytes.ptr(),
                 info.closedOver() ? " (closed over)" : "");
     }
 
     fprintf(stdout, "\n");
 }
 
 bool
 ParseContext::Scope::addPossibleAnnexBFunctionBox(ParseContext* pc, FunctionBox* funbox)
@@ -1221,22 +1222,22 @@ GeneralParser<ParseHandler, CharT>::repo
 }
 
 template <class ParseHandler, typename CharT>
 void
 GeneralParser<ParseHandler, CharT>::reportRedeclaration(HandlePropertyName name,
                                                         DeclarationKind prevKind,
                                                         TokenPos pos, uint32_t prevPos)
 {
-    UniqueChars bytes = AtomToPrintableString(context, name);
-    if (!bytes)
+    JSAutoByteString bytes;
+    if (!AtomToPrintableString(context, name, &bytes))
         return;
 
     if (prevPos == DeclaredNameInfo::npos) {
-        errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(prevKind), bytes.get());
+        errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(prevKind), bytes.ptr());
         return;
     }
 
     auto notes = MakeUnique<JSErrorNotes>();
     if (!notes) {
         ReportOutOfMemory(pc->sc()->context);
         return;
     }
@@ -1255,17 +1256,17 @@ GeneralParser<ParseHandler, CharT>::repo
                              GetErrorMessage, nullptr,
                              JSMSG_REDECLARED_PREV,
                              lineNumber, columnNumber))
     {
         return;
     }
 
     errorWithNotesAt(std::move(notes), pos.begin, JSMSG_REDECLARED_VAR,
-                     DeclarationKindString(prevKind), bytes.get());
+                     DeclarationKindString(prevKind), bytes.ptr());
 }
 
 // notePositionalFormalParameter is called for both the arguments of a regular
 // function definition and the arguments specified by the Function
 // constructor.
 //
 // The 'disallowDuplicateParams' bool indicates whether the use of another
 // feature (destructuring or default arguments) disables duplicate arguments.
@@ -1285,20 +1286,20 @@ GeneralParser<ParseHandler, CharT>::note
             return false;
         }
 
         // Strict-mode disallows duplicate args. We may not know whether we are
         // in strict mode or not (since the function body hasn't been parsed).
         // In such cases, report will queue up the potential error and return
         // 'true'.
         if (pc->sc()->needStrictChecks()) {
-            UniqueChars bytes = AtomToPrintableString(context, name);
-            if (!bytes)
+            JSAutoByteString bytes;
+            if (!AtomToPrintableString(context, name, &bytes))
                 return false;
-            if (!strictModeError(JSMSG_DUPLICATE_FORMAL, bytes.get()))
+            if (!strictModeError(JSMSG_DUPLICATE_FORMAL, bytes.ptr()))
                 return false;
         }
 
         *duplicatedParam = true;
     } else {
         DeclarationKind kind = DeclarationKind::PositionalFormalParameter;
         if (!pc->functionScope().addDeclaredName(pc, p, name, kind, beginPos))
             return false;
@@ -2420,21 +2421,21 @@ Parser<FullParseHandler, CharT>::moduleB
 
     // Check exported local bindings exist and mark them as closed over.
     for (auto entry : modulesc->builder.localExportEntries()) {
         JSAtom* name = entry->localName();
         MOZ_ASSERT(name);
 
         DeclaredNamePtr p = modulepc.varScope().lookupDeclaredName(name);
         if (!p) {
-            UniqueChars str = AtomToPrintableString(context, name);
-            if (!str)
+            JSAutoByteString str;
+            if (!AtomToPrintableString(context, name, &str))
                 return null();
 
-            errorAt(TokenStream::NoOffset, JSMSG_MISSING_EXPORT, str.get());
+            errorAt(TokenStream::NoOffset, JSMSG_MISSING_EXPORT, str.ptr());
             return null();
         }
 
         p->value()->setClosedOver();
     }
 
     if (!FoldConstants(context, &pn, this))
         return null();
@@ -5458,21 +5459,21 @@ GeneralParser<ParseHandler, CharT>::impo
 
 template<typename CharT>
 bool
 Parser<FullParseHandler, CharT>::checkExportedName(JSAtom* exportName)
 {
     if (!pc->sc()->asModuleContext()->builder.hasExportedName(exportName))
         return true;
 
-    UniqueChars str = AtomToPrintableString(context, exportName);
-    if (!str)
+    JSAutoByteString str;
+    if (!AtomToPrintableString(context, exportName, &str))
         return false;
 
-    error(JSMSG_DUPLICATE_EXPORT_NAME, str.get());
+    error(JSMSG_DUPLICATE_EXPORT_NAME, str.ptr());
     return false;
 }
 
 template<typename CharT>
 inline bool
 Parser<SyntaxParseHandler, CharT>::checkExportedName(JSAtom* exportName)
 {
     MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -568,28 +568,28 @@ UniqueChars
 Statistics::renderJsonSlice(size_t sliceNum) const
 {
     Sprinter printer(nullptr, false);
     if (!printer.init())
         return UniqueChars(nullptr);
     JSONPrinter json(printer);
 
     formatJsonSlice(sliceNum, json);
-    return printer.release();
+    return UniqueChars(printer.release());
 }
 
 UniqueChars
 Statistics::renderNurseryJson(JSRuntime* rt) const
 {
     Sprinter printer(nullptr, false);
     if (!printer.init())
         return UniqueChars(nullptr);
     JSONPrinter json(printer);
     rt->gc.nursery().renderProfileJSON(json);
-    return printer.release();
+    return UniqueChars(printer.release());
 }
 
 #ifdef DEBUG
 void
 Statistics::writeLogMessage(const char* fmt, ...)
 {
     va_list args;
     va_start(args, fmt);
@@ -636,17 +636,17 @@ Statistics::renderJsonMessage(uint64_t t
     }
 
     json.beginObjectProperty("totals"); // #24
     formatJsonPhaseTimes(phaseTimes, json);
     json.endObject();
 
     json.endObject();
 
-    return printer.release();
+    return UniqueChars(printer.release());
 }
 
 void
 Statistics::formatJsonDescription(uint64_t timestamp, JSONPrinter& json) const
 {
     // If you change JSON properties here, please update:
     // Telemetry ping code:
     //   toolkit/components/telemetry/GCTelemetry.jsm
--- a/js/src/jit-test/tests/self-test/readlineBuf.js
+++ b/js/src/jit-test/tests/self-test/readlineBuf.js
@@ -4,33 +4,31 @@ assertThrowsInstanceOf(function () { rea
 
 var testBuffers = [
     "foo\nbar\nbaz\n",
     "foo\nbar\nbaz",
     "foo\n\nbar\nbaz",
     "f",
     "\n",
     "\nf",
-    "",
-    "Ää\n\u{10ffff}",
+    ""
 ];
 
 var expected = [
     [ "foo", "bar", "baz" ],
     [ "foo", "bar", "baz" ],
     [ "foo", "", "bar", "baz" ],
     [ "f" ],
     [ "" ],
     [ "", "f" ],
-    [],
-    ["Ää", "\u{10ffff}"],
+    []
 ];
 
-for (var [idx, testValue] of testBuffers.entries()) {
-    readlineBuf(testValue);
+for (var idx in testBuffers) {
+    readlineBuf(testBuffers[idx]);
     var result = [];
 
     while ((line = readlineBuf()) != null) {
         result.push(line);
     }
 
     assertDeepEq(result, expected[idx]);
 }
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -12,17 +12,17 @@
 
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "gc/GC.h"
 #include "js/AllocPolicy.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/Vector.h"
 #include "vm/JSContext.h"
 
 /* Note: Aborts on OOM. */
 class JSAPITestString {
     js::Vector<char, 0, js::SystemAllocPolicy> chars;
 
   public:
@@ -98,19 +98,21 @@ class JSAPITest
     // Like exec(), but doesn't call fail() if JS::Evaluate returns false.
     bool execDontReport(const char* bytes, const char* filename, int lineno);
 
 #define EVAL(s, vp) do { if (!evaluate(s, __FILE__, __LINE__, vp)) return false; } while (false)
 
     bool evaluate(const char* bytes, const char* filename, int lineno, JS::MutableHandleValue vp);
 
     JSAPITestString jsvalToSource(JS::HandleValue v) {
-        if (JSString* str = JS_ValueToSource(cx, v)) {
-            if (JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, str))
-                return JSAPITestString(bytes.get());
+        JSString* str = JS_ValueToSource(cx, v);
+        if (str) {
+            JSAutoByteString bytes(cx, str);
+            if (!!bytes)
+                return JSAPITestString(bytes.ptr());
         }
         JS_ClearPendingException(cx);
         return JSAPITestString("<<error converting value to string>>");
     }
 
     JSAPITestString toSource(long v) {
         char buf[40];
         sprintf(buf, "%ld", v);
@@ -226,18 +228,19 @@ class JSAPITest
 
         if (JS_IsExceptionPending(cx)) {
             js::gc::AutoSuppressGC gcoff(cx);
             JS::RootedValue v(cx);
             JS_GetPendingException(cx, &v);
             JS_ClearPendingException(cx);
             JSString* s = JS::ToString(cx, v);
             if (s) {
-                if (JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, s))
-                    message += bytes.get();
+                JSAutoByteString bytes(cx, s);
+                if (!!bytes)
+                    message += bytes.ptr();
             }
         }
 
         fprintf(stderr, "%.*s\n", int(message.length()), message.begin());
 
         if (msgs.length() != 0)
             msgs += " | ";
         msgs += message;
@@ -265,20 +268,21 @@ class JSAPITest
     print(JSContext* cx, unsigned argc, JS::Value* vp)
     {
         JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
 
         for (unsigned i = 0; i < args.length(); i++) {
             JSString* str = JS::ToString(cx, args[i]);
             if (!str)
                 return false;
-            JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, str);
+            char* bytes = JS_EncodeString(cx, str);
             if (!bytes)
                 return false;
-            printf("%s%s", i ? " " : "", bytes.get());
+            printf("%s%s", i ? " " : "", bytes);
+            JS_free(cx, bytes);
         }
 
         putchar('\n');
         fflush(stdout);
         args.rval().setUndefined();
         return true;
     }
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -50,16 +50,17 @@
 #include "frontend/Parser.h" // for JS_BufferIsCompileableUnit
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "gc/PublicIterators.h"
 #include "gc/WeakMap.h"
 #include "jit/JitCommon.h"
 #include "jit/JitSpewer.h"
+#include "js/AutoByteString.h"
 #include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/CompileOptions.h"
 #include "js/Conversions.h"
 #include "js/Date.h"
 #include "js/Initialization.h"
 #include "js/JSON.h"
 #include "js/LocaleSensitive.h"
@@ -170,38 +171,43 @@ JS::ObjectOpResult::reportStrictErrorOrW
     unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_WARNING | JSREPORT_STRICT);
     if (code_ == JSMSG_OBJECT_NOT_EXTENSIBLE) {
         RootedValue val(cx, ObjectValue(*obj));
         return ReportValueErrorFlags(cx, flags, code_, JSDVG_IGNORE_STACK, val,
                                      nullptr, nullptr, nullptr);
     }
 
     if (ErrorTakesArguments(code_)) {
-        UniqueChars propName = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
-        if (!propName)
+        RootedValue idv(cx, IdToValue(id));
+        RootedString str(cx, ValueToSource(cx, idv));
+        if (!str)
+            return false;
+
+        JSAutoByteString propName;
+        if (!propName.encodeUtf8(cx, str))
             return false;
 
         if (code_ == JSMSG_SET_NON_OBJECT_RECEIVER) {
             // We know that the original receiver was a primitive, so unbox it.
             RootedValue val(cx, ObjectValue(*obj));
             if (!obj->is<ProxyObject>()) {
                 if (!Unbox(cx, obj, &val))
                     return false;
             }
             return ReportValueErrorFlags(cx, flags, code_, JSDVG_IGNORE_STACK, val,
-                                         nullptr, propName.get(), nullptr);
+                                         nullptr, propName.ptr(), nullptr);
         }
 
         if (ErrorTakesObjectArgument(code_)) {
             return JS_ReportErrorFlagsAndNumberUTF8(cx, flags, GetErrorMessage, nullptr, code_,
-                                                    obj->getClass()->name, propName.get());
+                                                    obj->getClass()->name, propName.ptr());
         }
 
         return JS_ReportErrorFlagsAndNumberUTF8(cx, flags, GetErrorMessage, nullptr, code_,
-                                                propName.get());
+                                                propName.ptr());
     }
     return JS_ReportErrorFlagsAndNumberASCII(cx, flags, GetErrorMessage, nullptr, code_);
 }
 
 JS_PUBLIC_API(bool)
 JS::ObjectOpResult::reportStrictErrorOrWarning(JSContext* cx, HandleObject obj, bool strict)
 {
     MOZ_ASSERT(code_ != Uninitialized);
@@ -1638,17 +1644,17 @@ JS::GetFirstArgumentAsTypeHint(JSContext
 
     if (!EqualStrings(cx, str, cx->names().number, &match))
         return false;
     if (match) {
         *result = JSTYPE_NUMBER;
         return true;
     }
 
-    UniqueChars bytes;
+    JSAutoByteString bytes;
     const char* source = ValueToSourceForError(cx, args.get(0), bytes);
     if (!source) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
                                "Symbol.toPrimitive",
@@ -5055,28 +5061,30 @@ JS::GetPromiseResolutionSite(JS::HandleO
 }
 
 #ifdef DEBUG
 JS_PUBLIC_API(void)
 JS::DumpPromiseAllocationSite(JSContext* cx, JS::HandleObject promise)
 {
     RootedObject stack(cx, promise->as<PromiseObject>().allocationSite());
     JSPrincipals* principals = cx->realm()->principals();
-    UniqueChars stackStr = BuildUTF8StackString(cx, principals, stack);
-    if (stackStr)
+    UniqueChars stackStr(
+        reinterpret_cast<char*>(BuildUTF8StackString(cx, principals, stack).get()));
+    if (stackStr.get())
         fputs(stackStr.get(), stderr);
 }
 
 JS_PUBLIC_API(void)
 JS::DumpPromiseResolutionSite(JSContext* cx, JS::HandleObject promise)
 {
     RootedObject stack(cx, promise->as<PromiseObject>().resolutionSite());
     JSPrincipals* principals = cx->realm()->principals();
-    UniqueChars stackStr = BuildUTF8StackString(cx, principals, stack);
-    if (stackStr)
+    UniqueChars stackStr(
+        reinterpret_cast<char*>(BuildUTF8StackString(cx, principals, stack).get()));
+    if (stackStr.get())
         fputs(stackStr.get(), stderr);
 }
 #endif
 
 JS_PUBLIC_API(JSObject*)
 JS::CallOriginalPromiseResolve(JSContext* cx, JS::HandleValue resolutionValue)
 {
     AssertHeapIsIdle();
@@ -6042,32 +6050,32 @@ JS_DecodeBytes(JSContext* cx, const char
         return false;
     }
 
     CopyAndInflateChars(dst, src, srclen);
     *dstlenp = srclen;
     return true;
 }
 
-JS_PUBLIC_API(JS::UniqueChars)
-JS_EncodeStringToLatin1(JSContext* cx, JSString* str)
-{
-    AssertHeapIsIdle();
-    CHECK_THREAD(cx);
-
-    return js::EncodeLatin1(cx, str);
-}
-
-JS_PUBLIC_API(JS::UniqueChars)
+JS_PUBLIC_API(char*)
+JS_EncodeString(JSContext* cx, JSString* str)
+{
+    AssertHeapIsIdle();
+    CHECK_THREAD(cx);
+
+    return js::EncodeLatin1(cx, str).release();
+}
+
+JS_PUBLIC_API(char*)
 JS_EncodeStringToUTF8(JSContext* cx, HandleString str)
 {
     AssertHeapIsIdle();
     CHECK_THREAD(cx);
 
-    return StringToNewUTF8CharsZ(cx, *str);
+    return StringToNewUTF8CharsZ(cx, *str).release();
 }
 
 JS_PUBLIC_API(size_t)
 JS_GetStringEncodingLength(JSContext* cx, JSString* str)
 {
     AssertHeapIsIdle();
     CHECK_THREAD(cx);
 
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -18,16 +18,17 @@
 
 #include "jsapi.h"
 #include "jsnum.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
+#include "js/AutoByteString.h"
 #include "js/CharacterEncoding.h"
 #include "js/UniquePtr.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
 #include "vm/ErrorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
@@ -878,17 +879,17 @@ ErrorReport::init(JSContext* cx, HandleV
             str = name;
         } else if (msg) {
             str = msg;
         }
 
         if (JS_GetProperty(cx, exnObject, filename_str, &val)) {
             RootedString tmp(cx, ToString<CanGC>(cx, val));
             if (tmp)
-                filename = JS_EncodeStringToUTF8(cx, tmp);
+                filename.encodeUtf8(cx, tmp);
             else
                 cx->clearPendingException();
         } else {
             cx->clearPendingException();
         }
 
         uint32_t lineno;
         if (!JS_GetProperty(cx, exnObject, js_lineNumber_str, &val) ||
@@ -903,17 +904,17 @@ ErrorReport::init(JSContext* cx, HandleV
             !ToUint32(cx, val, &column))
         {
             cx->clearPendingException();
             column = 0;
         }
 
         reportp = &ownedReport;
         new (reportp) JSErrorReport();
-        ownedReport.filename = filename.get();
+        ownedReport.filename = filename.ptr();
         ownedReport.lineno = lineno;
         ownedReport.exnType = JSEXN_INTERNALERR;
         ownedReport.column = column;
         if (str) {
             // Note that using |str| for |message_| here is kind of wrong,
             // because |str| is supposed to be of the format
             // |ErrorName: ErrorMessage|, and |message_| is supposed to
             // correspond to |ErrorMessage|. But this is what we've
@@ -929,20 +930,18 @@ ErrorReport::init(JSContext* cx, HandleV
             } else {
                 cx->clearPendingException();
                 str = nullptr;
             }
         }
     }
 
     const char* utf8Message = nullptr;
-    if (str) {
-        toStringResultBytesStorage = JS_EncodeStringToUTF8(cx, str);
-        utf8Message = toStringResultBytesStorage.get();
-    }
+    if (str)
+        utf8Message = toStringResultBytesStorage.encodeUtf8(cx, str);
     if (!utf8Message)
         utf8Message = "unknown (can't convert to string)";
 
     if (!reportp) {
         // This is basically an inlined version of
         //
         //   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
         //                            JSMSG_UNCAUGHT_EXCEPTION, utf8Message);
@@ -1050,17 +1049,17 @@ JS::CreateError(JSContext* cx, JSExnType
     if (!obj)
         return false;
 
     rval.setObject(*obj);
     return true;
 }
 
 const char*
-js::ValueToSourceForError(JSContext* cx, HandleValue val, UniqueChars& bytes)
+js::ValueToSourceForError(JSContext* cx, HandleValue val, JSAutoByteString& bytes)
 {
     if (val.isUndefined())
         return "undefined";
 
     if (val.isNull())
         return "null";
 
     AutoClearPendingException acpe(cx);
@@ -1089,26 +1088,24 @@ js::ValueToSourceForError(JSContext* cx,
     } else if (val.isNumber()) {
         if (!sb.append("the number "))
             return "<<error converting value to string>>";
     } else if (val.isString()) {
         if (!sb.append("the string "))
             return "<<error converting value to string>>";
     } else {
         MOZ_ASSERT(val.isBoolean() || val.isSymbol());
-        bytes = EncodeLatin1(cx, str);
-        return bytes.get();
+        return bytes.encodeLatin1(cx, str);
     }
     if (!sb.append(str))
         return "<<error converting value to string>>";
     str = sb.finishString();
     if (!str)
         return "<<error converting value to string>>";
-    bytes = EncodeLatin1(cx, str);
-    return bytes.get();
+    return bytes.encodeLatin1(cx, str);
 }
 
 bool
 js::GetInternalError(JSContext* cx, unsigned errorNumber, MutableHandleValue error)
 {
     FixedInvokeArgs<1> args(cx);
     args[0].set(Int32Value(errorNumber));
     return CallSelfHostedFunction(cx, cx->names().GetInternalError, NullHandleValue, args, error);
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -12,16 +12,18 @@
 #define jsexn_h
 
 #include "jsapi.h"
 #include "NamespaceImports.h"
 
 #include "js/UniquePtr.h"
 #include "vm/JSContext.h"
 
+class JSAutoByteString;
+
 namespace js {
 class ErrorObject;
 
 UniquePtr<JSErrorNotes::Note>
 CopyErrorNote(JSContext* cx, JSErrorNotes::Note* note);
 
 UniquePtr<JSErrorReport>
 CopyErrorReport(JSContext* cx, JSErrorReport* report);
@@ -127,17 +129,17 @@ class AutoAssertNoPendingException
     { }
 
     ~AutoAssertNoPendingException() {
         MOZ_ASSERT(!JS_IsExceptionPending(cx));
     }
 };
 
 extern const char*
-ValueToSourceForError(JSContext* cx, HandleValue val, JS::UniqueChars& bytes);
+ValueToSourceForError(JSContext* cx, HandleValue val, JSAutoByteString& bytes);
 
 bool
 GetInternalError(JSContext* cx, unsigned errorNumber, MutableHandleValue error);
 bool
 GetTypeError(JSContext* cx, unsigned errorNumber, MutableHandleValue error);
 
 } // namespace js
 
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -2,31 +2,30 @@
  * 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 "jsfriendapi.h"
 
 #include "mozilla/Atomics.h"
-#include "mozilla/Maybe.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/TimeStamp.h"
 
 #include <stdint.h>
 
 #ifdef ENABLE_BIGINT
 #include "builtin/BigInt.h"
 #endif
 #include "builtin/Promise.h"
 #include "builtin/TestingFunctions.h"
 #include "gc/GCInternals.h"
 #include "gc/PublicIterators.h"
 #include "gc/WeakMap.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/Printf.h"
 #include "js/Proxy.h"
 #include "js/Wrapper.h"
 #include "proxy/DeadObjectProxy.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/Realm.h"
@@ -804,44 +803,71 @@ JS_FRIEND_API(bool)
 js::DumpScript(JSContext* cx, JSScript* scriptArg)
 {
     return DumpScript(cx, scriptArg, stdout);
 }
 
 #endif
 
 static const char*
-FormatValue(JSContext* cx, HandleValue v, UniqueChars& bytes)
+FormatValue(JSContext* cx, const Value& vArg, JSAutoByteString& bytes)
 {
+    RootedValue v(cx, vArg);
+
     if (v.isMagic(JS_OPTIMIZED_OUT))
         return "[unavailable]";
 
-    if (IsCallable(v))
-        return "[function]";
-
-    if (v.isObject() && IsCrossCompartmentWrapper(&v.toObject()))
-        return "[cross-compartment wrapper]";
-
-    JSString* str;
-    {
-        mozilla::Maybe<AutoRealm> ar;
-        if (v.isObject())
-            ar.emplace(cx, &v.toObject());
-
+    /*
+     * We could use Maybe<AutoRealm> here, but G++ can't quite follow
+     * that, and warns about uninitialized members being used in the
+     * destructor.
+     */
+    RootedString str(cx);
+    if (v.isObject()) {
+        if (IsCrossCompartmentWrapper(&v.toObject()))
+            return "[cross-compartment wrapper]";
+        AutoRealm ar(cx, &v.toObject());
         str = ToString<CanGC>(cx, v);
-        if (!str)
-            return nullptr;
+    } else {
+        str = ToString<CanGC>(cx, v);
     }
 
-    bytes = StringToNewUTF8CharsZ(cx, *str);
-    return bytes.get();
+    if (!str)
+        return nullptr;
+    const char* buf = bytes.encodeLatin1(cx, str);
+    if (!buf)
+        return nullptr;
+    const char* found = strstr(buf, "function ");
+    if (found && (found - buf <= 2))
+        return "[function]";
+    return buf;
 }
 
-static bool
-FormatFrame(JSContext* cx, const FrameIter& iter, Sprinter& sp, int num,
+// Wrapper for JS_sprintf_append() that reports allocation failure to the
+// context.
+static JS::UniqueChars
+MOZ_FORMAT_PRINTF(3, 4)
+sprintf_append(JSContext* cx, JS::UniqueChars&& buf, const char* fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    JS::UniqueChars result = JS_vsprintf_append(std::move(buf), fmt, ap);
+    va_end(ap);
+
+    if (!result) {
+        ReportOutOfMemory(cx);
+        return nullptr;
+    }
+
+    return result;
+}
+
+static JS::UniqueChars
+FormatFrame(JSContext* cx, const FrameIter& iter, JS::UniqueChars&& inBuf, int num,
             bool showArgs, bool showLocals, bool showThisProps)
 {
     MOZ_ASSERT(!cx->isExceptionPending());
     RootedScript script(cx, iter.script());
     jsbytecode* pc = iter.pc();
 
     RootedObject envChain(cx, iter.environmentChain(cx));
     JSAutoRealm ar(cx, envChain);
@@ -855,33 +881,34 @@ FormatFrame(JSContext* cx, const FrameIt
 
     RootedValue thisVal(cx);
     if (iter.hasUsableAbstractFramePtr() &&
         iter.isFunctionFrame() &&
         fun && !fun->isArrow() && !fun->isDerivedClassConstructor() &&
         !(fun->isBoundFunction() && iter.isConstructing()))
     {
         if (!GetFunctionThis(cx, iter.abstractFramePtr(), &thisVal))
-            return false;
+            return nullptr;
     }
 
     // print the frame number and function name
+    JS::UniqueChars buf(std::move(inBuf));
     if (funname) {
-        UniqueChars funbytes = StringToNewUTF8CharsZ(cx, *funname);
-        if (!funbytes)
-            return false;
-        if (!sp.printf("%d %s(", num, funbytes.get()))
-            return false;
+        JSAutoByteString funbytes;
+        char* str = funbytes.encodeLatin1(cx, funname);
+        if (!str)
+            return nullptr;
+        buf = sprintf_append(cx, std::move(buf), "%d %s(", num, str);
     } else if (fun) {
-        if (!sp.printf("%d anonymous(", num))
-            return false;
+        buf = sprintf_append(cx, std::move(buf), "%d anonymous(", num);
     } else {
-        if (!sp.printf("%d <TOP LEVEL>", num))
-            return false;
+        buf = sprintf_append(cx, std::move(buf), "%d <TOP LEVEL>", num);
     }
+    if (!buf)
+        return nullptr;
 
     if (showArgs && iter.hasArgs()) {
         PositionalFormalParameterIter fi(script);
         bool first = true;
         for (unsigned i = 0; i < iter.numActualArgs(); i++) {
             RootedValue arg(cx);
             if (i < iter.numFormalArgs() && fi.closedOver()) {
                 arg = iter.callObj(cx).aliasedBinding(fi);
@@ -893,204 +920,206 @@ FormatFrame(JSContext* cx, const FrameIt
                     arg = iter.argsObj().arg(i);
                 } else {
                     arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING);
                 }
             } else {
                 arg = MagicValue(JS_OPTIMIZED_OUT);
             }
 
-            UniqueChars valueBytes;
+            JSAutoByteString valueBytes;
             const char* value = FormatValue(cx, arg, valueBytes);
             if (!value) {
                 if (cx->isThrowingOutOfMemory())
-                    return false;
+                    return nullptr;
                 cx->clearPendingException();
             }
 
-            UniqueChars nameBytes;
+            JSAutoByteString nameBytes;
             const char* name = nullptr;
 
             if (i < iter.numFormalArgs()) {
                 MOZ_ASSERT(fi.argumentSlot() == i);
                 if (!fi.isDestructured()) {
-                    nameBytes = StringToNewUTF8CharsZ(cx, *fi.name());
-                    name = nameBytes.get();
+                    name = nameBytes.encodeLatin1(cx, fi.name());
                     if (!name)
-                        return false;
+                        return nullptr;
                 } else {
                     name = "(destructured parameter)";
                 }
                 fi++;
             }
 
             if (value) {
-                if (!sp.printf("%s%s%s%s%s%s",
-                               !first ? ", " : "",
-                               name ? name :"",
-                               name ? " = " : "",
-                               arg.isString() ? "\"" : "",
-                               value,
-                               arg.isString() ? "\"" : ""))
-                {
-                    return false;
-                }
+                buf = sprintf_append(cx, std::move(buf), "%s%s%s%s%s%s",
+                                     !first ? ", " : "",
+                                     name ? name :"",
+                                     name ? " = " : "",
+                                     arg.isString() ? "\"" : "",
+                                     value,
+                                     arg.isString() ? "\"" : "");
+                if (!buf)
+                    return nullptr;
 
                 first = false;
             } else {
-                if (!sp.put("    <Failed to get argument while inspecting stack frame>\n"))
-                    return false;
+                buf = sprintf_append(cx, std::move(buf),
+                                     "    <Failed to get argument while inspecting stack frame>\n");
+                if (!buf)
+                    return nullptr;
 
             }
         }
     }
 
     // print filename and line number
-    if (!sp.printf("%s [\"%s\":%d]\n",
-                   fun ? ")" : "",
-                   filename ? filename : "<unknown>",
-                   lineno))
-    {
-        return false;
-    }
+    buf = sprintf_append(cx, std::move(buf), "%s [\"%s\":%d]\n",
+                         fun ? ")" : "",
+                         filename ? filename : "<unknown>",
+                         lineno);
+    if (!buf)
+        return nullptr;
+
 
     // Note: Right now we don't dump the local variables anymore, because
     // that is hard to support across all the JITs etc.
 
     // print the value of 'this'
     if (showLocals) {
         if (!thisVal.isUndefined()) {
+            JSAutoByteString thisValBytes;
             RootedString thisValStr(cx, ToString<CanGC>(cx, thisVal));
             if (!thisValStr) {
                 if (cx->isThrowingOutOfMemory())
-                    return false;
+                    return nullptr;
                 cx->clearPendingException();
             }
             if (thisValStr) {
-                UniqueChars thisValBytes = StringToNewUTF8CharsZ(cx, *thisValStr);
-                if (!thisValBytes)
-                    return false;
-                if (!sp.printf("    this = %s\n", thisValBytes.get()))
-                    return false;
+                const char* str = thisValBytes.encodeLatin1(cx, thisValStr);
+                if (!str)
+                    return nullptr;
+                buf = sprintf_append(cx, std::move(buf), "    this = %s\n", str);
             } else {
-                if (!sp.put("    <failed to get 'this' value>\n"))
-                    return false;
+                buf = sprintf_append(cx, std::move(buf), "    <failed to get 'this' value>\n");
             }
+            if (!buf)
+                return nullptr;
         }
     }
 
     if (showThisProps && thisVal.isObject()) {
         RootedObject obj(cx, &thisVal.toObject());
 
         AutoIdVector keys(cx);
         if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &keys)) {
             if (cx->isThrowingOutOfMemory())
-                return false;
+                return nullptr;
             cx->clearPendingException();
         }
 
+        RootedId id(cx);
         for (size_t i = 0; i < keys.length(); i++) {
             RootedId id(cx, keys[i]);
             RootedValue key(cx, IdToValue(id));
             RootedValue v(cx);
 
             if (!GetProperty(cx, obj, obj, id, &v)) {
                 if (cx->isThrowingOutOfMemory())
-                    return false;
+                    return nullptr;
                 cx->clearPendingException();
-                if (!sp.put("    <Failed to fetch property while inspecting stack frame>\n"))
-                    return false;
+                buf = sprintf_append(cx, std::move(buf),
+                                     "    <Failed to fetch property while inspecting stack frame>\n");
+                if (!buf)
+                    return nullptr;
                 continue;
             }
 
-            UniqueChars nameBytes;
+            JSAutoByteString nameBytes;
             const char* name = FormatValue(cx, key, nameBytes);
             if (!name) {
                 if (cx->isThrowingOutOfMemory())
-                    return false;
+                    return nullptr;
                 cx->clearPendingException();
             }
 
-            UniqueChars valueBytes;
+            JSAutoByteString valueBytes;
             const char* value = FormatValue(cx, v, valueBytes);
             if (!value) {
                 if (cx->isThrowingOutOfMemory())
-                    return false;
+                    return nullptr;
                 cx->clearPendingException();
             }
 
             if (name && value) {
-                if (!sp.printf("    this.%s = %s%s%s\n",
-                               name,
-                               v.isString() ? "\"" : "",
-                               value,
-                               v.isString() ? "\"" : ""))
-                {
-                    return false;
-                }
+                buf = sprintf_append(cx, std::move(buf), "    this.%s = %s%s%s\n",
+                                     name,
+                                     v.isString() ? "\"" : "",
+                                     value,
+                                     v.isString() ? "\"" : "");
             } else {
-                if (!sp.put("    <Failed to format values while inspecting stack frame>\n"))
-                    return false;
+                buf = sprintf_append(cx, std::move(buf),
+                                     "    <Failed to format values while inspecting stack frame>\n");
             }
+            if (!buf)
+                return nullptr;
         }
     }
 
     MOZ_ASSERT(!cx->isExceptionPending());
-    return true;
+    return buf;
 }
 
-static bool
-FormatWasmFrame(JSContext* cx, const FrameIter& iter, Sprinter& sp, int num)
+static JS::UniqueChars
+FormatWasmFrame(JSContext* cx, const FrameIter& iter, JS::UniqueChars&& inBuf, int num)
 {
     UniqueChars nameStr;
     if (JSAtom* functionDisplayAtom = iter.maybeFunctionDisplayAtom()) {
         nameStr = StringToNewUTF8CharsZ(cx, *functionDisplayAtom);
         if (!nameStr)
-            return false;
+            return nullptr;
     }
 
-    if (!sp.printf("%d %s()", num, nameStr ? nameStr.get() : "<wasm-function>"))
-        return false;
+    JS::UniqueChars buf = sprintf_append(cx, std::move(inBuf), "%d %s()",
+                                         num,
+                                         nameStr ? nameStr.get() : "<wasm-function>");
+    if (!buf)
+        return nullptr;
 
-    if (!sp.printf(" [\"%s\":wasm-function[%d]:0x%x]\n",
-                   iter.filename() ? iter.filename() : "<unknown>",
-                   iter.wasmFuncIndex(),
-                   iter.wasmBytecodeOffset()))
-    {
-        return false;
-    }
+    buf = sprintf_append(cx, std::move(buf), " [\"%s\":wasm-function[%d]:0x%x]\n",
+                         iter.filename() ? iter.filename() : "<unknown>",
+                         iter.wasmFuncIndex(),
+                         iter.wasmBytecodeOffset());
+    if (!buf)
+        return nullptr;
 
     MOZ_ASSERT(!cx->isExceptionPending());
-    return true;
+    return buf;
 }
 
 JS_FRIEND_API(JS::UniqueChars)
-JS::FormatStackDump(JSContext* cx, bool showArgs, bool showLocals, bool showThisProps)
+JS::FormatStackDump(JSContext* cx, JS::UniqueChars&& inBuf, bool showArgs, bool showLocals,
+                    bool showThisProps)
 {
     int num = 0;
 
-    Sprinter sp(cx);
-    if (!sp.init())
-        return nullptr;
-
+    JS::UniqueChars buf(std::move(inBuf));
     for (AllFramesIter i(cx); !i.done(); ++i) {
-        bool ok = i.hasScript()
-                  ? FormatFrame(cx, i, sp, num, showArgs, showLocals, showThisProps)
-                  : FormatWasmFrame(cx, i, sp, num);
-        if (!ok)
+        if (i.hasScript())
+            buf = FormatFrame(cx, i, std::move(buf), num, showArgs, showLocals, showThisProps);
+        else
+            buf = FormatWasmFrame(cx, i, std::move(buf), num);
+        if (!buf)
             return nullptr;
         num++;
     }
 
-    if (num == 0) {
-        if (!sp.put("JavaScript stack is empty\n"))
-            return nullptr;
-    }
+    if (!num)
+        buf = JS_sprintf_append(std::move(buf), "JavaScript stack is empty\n");
 
-    return sp.release();
+    return buf;
 }
 
 extern JS_FRIEND_API(bool)
 JS::ForceLexicalInitialization(JSContext *cx, HandleObject obj)
 {
     AssertHeapIsIdle();
     CHECK_THREAD(cx);
     cx->check(obj);
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -10,16 +10,17 @@
 #include "mozilla/Atomics.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/UniquePtr.h"
 
 #include "jspubtd.h"
 
+#include "js/AutoByteString.h"
 #include "js/CallArgs.h"
 #include "js/CallNonGenericMethod.h"
 #include "js/CharacterEncoding.h"
 #include "js/Class.h"
 #include "js/ErrorReport.h"
 #include "js/HeapAPI.h"
 #include "js/StableStringChars.h"
 #include "js/TypeDecls.h"
@@ -296,17 +297,18 @@ extern JS_FRIEND_API(void)
 DumpBacktrace(JSContext* cx);
 
 } // namespace js
 
 namespace JS {
 
 /** Exposed for DumpJSStack */
 extern JS_FRIEND_API(JS::UniqueChars)
-FormatStackDump(JSContext* cx, bool showArgs, bool showLocals, bool showThisProps);
+FormatStackDump(JSContext* cx, JS::UniqueChars&& buf, bool showArgs, bool showLocals,
+                bool showThisProps);
 
 /**
  * Set all of the uninitialized lexicals on an object to undefined. Return
  * true if any lexicals were initialized and false otherwise.
  * */
 extern JS_FRIEND_API(bool)
 ForceLexicalInitialization(JSContext *cx, HandleObject obj);
 
@@ -1457,23 +1459,23 @@ struct MOZ_STACK_CLASS JS_FRIEND_API(Err
 
     // And keep its chars alive too.
     JS::AutoStableStringChars strChars;
 
     // And we need to root our exception value.
     JS::RootedObject exnObject;
 
     // And for our filename.
-    JS::UniqueChars filename;
+    JSAutoByteString filename;
 
     // We may have a result of error.toString().
     // FIXME: We should not call error.toString(), since it could have side
     //        effect (see bug 633623).
     JS::ConstUTF8CharsZ toStringResult_;
-    JS::UniqueChars toStringResultBytesStorage;
+    JSAutoByteString toStringResultBytesStorage;
 };
 
 /* Implemented in vm/StructuredClone.cpp. */
 extern JS_FRIEND_API(uint64_t)
 GetSCOffset(JSStructuredCloneWriter* writer);
 
 namespace Scalar {
 
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -22,17 +22,16 @@
 #endif
 #include <math.h>
 #include <string.h>
 
 #include "jstypes.h"
 
 #include "builtin/String.h"
 #include "double-conversion/double-conversion.h"
-#include "js/CharacterEncoding.h"
 #include "js/Conversions.h"
 #if !EXPOSE_INTL_API
 #include "js/LocaleSensitive.h"
 #endif
 #include "util/DoubleToString.h"
 #include "util/StringBuffer.h"
 #ifdef ENABLE_BIGINT
 #include "vm/BigIntType.h"
@@ -792,20 +791,20 @@ num_toLocaleString_impl(JSContext* cx, c
         JS_ReportOutOfMemory(cx);
         return false;
     }
 
     /*
      * Create the string, move back to bytes to make string twiddling
      * a bit easier and so we can insert platform charset seperators.
      */
-    UniqueChars numBytes = JS_EncodeStringToLatin1(cx, str);
+    JSAutoByteString numBytes(cx, str);
     if (!numBytes)
         return false;
-    const char* num = numBytes.get();
+    const char* num = numBytes.ptr();
     if (!num)
         return false;
 
     /*
      * Find the first non-integer value, whether it be a letter as in
      * 'Infinity', a decimal point, or an 'e' from exponential notation.
      */
     const char* nint = num;
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -119,16 +119,17 @@ EXPORTS += [
     'jsfriendapi.h',
     'jspubtd.h',
     'jstypes.h',
     'perf/jsperf.h',
 ]
 
 EXPORTS.js += [
     '../public/AllocPolicy.h',
+    '../public/AutoByteString.h',
     '../public/CallArgs.h',
     '../public/CallNonGenericMethod.h',
     '../public/CharacterEncoding.h',
     '../public/Class.h',
     '../public/CompilationAndEvaluation.h',
     '../public/CompileOptions.h',
     '../public/Conversions.h',
     '../public/Date.h',
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -218,17 +218,17 @@ pm_finalize(JSFreeOp* fop, JSObject* obj
 
 static PerfMeasurement*
 GetPM(JSContext* cx, JS::HandleValue value, const char* fname)
 {
     if (!value.isObject()) {
         UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, value, nullptr);
         if (!bytes)
             return nullptr;
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, 0, JSMSG_NOT_NONNULL_OBJECT, bytes.get());
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, 0, JSMSG_NOT_NONNULL_OBJECT, bytes.get());
         return nullptr;
     }
     RootedObject obj(cx, &value.toObject());
     PerfMeasurement* p = (PerfMeasurement*)
         JS_GetInstancePrivate(cx, obj, &pm_class, nullptr);
     if (p)
         return p;
 
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -23,31 +23,39 @@
 #include "gc/Marking-inl.h"
 #include "vm/JSAtom-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
+using JS::AutoStableStringChars;
+
 void
-js::AutoEnterPolicy::reportErrorIfExceptionIsNotPending(JSContext* cx, HandleId id)
+js::AutoEnterPolicy::reportErrorIfExceptionIsNotPending(JSContext* cx, jsid id)
 {
     if (JS_IsExceptionPending(cx))
         return;
 
     if (JSID_IS_VOID(id)) {
         ReportAccessDenied(cx);
     } else {
-        UniqueChars prop = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
-        if (!prop)
+        RootedValue idVal(cx, IdToValue(id));
+        JSString* str = ValueToSource(cx, idVal);
+        if (!str) {
             return;
+        }
+        AutoStableStringChars chars(cx);
+        const char16_t* prop = nullptr;
+        if (str->ensureFlat(cx) && chars.initTwoByte(cx, str))
+            prop = chars.twoByteChars();
 
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_ACCESS_DENIED,
-                                 prop.get());
+        JS_ReportErrorNumberUC(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_ACCESS_DENIED,
+                               prop);
     }
 }
 
 #ifdef DEBUG
 void
 js::AutoEnterPolicy::recordEnter(JSContext* cx, HandleObject proxy, HandleId id, Action act)
 {
     if (allowed()) {
--- a/js/src/proxy/ScriptedProxyHandler.cpp
+++ b/js/src/proxy/ScriptedProxyHandler.cpp
@@ -3,17 +3,17 @@
  * 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 "proxy/ScriptedProxyHandler.h"
 
 #include "jsapi.h"
 
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "vm/Interpreter.h" // For InstanceOfOperator
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using JS::IsArrayAnswer;
@@ -174,21 +174,21 @@ GetProxyTrap(JSContext* cx, HandleObject
 
     if (func.isNull()) {
         func.setUndefined();
         return true;
     }
 
     // Step 4.
     if (!IsCallable(func)) {
-        UniqueChars bytes = EncodeLatin1(cx, name);
+        JSAutoByteString bytes(cx, name);
         if (!bytes)
             return false;
 
-        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_BAD_TRAP, bytes.get());
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_BAD_TRAP, bytes.ptr());
         return false;
     }
 
     return true;
 }
 
 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.1 Proxy.[[GetPrototypeOf]].
 bool
@@ -826,20 +826,18 @@ ScriptedProxyHandler::ownPropertyKeys(JS
         if (!ptr)
             return js::Throw(cx, targetConfigurableKeys[i], JSMSG_CANT_REPORT_E_AS_NE);
 
         // Step 21.b.
         uncheckedResultKeys.remove(ptr);
     }
 
     // Step 22.
-    if (!uncheckedResultKeys.empty()) {
-        RootedId id(cx, uncheckedResultKeys.all().front());
-        return js::Throw(cx, id, JSMSG_CANT_REPORT_NEW);
-    }
+    if (!uncheckedResultKeys.empty())
+        return js::Throw(cx, uncheckedResultKeys.all().front(), JSMSG_CANT_REPORT_NEW);
 
     // Step 23.
     return props.appendAll(trapResult);
 }
 
 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.10 Proxy.[[Delete]](P)
 bool
 ScriptedProxyHandler::delete_(JSContext* cx, HandleObject proxy, HandleId id,
@@ -886,21 +884,18 @@ ScriptedProxyHandler::delete_(JSContext*
 
     // Step 10.
     Rooted<PropertyDescriptor> desc(cx);
     if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
         return false;
 
     // Step 12.
     if (desc.object() && !desc.configurable()) {
-        UniqueChars bytes = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
-        if (!bytes)
-            return false;
-
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_CANT_DELETE, bytes.get());
+        RootedValue v(cx, IdToValue(id));
+        ReportValueError(cx, JSMSG_CANT_DELETE, JSDVG_IGNORE_STACK, v, nullptr);
         return false;
     }
 
     // Steps 11,13.
     return result.succeed();
 }
 
 // ES8 rev 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.7 Proxy.[[HasProperty]](P)
--- a/js/src/proxy/SecurityWrapper.cpp
+++ b/js/src/proxy/SecurityWrapper.cpp
@@ -108,22 +108,26 @@ SecurityWrapper<Base>::boxedValue_unbox(
 
 template <class Base>
 bool
 SecurityWrapper<Base>::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
                                       Handle<PropertyDescriptor> desc,
                                       ObjectOpResult& result) const
 {
     if (desc.getter() || desc.setter()) {
-        UniqueChars prop = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
-        if (!prop)
+        RootedValue idVal(cx, IdToValue(id));
+        JSString* str = ValueToSource(cx, idVal);
+        if (!str)
             return false;
-
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_ACCESSOR_DEF_DENIED,
-                                 prop.get());
+        AutoStableStringChars chars(cx);
+        const char16_t* prop = nullptr;
+        if (str->ensureFlat(cx) && chars.initTwoByte(cx, str))
+            prop = chars.twoByteChars();
+        JS_ReportErrorNumberUC(cx, GetErrorMessage, nullptr,
+                               JSMSG_ACCESSOR_DEF_DENIED, prop);
         return false;
     }
 
     return Base::defineProperty(cx, wrapper, id, desc, result);
 }
 
 template class js::SecurityWrapper<Wrapper>;
 template class js::SecurityWrapper<CrossCompartmentWrapper>;
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -20,17 +20,17 @@
 #endif
 
 #include "jsapi.h"
 // For JSFunctionSpecWithHelp
 #include "jsfriendapi.h"
 
 #include "builtin/String.h"
 #include "gc/FreeOp.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.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"
@@ -53,19 +53,19 @@ namespace shell {
 
 #ifdef XP_WIN
 const char PathSeparator = '\\';
 #else
 const char PathSeparator = '/';
 #endif
 
 static bool
-IsAbsolutePath(const UniqueChars& filename)
+IsAbsolutePath(const JSAutoByteString& filename)
 {
-    const char* pathname = filename.get();
+    const char* pathname = filename.ptr();
 
     if (pathname[0] == PathSeparator)
         return true;
 
 #ifdef XP_WIN
     // On Windows there are various forms of absolute paths (see
     // http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
     // for details):
@@ -101,17 +101,17 @@ ResolvePath(JSContext* cx, HandleString 
     if (!filenameStr) {
 #ifdef XP_WIN
         return JS_NewStringCopyZ(cx, "nul");
 #else
         return JS_NewStringCopyZ(cx, "/dev/null");
 #endif
     }
 
-    UniqueChars filename = JS_EncodeStringToLatin1(cx, filenameStr);
+    JSAutoByteString filename(cx, filenameStr);
     if (!filename)
         return nullptr;
 
     if (IsAbsolutePath(filename))
         return filenameStr;
 
     JS::AutoFilename scriptFilename;
     if (resolveMode == ScriptRelative) {
@@ -143,89 +143,89 @@ ResolvePath(JSContext* cx, HandleString 
     } else {
         const char* cwd = getcwd(buffer, PATH_MAX);
         if (!cwd)
             return nullptr;
     }
 
     size_t len = strlen(buffer);
     buffer[len] = '/';
-    strncpy(buffer + len + 1, filename.get(), sizeof(buffer) - (len+1));
+    strncpy(buffer + len + 1, filename.ptr(), sizeof(buffer) - (len+1));
     if (buffer[PATH_MAX] != '\0')
         return nullptr;
 
     return JS_NewStringCopyZ(cx, buffer);
 }
 
 JSObject*
 FileAsTypedArray(JSContext* cx, JS::HandleString pathnameStr)
 {
-    UniqueChars pathname = JS_EncodeStringToLatin1(cx, pathnameStr);
+    JSAutoByteString pathname(cx, pathnameStr);
     if (!pathname)
         return nullptr;
 
-    FILE* file = fopen(pathname.get(), "rb");
+    FILE* file = fopen(pathname.ptr(), "rb");
     if (!file) {
         /*
          * Use Latin1 variant here because the encoding of the return value of
          * strerror function can be non-UTF-8.
          */
-        JS_ReportErrorLatin1(cx, "can't open %s: %s", pathname.get(), strerror(errno));
+        JS_ReportErrorLatin1(cx, "can't open %s: %s", pathname.ptr(), strerror(errno));
         return nullptr;
     }
     AutoCloseFile autoClose(file);
 
     RootedObject obj(cx);
     if (fseek(file, 0, SEEK_END) != 0) {
-        pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
-        if (!pathname)
+        pathname.clear();
+        if (!pathname.encodeUtf8(cx, pathnameStr))
             return nullptr;
-        JS_ReportErrorUTF8(cx, "can't seek end of %s", pathname.get());
+        JS_ReportErrorUTF8(cx, "can't seek end of %s", pathname.ptr());
     } else {
         size_t len = ftell(file);
         if (fseek(file, 0, SEEK_SET) != 0) {
-            pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
-            if (!pathname)
+            pathname.clear();
+            if (!pathname.encodeUtf8(cx, pathnameStr))
                 return nullptr;
-            JS_ReportErrorUTF8(cx, "can't seek start of %s", pathname.get());
+            JS_ReportErrorUTF8(cx, "can't seek start of %s", pathname.ptr());
         } else {
             obj = JS_NewUint8Array(cx, len);
             if (!obj)
                 return nullptr;
             js::TypedArrayObject& ta = obj->as<js::TypedArrayObject>();
             if (ta.isSharedMemory()) {
                 // Must opt in to use shared memory.  For now, don't.
                 //
                 // (It is incorrect to read into the buffer without
                 // synchronization since that can create a race.  A
                 // lock here won't fix it - both sides must
                 // participate.  So what one must do is to create a
                 // temporary buffer, read into that, and use a
                 // race-safe primitive to copy memory into the
                 // buffer.)
-                pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
-                if (!pathname)
+                pathname.clear();
+                if (!pathname.encodeUtf8(cx, pathnameStr))
                     return nullptr;
-                JS_ReportErrorUTF8(cx, "can't read %s: shared memory buffer", pathname.get());
+                JS_ReportErrorUTF8(cx, "can't read %s: shared memory buffer", pathname.ptr());
                 return nullptr;
             }
             char* buf = static_cast<char*>(ta.viewDataUnshared());
             size_t cc = fread(buf, 1, len, file);
             if (cc != len) {
                 if (ptrdiff_t(cc) < 0) {
                     /*
                      * Use Latin1 variant here because the encoding of the return
                      * value of strerror function can be non-UTF-8.
                      */
-                    JS_ReportErrorLatin1(cx, "can't read %s: %s", pathname.get(), strerror(errno));
+                    JS_ReportErrorLatin1(cx, "can't read %s: %s", pathname.ptr(), strerror(errno));
                 } else {
-                    pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
-                    if (!pathname)
+                    pathname.clear();
+                    if (!pathname.encodeUtf8(cx, pathnameStr))
                         return nullptr;
-                    JS_ReportErrorUTF8(cx, "can't read %s: short read", pathname.get());
+                    JS_ReportErrorUTF8(cx, "can't read %s: short read", pathname.ptr());
                 }
                 obj = nullptr;
             }
         }
     }
     return obj;
 }
 
@@ -314,51 +314,51 @@ osfile_writeTypedArrayToFile(JSContext* 
         return false;
     }
 
     RootedString givenPath(cx, args[0].toString());
     RootedString str(cx, ResolvePath(cx, givenPath, RootRelative));
     if (!str)
         return false;
 
-    UniqueChars filename = JS_EncodeStringToLatin1(cx, str);
+    JSAutoByteString filename(cx, str);
     if (!filename)
         return false;
 
-    FILE* file = fopen(filename.get(), "wb");
+    FILE* file = fopen(filename.ptr(), "wb");
     if (!file) {
         /*
          * Use Latin1 variant here because the encoding of the return value of
          * strerror function can be non-UTF-8.
          */
-        JS_ReportErrorLatin1(cx, "can't open %s: %s", filename.get(), strerror(errno));
+        JS_ReportErrorLatin1(cx, "can't open %s: %s", filename.ptr(), strerror(errno));
         return false;
     }
     AutoCloseFile autoClose(file);
 
     TypedArrayObject* obj = &args[1].toObject().as<TypedArrayObject>();
 
     if (obj->isSharedMemory()) {
         // Must opt in to use shared memory.  For now, don't.
         //
         // See further comments in FileAsTypedArray, above.
-        filename = JS_EncodeStringToUTF8(cx, str);
-        if (!filename)
+        filename.clear();
+        if (!filename.encodeUtf8(cx, str))
             return false;
-        JS_ReportErrorUTF8(cx, "can't write %s: shared memory buffer", filename.get());
+        JS_ReportErrorUTF8(cx, "can't write %s: shared memory buffer", filename.ptr());
         return false;
     }
     void* buf = obj->viewDataUnshared();
     if (fwrite(buf, obj->bytesPerElement(), obj->length(), file) != obj->length() ||
         !autoClose.release())
     {
-        filename = JS_EncodeStringToUTF8(cx, str);
-        if (!filename)
+        filename.clear();
+        if (!filename.encodeUtf8(cx, str))
             return false;
-        JS_ReportErrorUTF8(cx, "can't write %s", filename.get());
+        JS_ReportErrorUTF8(cx, "can't write %s", filename.ptr());
         return false;
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 /* static */ RCFile*
@@ -467,26 +467,26 @@ const js::Class FileObject::class_ = {
 };
 
 static FileObject*
 redirect(JSContext* cx, HandleString relFilename, RCFile** globalFile)
 {
     RootedString filename(cx, ResolvePath(cx, relFilename, RootRelative));
     if (!filename)
         return nullptr;
-    UniqueChars filenameABS = JS_EncodeStringToLatin1(cx, filename);
+    JSAutoByteString filenameABS(cx, filename);
     if (!filenameABS)
         return nullptr;
-    RCFile* file = RCFile::create(cx, filenameABS.get(), "wb");
+    RCFile* file = RCFile::create(cx, filenameABS.ptr(), "wb");
     if (!file) {
         /*
          * Use Latin1 variant here because the encoding of the return value of
          * strerror function can be non-UTF-8.
          */
-        JS_ReportErrorLatin1(cx, "cannot redirect to %s: %s", filenameABS.get(), strerror(errno));
+        JS_ReportErrorLatin1(cx, "cannot redirect to %s: %s", filenameABS.ptr(), strerror(errno));
         return nullptr;
     }
 
     // Grant the global gOutFile ownership of the new file, release ownership
     // of its old file, and return a FileObject owning the old file.
     file->acquire(); // Global owner of new file
 
     FileObject* fileObj = FileObject::create(cx, *globalFile); // Newly created owner of old file
@@ -631,17 +631,17 @@ ospath_isAbsolute(JSContext* cx, unsigne
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 1 || !args[0].isString()) {
         JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
                                   "isAbsolute");
         return false;
     }
 
-    UniqueChars path = JS_EncodeStringToLatin1(cx, args[0].toString());
+    JSAutoByteString path(cx, args[0].toString());
     if (!path)
         return false;
 
     args.rval().setBoolean(IsAbsolutePath(path));
     return true;
 }
 
 static bool
@@ -660,17 +660,17 @@ ospath_join(JSContext* cx, unsigned argc
     StringBuffer 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());
+        JSAutoByteString path(cx, args[i].toString());
         if (!path)
             return false;
 
         if (IsAbsolutePath(path)) {
             MOZ_ALWAYS_TRUE(buffer.resize(0));
         } else if (i != 0) {
             if (!buffer.append(PathSeparator))
                 return false;
@@ -706,21 +706,21 @@ os_getenv(JSContext* cx, unsigned argc, 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() < 1) {
         JS_ReportErrorASCII(cx, "os.getenv requires 1 argument");
         return false;
     }
     RootedString key(cx, ToString(cx, args[0]));
     if (!key)
         return false;
-    UniqueChars keyBytes = JS_EncodeStringToUTF8(cx, key);
-    if (!keyBytes)
+    JSAutoByteString keyBytes;
+    if (!keyBytes.encodeUtf8(cx, key))
         return false;
 
-    if (const char* valueBytes = getenv(keyBytes.get())) {
+    if (const char* valueBytes = getenv(keyBytes.ptr())) {
         RootedString value(cx, JS_NewStringCopyZ(cx, valueBytes));
         if (!value)
             return false;
         args.rval().setString(value);
     } else {
         args.rval().setUndefined();
     }
     return true;
@@ -790,21 +790,21 @@ os_system(JSContext* cx, unsigned argc, 
         JS_ReportErrorASCII(cx, "os.system requires 1 argument");
         return false;
     }
 
     JSString* str = JS::ToString(cx, args[0]);
     if (!str)
         return false;
 
-    UniqueChars command = JS_EncodeStringToLatin1(cx, str);
+    JSAutoByteString command(cx, str);
     if (!command)
         return false;
 
-    int result = system(command.get());
+    int result = system(command.ptr());
     if (result == -1) {
         ReportSysError(cx, "system call failed");
         return false;
     }
 
     args.rval().setInt32(result);
     return true;
 }
@@ -819,17 +819,17 @@ os_spawn(JSContext* cx, unsigned argc, V
         JS_ReportErrorASCII(cx, "os.spawn requires 1 argument");
         return false;
     }
 
     JSString* str = JS::ToString(cx, args[0]);
     if (!str)
         return false;
 
-    UniqueChars command = JS_EncodeStringToLatin1(cx, str);
+    JSAutoByteString command(cx, str);
     if (!command)
         return false;
 
     int32_t childPid = fork();
     if (childPid == -1) {
         ReportSysError(cx, "fork failed");
         return false;
     }
@@ -837,17 +837,17 @@ os_spawn(JSContext* cx, unsigned argc, V
     if (childPid) {
         args.rval().setInt32(childPid);
         return true;
     }
 
     // We are in the child
 
     const char* cmd[] = {"sh", "-c", nullptr, nullptr};
-    cmd[2] = command.get();
+    cmd[2] = command.ptr();
 
     execvp("sh", (char * const*)cmd);
     exit(1);
 }
 
 static bool
 os_kill(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -72,17 +72,17 @@
 #include "frontend/Parser.h"
 #include "gc/PublicIterators.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/InlinableNatives.h"
 #include "jit/Ion.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitRealm.h"
 #include "jit/shared/CodeGenerator-shared.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/CompileOptions.h"
 #include "js/Debug.h"
 #include "js/GCVector.h"
 #include "js/Initialization.h"
 #include "js/JSON.h"
 #include "js/Printf.h"
 #include "js/SourceBufferHolder.h"
@@ -1048,51 +1048,46 @@ BoundToAsyncStack(JSContext* cx, unsigne
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedFunction function(cx, (&GetFunctionNativeReserved(&args.callee(), 0)
                                  .toObject().as<JSFunction>()));
     RootedObject options(cx, &GetFunctionNativeReserved(&args.callee(), 1).toObject());
 
     RootedSavedFrame stack(cx, nullptr);
+    JSAutoByteString cause;
     bool isExplicit;
 
     RootedValue v(cx);
 
     if (!JS_GetProperty(cx, options, "stack", &v))
         return false;
     if (!v.isObject() || !v.toObject().is<SavedFrame>()) {
         JS_ReportErrorASCII(cx, "The 'stack' property must be a SavedFrame object.");
         return false;
     }
     stack = &v.toObject().as<SavedFrame>();
 
     if (!JS_GetProperty(cx, options, "cause", &v))
         return false;
     RootedString causeString(cx, ToString(cx, v));
-    if (!causeString) {
-        MOZ_ASSERT(cx->isExceptionPending());
-        return false;
-    }
-
-    UniqueChars cause = JS_EncodeStringToUTF8(cx, causeString);
-    if (!cause) {
+    if (!causeString || !cause.encodeUtf8(cx, causeString)) {
         MOZ_ASSERT(cx->isExceptionPending());
         return false;
     }
 
     if (!JS_GetProperty(cx, options, "explicit", &v))
         return false;
     isExplicit = v.isUndefined() ? true : ToBoolean(v);
 
     auto kind = (isExplicit
                  ? JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT
                  : JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::IMPLICIT);
 
-    JS::AutoSetAsyncStackForNewCalls asasfnckthxbye(cx, stack, cause.get(), kind);
+    JS::AutoSetAsyncStackForNewCalls asasfnckthxbye(cx, stack, cause.ptr(), kind);
     return Call(cx, UndefinedHandleValue, function,
                 JS::HandleValueArray::empty(), args.rval());
 }
 
 static bool
 BindToAsyncStack(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -1171,24 +1166,26 @@ EvalAndPrint(JSContext* cx, const char* 
     if (compileOnly)
         return true;
     RootedValue result(cx);
     if (!JS_ExecuteScript(cx, script, &result))
         return false;
 
     if (!result.isUndefined() && gOutFile->isOpen()) {
         // Print.
-        RootedString str(cx, JS_ValueToSource(cx, result));
+        RootedString str(cx);
+        str = JS_ValueToSource(cx, result);
         if (!str)
             return false;
 
-        UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, str);
+        char* utf8chars = JS_EncodeStringToUTF8(cx, str);
         if (!utf8chars)
             return false;
-        fprintf(gOutFile->fp, "%s\n", utf8chars.get());
+        fprintf(gOutFile->fp, "%s\n", utf8chars);
+        JS_free(cx, utf8chars);
     }
     return true;
 }
 
 static MOZ_MUST_USE bool
 ReadEvalPrintLoop(JSContext* cx, FILE* in, bool compileOnly)
 {
     ShellContext* sc = GetShellContext(cx);
@@ -1358,17 +1355,17 @@ CreateMappedArrayBuffer(JSContext* cx, u
         return false;
     // It's a little bizarre to resolve relative to the script, but for testing
     // I need a file at a known location, and the only good way I know of to do
     // that right now is to include it in the repo alongside the test script.
     // Bug 944164 would introduce an alternative.
     JSString* filenameStr = ResolvePath(cx, rawFilenameStr, ScriptRelative);
     if (!filenameStr)
         return false;
-    UniqueChars filename = JS_EncodeStringToLatin1(cx, filenameStr);
+    JSAutoByteString filename(cx, filenameStr);
     if (!filename)
         return false;
 
     uint32_t offset = 0;
     if (args.length() >= 2) {
         if (!JS::ToUint32(cx, args[1], &offset))
             return false;
     }
@@ -1380,19 +1377,19 @@ CreateMappedArrayBuffer(JSContext* cx, u
             return false;
         sizeGiven = true;
         if (size == 0) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
             return false;
         }
     }
 
-    FILE* file = fopen(filename.get(), "rb");
+    FILE* file = fopen(filename.ptr(), "rb");
     if (!file) {
-        ReportCantOpenErrorUnknownEncoding(cx, filename.get());
+        ReportCantOpenErrorUnknownEncoding(cx, filename.ptr());
         return false;
     }
     AutoCloseFile autoClose(file);
 
     if (!sizeGiven) {
         struct stat st;
         if (fstat(fileno(file), &st) < 0) {
             JS_ReportErrorASCII(cx, "Unable to stat file");
@@ -1466,46 +1463,43 @@ Options(JSContext* cx, unsigned argc, Va
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     JS::ContextOptions oldContextOptions = JS::ContextOptionsRef(cx);
     for (unsigned i = 0; i < args.length(); i++) {
         RootedString str(cx, JS::ToString(cx, args[i]));
         if (!str)
             return false;
-
-        RootedLinearString opt(cx, str->ensureLinear(cx));
-        if (!opt)
-            return false;
-
-        if (StringEqualsAscii(opt, "strict")) {
+        args[i].setString(str);
+
+        JSAutoByteString opt;
+        if (!opt.encodeUtf8(cx, str))
+            return false;
+
+        if (strcmp(opt.ptr(), "strict") == 0) {
             JS::ContextOptionsRef(cx).toggleExtraWarnings();
-        } else if (StringEqualsAscii(opt, "werror")) {
+        } else if (strcmp(opt.ptr(), "werror") == 0) {
             // Disallow toggling werror when there are off-thread jobs, to avoid
             // confusing CompileError::throwError.
             ShellContext* sc = GetShellContext(cx);
             if (!sc->offThreadJobs.empty()) {
                 JS_ReportErrorASCII(cx, "can't toggle werror when there are off-thread jobs");
                 return false;
             }
             JS::ContextOptionsRef(cx).toggleWerror();
-        } else if (StringEqualsAscii(opt, "throw_on_asmjs_validation_failure")) {
+        } else if (strcmp(opt.ptr(), "throw_on_asmjs_validation_failure") == 0) {
             JS::ContextOptionsRef(cx).toggleThrowOnAsmJSValidationFailure();
-        } else if (StringEqualsAscii(opt, "strict_mode")) {
+        } else if (strcmp(opt.ptr(), "strict_mode") == 0) {
             JS::ContextOptionsRef(cx).toggleStrictMode();
         } else {
-            UniqueChars optChars = JS_EncodeStringToUTF8(cx, opt);
-            if (!optChars)
-                return false;
-
             JS_ReportErrorUTF8(cx,
                                "unknown option name '%s'."
                                " The valid names are strict,"
                                " werror, and strict_mode.",
-                               optChars.get());
+                               opt.ptr());
             return false;
         }
     }
 
     UniqueChars names = DuplicateString("");
     bool found = false;
     if (names && oldContextOptions.extraWarnings()) {
         names = JS_sprintf_append(std::move(names), "%s%s", found ? "," : "", "strict");
@@ -1548,29 +1542,29 @@ LoadScript(JSContext* cx, unsigned argc,
                                       "load");
             return false;
         }
         str = ResolvePath(cx, str, scriptRelative ? ScriptRelative : RootRelative);
         if (!str) {
             JS_ReportErrorASCII(cx, "unable to resolve path");
             return false;
         }
-        UniqueChars filename = JS_EncodeStringToLatin1(cx, str);
+        JSAutoByteString filename(cx, str);
         if (!filename)
             return false;
         errno = 0;
         CompileOptions opts(cx);
         opts.setIntroductionType("js shell load")
             .setUTF8(true)
             .setIsRunOnce(true)
             .setNoScriptRval(true);
         RootedScript script(cx);
         RootedValue unused(cx);
-        if ((compileOnly && !Compile(cx, opts, filename.get(), &script)) ||
-            !Evaluate(cx, opts, filename.get(), &unused))
+        if ((compileOnly && !Compile(cx, opts, filename.ptr(), &script)) ||
+            !Evaluate(cx, opts, filename.ptr(), &unused))
         {
             return false;
         }
     }
 
     args.rval().setUndefined();
     return true;
 }
@@ -1587,17 +1581,17 @@ LoadScriptRelativeToScript(JSContext* cx
     return LoadScript(cx, argc, vp, true);
 }
 
 // Populate |options| with the options given by |opts|'s properties. If we
 // need to convert a filename to a C string, let fileNameBytes own the
 // bytes.
 static bool
 ParseCompileOptions(JSContext* cx, CompileOptions& options, HandleObject opts,
-                    UniqueChars& fileNameBytes)
+                    JSAutoByteString& fileNameBytes)
 {
     RootedValue v(cx);
     RootedString s(cx);
 
     if (!JS_GetProperty(cx, opts, "isRunOnce", &v))
         return false;
     if (!v.isUndefined())
         options.setIsRunOnce(ToBoolean(v));
@@ -1610,20 +1604,20 @@ ParseCompileOptions(JSContext* cx, Compi
     if (!JS_GetProperty(cx, opts, "fileName", &v))
         return false;
     if (v.isNull()) {
         options.setFile(nullptr);
     } else if (!v.isUndefined()) {
         s = ToString(cx, v);
         if (!s)
             return false;
-        fileNameBytes = JS_EncodeStringToLatin1(cx, s);
-        if (!fileNameBytes)
-            return false;
-        options.setFile(fileNameBytes.get());
+        char* fileName = fileNameBytes.encodeLatin1(cx, s);
+        if (!fileName)
+            return false;
+        options.setFile(fileName);
     }
 
     if (!JS_GetProperty(cx, opts, "element", &v))
         return false;
     if (v.isObject())
         options.setElement(&v.toObject());
 
     if (!JS_GetProperty(cx, opts, "elementAttributeName", &v))
@@ -1819,17 +1813,17 @@ Evaluate(JSContext* cx, unsigned argc, V
     }
 
     if (!code || (args.length() == 2 && args[1].isPrimitive())) {
         JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "evaluate");
         return false;
     }
 
     CompileOptions options(cx);
-    UniqueChars fileNameBytes;
+    JSAutoByteString fileNameBytes;
     RootedString displayURL(cx);
     RootedString sourceMapURL(cx);
     RootedObject global(cx, nullptr);
     bool catchTermination = false;
     bool loadBytecode = false;
     bool saveBytecode = false;
     bool saveIncrementalBytecode = false;
     bool assertEqBytecode = false;
@@ -2093,72 +2087,72 @@ Evaluate(JSContext* cx, unsigned argc, V
     }
 
     return JS_WrapValue(cx, args.rval());
 }
 
 JSString*
 js::shell::FileAsString(JSContext* cx, JS::HandleString pathnameStr)
 {
-    UniqueChars pathname = JS_EncodeStringToLatin1(cx, pathnameStr);
+    JSAutoByteString pathname(cx, pathnameStr);
     if (!pathname)
         return nullptr;
 
     FILE* file;
 
-    file = fopen(pathname.get(), "rb");
+    file = fopen(pathname.ptr(), "rb");
     if (!file) {
-        ReportCantOpenErrorUnknownEncoding(cx, pathname.get());
+        ReportCantOpenErrorUnknownEncoding(cx, pathname.ptr());
         return nullptr;
     }
 
     AutoCloseFile autoClose(file);
 
     if (fseek(file, 0, SEEK_END) != 0) {
-        pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
-        if (!pathname)
+        pathname.clear();
+        if (!pathname.encodeUtf8(cx, pathnameStr))
             return nullptr;
-        JS_ReportErrorUTF8(cx, "can't seek end of %s", pathname.get());
+        JS_ReportErrorUTF8(cx, "can't seek end of %s", pathname.ptr());
         return nullptr;
     }
 
     size_t len = ftell(file);
     if (fseek(file, 0, SEEK_SET) != 0) {
-        pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
-        if (!pathname)
+        pathname.clear();
+        if (!pathname.encodeUtf8(cx, pathnameStr))
             return nullptr;
-        JS_ReportErrorUTF8(cx, "can't seek start of %s", pathname.get());
+        JS_ReportErrorUTF8(cx, "can't seek start of %s", pathname.ptr());
         return nullptr;
     }
 
     UniqueChars buf(js_pod_malloc<char>(len + 1));
     if (!buf)
         return nullptr;
 
     size_t cc = fread(buf.get(), 1, len, file);
     if (cc != len) {
         if (ptrdiff_t(cc) < 0) {
-            ReportCantOpenErrorUnknownEncoding(cx, pathname.get());
+            ReportCantOpenErrorUnknownEncoding(cx, pathname.ptr());
         } else {
-            pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
-            if (!pathname)
+            pathname.clear();
+            if (!pathname.encodeUtf8(cx, pathnameStr))
                 return nullptr;
-            JS_ReportErrorUTF8(cx, "can't read %s: short read", pathname.get());
+            JS_ReportErrorUTF8(cx, "can't read %s: short read", pathname.ptr());
         }
         return nullptr;
     }
 
     UniqueTwoByteChars ucbuf(
         JS::LossyUTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(buf.get(), len), &len).get()
     );
     if (!ucbuf) {
-        pathname = JS_EncodeStringToUTF8(cx, pathnameStr);
-        if (!pathname)
+        pathname.clear();
+        if (!pathname.encodeUtf8(cx, pathnameStr))
             return nullptr;
-        JS_ReportErrorUTF8(cx, "Invalid UTF-8 in file '%s'", pathname.get());
+        JS_ReportErrorUTF8(cx, "Invalid UTF-8 in file '%s'", pathname.ptr());
         return nullptr;
     }
 
     return JS_NewUCStringCopyN(cx, ucbuf.get(), len);
 }
 
 /*
  * Function to run scripts and return compilation + execution time. Semantics
@@ -2189,23 +2183,23 @@ Run(JSContext* cx, unsigned argc, Value*
 
     JS::SourceBufferHolder srcBuf(chars.twoByteRange().begin().get(), str->length(),
                                   JS::SourceBufferHolder::NoOwnership);
 
     RootedScript script(cx);
     int64_t startClock = PRMJ_Now();
     {
         /* FIXME: This should use UTF-8 (bug 987069). */
-        UniqueChars filename = JS_EncodeStringToLatin1(cx, str);
+        JSAutoByteString filename(cx, str);
         if (!filename)
             return false;
 
         JS::CompileOptions options(cx);
         options.setIntroductionType("js shell run")
-               .setFileAndLine(filename.get(), 1)
+               .setFileAndLine(filename.ptr(), 1)
                .setIsRunOnce(true)
                .setNoScriptRval(true);
         if (!JS_CompileUCScript(cx, srcBuf, options, &script))
             return false;
     }
 
     if (!JS_ExecuteScript(cx, script))
         return false;
@@ -2321,41 +2315,41 @@ ReadLineBuf(JSContext* cx, unsigned argc
 
         size_t len = 0;
         while(len < buflen) {
             if (currentBuf[len] == '\n')
                 break;
             len++;
         }
 
-        JSString* str = JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(currentBuf, len));
+        JSString* str = JS_NewStringCopyN(cx, currentBuf, len);
         if (!str)
             return false;
 
         if (currentBuf[len] == '\0')
             sc->readLineBufPos += len;
         else
             sc->readLineBufPos += len + 1;
 
         args.rval().setString(str);
         return true;
     }
 
     if (args.length() == 1) {
-        sc->readLineBuf = nullptr;
-        sc->readLineBufPos = 0;
+        if (sc->readLineBuf)
+            sc->readLineBuf.reset();
 
         RootedString str(cx, JS::ToString(cx, args[0]));
         if (!str)
             return false;
-        sc->readLineBuf = JS_EncodeStringToUTF8(cx, str);
+        sc->readLineBuf = UniqueChars(JS_EncodeStringToUTF8(cx, str));
         if (!sc->readLineBuf)
             return false;
 
-        args.rval().setUndefined();
+        sc->readLineBufPos = 0;
         return true;
     }
 
     JS_ReportErrorASCII(cx, "Must specify at most one argument");
     return false;
 }
 
 static bool
@@ -2367,20 +2361,21 @@ PutStr(JSContext* cx, unsigned argc, Val
         if (!gOutFile->isOpen()) {
             JS_ReportErrorASCII(cx, "output file is closed");
             return false;
         }
 
         RootedString str(cx, JS::ToString(cx, args[0]));
         if (!str)
             return false;
-        UniqueChars bytes = JS_EncodeStringToUTF8(cx, str);
+        char* bytes = JS_EncodeStringToUTF8(cx, str);
         if (!bytes)
             return false;
-        fputs(bytes.get(), gOutFile->fp);
+        fputs(bytes, gOutFile->fp);
+        JS_free(cx, bytes);
         fflush(gOutFile->fp);
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
@@ -2399,20 +2394,21 @@ PrintInternal(JSContext* cx, const CallA
         JS_ReportErrorASCII(cx, "output file is closed");
         return false;
     }
 
     for (unsigned i = 0; i < args.length(); i++) {
         RootedString str(cx, JS::ToString(cx, args[i]));
         if (!str)
             return false;
-        UniqueChars bytes = JS_EncodeStringToUTF8(cx, str);
+        char* bytes = JS_EncodeStringToUTF8(cx, str);
         if (!bytes)
             return false;
-        fprintf(file->fp, "%s%s", i ? " " : "", bytes.get());
+        fprintf(file->fp, "%s%s", i ? " " : "", bytes);
+        JS_free(cx, bytes);
     }
 
     fputc('\n', file->fp);
     fflush(file->fp);
 
     args.rval().setUndefined();
     return true;
 }
@@ -2506,23 +2502,23 @@ StopTimingMutator(JSContext* cx, unsigne
                 mutator_ms, mutator_ms / total_ms * 100.0, gc_ms, gc_ms / total_ms * 100.0);
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 static const char*
-ToSource(JSContext* cx, HandleValue vp, UniqueChars* bytes)
-{
-    RootedString str(cx, JS_ValueToSource(cx, vp));
+ToSource(JSContext* cx, MutableHandleValue vp, JSAutoByteString* bytes)
+{
+    JSString* str = JS_ValueToSource(cx, vp);
     if (str) {
-        *bytes = JS_EncodeStringToUTF8(cx, str);
-        if (*bytes)
-            return bytes->get();
+        vp.setString(str);
+        if (bytes->encodeLatin1(cx, str))
+            return bytes->ptr();
     }
     JS_ClearPendingException(cx);
     return "<<error converting value to string>>";
 }
 
 static bool
 AssertEq(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -2537,29 +2533,29 @@ AssertEq(JSContext* cx, unsigned argc, V
                                   "assertEq");
         return false;
     }
 
     bool same;
     if (!JS_SameValue(cx, args[0], args[1], &same))
         return false;
     if (!same) {
-        UniqueChars bytes0, bytes1;
+        JSAutoByteString bytes0, bytes1;
         const char* actual = ToSource(cx, args[0], &bytes0);
         const char* expected = ToSource(cx, args[1], &bytes1);
         if (args.length() == 2) {
-            JS_ReportErrorNumberUTF8(cx, my_GetErrorMessage, nullptr, JSSMSG_ASSERT_EQ_FAILED,
-                                     actual, expected);
+            JS_ReportErrorNumberLatin1(cx, my_GetErrorMessage, nullptr, JSSMSG_ASSERT_EQ_FAILED,
+                                       actual, expected);
         } else {
-            RootedString message(cx, args[2].toString());
-            UniqueChars bytes2 = JS_EncodeStringToUTF8(cx, message);
+            JSAutoByteString bytes2(cx, args[2].toString());
             if (!bytes2)
                 return false;
-            JS_ReportErrorNumberUTF8(cx, my_GetErrorMessage, nullptr, JSSMSG_ASSERT_EQ_FAILED_MSG,
-                                     actual, expected, bytes2.get());
+            JS_ReportErrorNumberLatin1(cx, my_GetErrorMessage, nullptr,
+                                       JSSMSG_ASSERT_EQ_FAILED_MSG,
+                                       actual, expected, bytes2.ptr());
         }
         return false;
     }
     args.rval().setUndefined();
     return true;
 }
 
 static JSScript*
@@ -3095,18 +3091,17 @@ DisassembleToString(JSContext* cx, unsig
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     Sprinter sprinter(cx);
     if (!sprinter.init())
         return false;
     if (!DisassembleToSprinter(cx, args.length(), vp, &sprinter))
         return false;
 
-    JS::ConstUTF8CharsZ utf8chars(sprinter.string(), strlen(sprinter.string()));
-    JSString* str = JS_NewStringCopyUTF8Z(cx, utf8chars);
+    JSString* str = JS_NewStringCopyZ(cx, sprinter.string());
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
 static bool
 Disassemble(JSContext* cx, unsigned argc, Value* vp)
@@ -3148,40 +3143,41 @@ DisassFile(JSContext* cx, unsigned argc,
         args.rval().setUndefined();
         return true;
     }
 
     // We should change DisassembleOptionParser to store CallArgs.
     JSString* str = JS::ToString(cx, HandleValue::fromMarkedLocation(&p.argv[0]));
     if (!str)
         return false;
-    UniqueChars filename = JS_EncodeStringToLatin1(cx, str);
+    JSAutoByteString filename(cx, str);
     if (!filename)
         return false;
     RootedScript script(cx);
 
     {
         CompileOptions options(cx);
         options.setIntroductionType("js shell disFile")
                .setUTF8(true)
-               .setFileAndLine(filename.get(), 1)
+               .setFileAndLine(filename.ptr(), 1)
                .setIsRunOnce(true)
                .setNoScriptRval(true);
 
-        if (!JS::Compile(cx, options, filename.get(), &script))
+        if (!JS::Compile(cx, options, filename.ptr(), &script))
             return false;
     }
 
     Sprinter sprinter(cx);
     if (!sprinter.init())
         return false;
-    if (!DisassembleScript(cx, script, nullptr, p.lines, p.recursive, p.sourceNotes, &sprinter))
-        return false;
-
-    fprintf(gOutFile->fp, "%s\n", sprinter.string());
+    bool ok = DisassembleScript(cx, script, nullptr, p.lines, p.recursive, p.sourceNotes, &sprinter);
+    if (ok)
+        fprintf(gOutFile->fp, "%s\n", sprinter.string());
+    if (!ok)
+        return false;
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 DisassWithSrc(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -3367,31 +3363,31 @@ static bool
 Crash(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 0)
         MOZ_CRASH("forced crash");
     RootedString message(cx, JS::ToString(cx, args[0]));
     if (!message)
         return false;
-    UniqueChars utf8chars = JS_EncodeStringToUTF8(cx, message);
+    char* utf8chars = JS_EncodeStringToUTF8(cx, message);
     if (!utf8chars)
         return false;
     if (args.get(1).isObject()) {
         RootedValue v(cx);
         RootedObject opts(cx, &args[1].toObject());
         if (!JS_GetProperty(cx, opts, "suppress_minidump", &v))
             return false;
         if (v.isBoolean() && v.toBoolean())
             js::NoteIntentionalCrash();
     }
 #ifndef DEBUG
-    MOZ_ReportCrash(utf8chars.get(), __FILE__, __LINE__);
+    MOZ_ReportCrash(utf8chars, __FILE__, __LINE__);
 #endif
-    MOZ_CRASH_UNSAFE_OOL(utf8chars.get());
+    MOZ_CRASH_UNSAFE_OOL(utf8chars);
 }
 
 static bool
 GetSLX(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedScript script(cx);
 
@@ -4183,17 +4179,17 @@ StackDump(JSContext* cx, unsigned argc, 
         JS_ReportErrorASCII(cx, "output file is closed");
         return false;
     }
 
     bool showArgs = ToBoolean(args.get(0));
     bool showLocals = ToBoolean(args.get(1));
     bool showThisProps = ToBoolean(args.get(2));
 
-    JS::UniqueChars buf = JS::FormatStackDump(cx, showArgs, showLocals, showThisProps);
+    JS::UniqueChars buf = JS::FormatStackDump(cx, nullptr, showArgs, showLocals, showThisProps);
     if (!buf) {
         fputs("Failed to format JavaScript stack for dump\n", gOutFile->fp);
         JS_ClearPendingException(cx);
     } else {
         fputs(buf.get(), gOutFile->fp);
     }
 
     args.rval().setUndefined();
@@ -4372,17 +4368,17 @@ ParseModule(JSContext* cx, unsigned argc
     if (args.length() > 1) {
         if (!args[1].isString()) {
             const char* typeName = InformalValueTypeName(args[1]);
             JS_ReportErrorASCII(cx, "expected filename string, got %s", typeName);
             return false;
         }
 
         RootedString str(cx, args[1].toString());
-        filename = JS_EncodeStringToLatin1(cx, str);
+        filename.reset(JS_EncodeString(cx, str));
         if (!filename)
             return false;
 
         options.setFileAndLine(filename.get(), 1);
     } else {
         options.setFileAndLine("<string>", 1);
     }
 
@@ -4746,31 +4742,30 @@ BinParse(JSContext* cx, unsigned argc, V
         RootedValue optionFormat(cx);
         if (!JS_GetProperty(cx, objOptions, "format", &optionFormat))
             return false;
 
         if (optionFormat.isUndefined()) {
             // By default, `useMultipart` is `true`.
             useMultipart = true;
         } else if (optionFormat.isString()) {
-            RootedLinearString linearFormat(cx, optionFormat.toString()->ensureLinear(cx));
-            if (!linearFormat)
-                return false;
+            RootedString stringFormat(cx);
+            stringFormat = optionFormat.toString();
+            JS::Rooted<JSLinearString*> linearFormat(cx);
+            linearFormat = stringFormat->ensureLinear(cx);
             if (StringEqualsAscii(linearFormat, "multipart")) {
                 useMultipart = true;
             } else if (StringEqualsAscii(linearFormat, "simple")) {
                 useMultipart = false;
             } else {
-                UniqueChars printable = JS_EncodeStringToUTF8(cx, linearFormat);
-                if (!printable)
-                    return false;
-
+                JSAutoByteString printable;
                 JS_ReportErrorUTF8(cx,
                                    "Unknown value for option `format`, expected 'multipart' or "
-                                   "'simple', got %s", printable.get());
+                                   "'simple', got %s",
+                                   ValueToPrintableUTF8(cx, optionFormat, &printable));
                 return false;
             }
         } else {
             const char* typeName = InformalValueTypeName(optionFormat);
             JS_ReportErrorASCII(cx, "option `format` should be a string, got %s", typeName);
             return false;
         }
     }
@@ -4983,17 +4978,17 @@ OffThreadCompileScript(JSContext* cx, un
         return false;
     }
     if (!args[0].isString()) {
         const char* typeName = InformalValueTypeName(args[0]);
         JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
         return false;
     }
 
-    UniqueChars fileNameBytes;
+    JSAutoByteString fileNameBytes;
     CompileOptions options(cx);
     options.setIntroductionType("js shell offThreadCompileScript")
            .setFileAndLine("<string>", 1);
 
     if (args.length() >= 2) {
         if (args[1].isPrimitive()) {
             JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
                                       "evaluate");
@@ -5088,17 +5083,17 @@ OffThreadCompileModule(JSContext* cx, un
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 1 || !args[0].isString()) {
         JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
                                   "offThreadCompileModule");
         return false;
     }
 
-    UniqueChars fileNameBytes;
+    JSAutoByteString fileNameBytes;
     CompileOptions options(cx);
     options.setIntroductionType("js shell offThreadCompileModule")
            .setFileAndLine("<string>", 1);
     options.setIsRunOnce(true)
            .setSourceIsLazy(false);
     options.forceAsync = true;
 
     JSString* scriptContents = args[0].toString();
@@ -5192,17 +5187,17 @@ OffThreadDecodeScript(JSContext* cx, uns
     }
     if (!args[0].isObject() || !CacheEntry_isCacheEntry(&args[0].toObject())) {
         const char* typeName = InformalValueTypeName(args[0]);
         JS_ReportErrorASCII(cx, "expected cache entry, got %s", typeName);
         return false;
     }
     RootedObject cacheEntry(cx, &args[0].toObject());
 
-    UniqueChars fileNameBytes;
+    JSAutoByteString fileNameBytes;
     CompileOptions options(cx);
     options.setIntroductionType("js shell offThreadDecodeScript")
            .setFileAndLine("<string>", 1);
 
     if (args.length() >= 2) {
         if (args[1].isPrimitive()) {
             JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
                                       "evaluate");
@@ -5330,16 +5325,23 @@ class AutoCStringVector
     }
     char* operator[](size_t i) const {
         return argv_[i];
     }
     void replace(size_t i, UniqueChars arg) {
         js_free(argv_[i]);
         argv_[i] = arg.release();
     }
+    const char* back() const {
+        return argv_.back();
+    }
+    void replaceBack(UniqueChars arg) {
+        js_free(argv_.back());
+        argv_.back() = arg.release();
+    }
 };
 
 #if defined(XP_WIN)
 static bool
 EscapeForShell(JSContext* cx, AutoCStringVector& argv)
 {
     // Windows will break arguments in argv by various spaces, so we wrap each
     // argument in quotes and escape quotes within. Even with quotes, \ will be
@@ -5408,42 +5410,36 @@ NestedShell(JSContext* cx, unsigned argc
     // Propagate selected flags from the current shell
     for (unsigned i = 0; i < sPropagatedFlags.length(); i++) {
         UniqueChars flags = DuplicateString(cx, sPropagatedFlags[i]);
         if (!flags || !argv.append(std::move(flags)))
             return false;
     }
 
     // The arguments to nestedShell are stringified and append to argv.
+    RootedString str(cx);
     for (unsigned i = 0; i < args.length(); i++) {
-        JSString* str = ToString(cx, args[i]);
+        str = ToString(cx, args[i]);
         if (!str)
             return false;
 
-        JSLinearString* linear = str->ensureLinear(cx);
-        if (!linear)
-            return false;
-
-        UniqueChars arg;
-        if (StringEqualsAscii(linear, "--js-cache") && jsCacheDir) {
-            // As a special case, if the caller passes "--js-cache", use
-            // "--js-cache=$(jsCacheDir)" instead.
-            arg = JS_smprintf("--js-cache=%s", jsCacheDir);
-            if (!arg) {
+        UniqueChars arg(JS_EncodeString(cx, str));
+        if (!arg || !argv.append(std::move(arg)))
+            return false;
+
+        // As a special case, if the caller passes "--js-cache", replace that
+        // with "--js-cache=$(jsCacheDir)"
+        if (!strcmp(argv.back(), "--js-cache") && jsCacheDir) {
+            UniqueChars newArg = JS_smprintf("--js-cache=%s", jsCacheDir);
+            if (!newArg) {
                 JS_ReportOutOfMemory(cx);
                 return false;
             }
-        } else {
-            arg = JS_EncodeStringToLatin1(cx, str);
-            if (!arg)
-                return false;
-        }
-
-        if (!argv.append(std::move(arg)))
-            return false;
+            argv.replaceBack(std::move(newArg));
+        }
     }
 
     // execv assumes argv is null-terminated
     if (!argv.append(nullptr))
         return false;
 
     int status = 0;
 #if defined(XP_WIN)
@@ -6960,17 +6956,17 @@ class ShellAutoEntryMonitor : JS::dbg::A
 
     void Entry(JSContext* cx, JSFunction* function, JS::HandleValue asyncStack,
                const char* asyncCause) override {
         MOZ_ASSERT(!enteredWithoutExit);
         enteredWithoutExit = true;
 
         RootedString displayId(cx, JS_GetFunctionDisplayId(function));
         if (displayId) {
-            UniqueChars displayIdStr = JS_EncodeStringToUTF8(cx, displayId);
+            UniqueChars displayIdStr(JS_EncodeStringToUTF8(cx, displayId));
             if (!displayIdStr) {
                 // We report OOM in buildResult.
                 cx->recoverFromOutOfMemory();
                 oom = true;
                 return;
             }
             oom = !log.append(std::move(displayIdStr));
             return;
@@ -7158,21 +7154,21 @@ SetARMHwCapFlags(JSContext* cx, unsigned
         return false;
     }
 
     RootedString flagsListString(cx, JS::ToString(cx, args.get(0)));
     if (!flagsListString)
         return false;
 
 #if defined(JS_CODEGEN_ARM)
-    UniqueChars flagsList = JS_EncodeStringToLatin1(cx, flagsListString);
+    JSAutoByteString flagsList(cx, flagsListString);
     if (!flagsList)
         return false;
 
-    jit::ParseARMHwCapFlags(flagsList.get());
+    jit::ParseARMHwCapFlags(flagsList.ptr());
 #endif
 
     args.rval().setUndefined();
     return true;
 }
 
 #ifndef __AFL_HAVE_MANUAL_CONTROL
 # define __AFL_LOOP(x) true
@@ -8196,17 +8192,17 @@ PrintStackTrace(JSContext* cx, HandleVal
     if (!stackObj)
         return true;
 
     JSPrincipals* principals = exnObj->as<ErrorObject>().realm()->principals();
     RootedString stackStr(cx);
     if (!BuildStackString(cx, principals, stackObj, &stackStr, 2))
         return false;
 
-    UniqueChars stack = JS_EncodeStringToUTF8(cx, stackStr);
+    UniqueChars stack(JS_EncodeStringToUTF8(cx, stackStr));
     if (!stack)
         return false;
 
     FILE* fp = ErrorFilePointer();
     fputs("Stack:\n", fp);
     fputs(stack.get(), fp);
 
     return true;
@@ -9055,17 +9051,17 @@ ProcessArgs(JSContext* cx, OptionParser*
         RootedString jspath(cx, JS_NewStringCopyZ(cx, path));
         if (!jspath)
             return false;
 
         JSString* absolutePath = js::shell::ResolvePath(cx, jspath, RootRelative);
         if (!absolutePath)
             return false;
 
-        sc->moduleLoadPath = JS_EncodeStringToLatin1(cx, absolutePath);
+        sc->moduleLoadPath = UniqueChars(JS_EncodeString(cx, absolutePath));
     } else {
         sc->moduleLoadPath = js::shell::GetCWD();
     }
 
     if (!sc->moduleLoadPath)
         return false;
 
     if (!modulePaths.empty() && !InitModuleLoader(cx))
--- a/js/src/vm/BytecodeUtil.cpp
+++ b/js/src/vm/BytecodeUtil.cpp
@@ -27,16 +27,17 @@
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "builtin/String.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/SourceNotes.h"
 #include "gc/FreeOp.h"
 #include "gc/GCInternals.h"
+#include "js/AutoByteString.h"
 #include "js/CharacterEncoding.h"
 #include "js/Printf.h"
 #include "util/StringBuffer.h"
 #include "util/Text.h"
 #include "vm/CodeCoverage.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
@@ -1125,65 +1126,82 @@ js::DumpScript(JSContext* cx, JSScript* 
     if (!sprinter.init())
         return false;
     RootedScript script(cx, scriptArg);
     bool ok = Disassemble(cx, script, true, &sprinter);
     fprintf(fp, "%s", sprinter.string());
     return ok;
 }
 
-static UniqueChars
-ToDisassemblySource(JSContext* cx, HandleValue v)
+static bool
+ToDisassemblySource(JSContext* cx, HandleValue v, JSAutoByteString* bytes)
 {
-    if (v.isString())
-        return QuoteString(cx, v.toString(), '"');
-
-    if (JS::RuntimeHeapIsBusy())
-        return DuplicateString(cx, "<value>");
+    if (v.isString()) {
+        Sprinter sprinter(cx);
+        if (!sprinter.init())
+            return false;
+        char* nbytes = QuoteString(&sprinter, v.toString(), '"');
+        if (!nbytes)
+            return false;
+        UniqueChars copy = JS_smprintf("%s", nbytes);
+        if (!copy) {
+            ReportOutOfMemory(cx);
+            return false;
+        }
+        bytes->initBytes(std::move(copy));
+        return true;
+    }
+
+    if (JS::RuntimeHeapIsBusy()) {
+        UniqueChars source = JS_smprintf("<value>");
+        if (!source) {
+            ReportOutOfMemory(cx);
+            return false;
+        }
+        bytes->initBytes(std::move(source));
+        return true;
+    }
 
     if (v.isObject()) {
         JSObject& obj = v.toObject();
 
         if (obj.is<JSFunction>()) {
             RootedFunction fun(cx, &obj.as<JSFunction>());
             JSString* str = JS_DecompileFunction(cx, fun);
             if (!str)
-                return nullptr;
-            return StringToNewUTF8CharsZ(cx, *str);
+                return false;
+            return bytes->encodeLatin1(cx, str);
         }
 
         if (obj.is<RegExpObject>()) {
             JSString* source = obj.as<RegExpObject>().toString(cx);
             if (!source)
-                return nullptr;
-            return StringToNewUTF8CharsZ(cx, *source);
+                return false;
+            return bytes->encodeLatin1(cx, source);
         }
     }
 
-    JSString* str = ValueToSource(cx, v);
-    if (!str)
-        return nullptr;
-    return QuoteString(cx, str);
+    return !!ValueToPrintableLatin1(cx, v, bytes, true);
 }
 
 static bool
-ToDisassemblySource(JSContext* cx, HandleScope scope, UniqueChars* bytes)
+ToDisassemblySource(JSContext* cx, HandleScope scope, JSAutoByteString* bytes)
 {
     UniqueChars source = JS_smprintf("%s {", ScopeKindString(scope->kind()));
     if (!source) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) {
-        UniqueChars nameBytes = AtomToPrintableString(cx, bi.name());
-        if (!nameBytes)
+        JSAutoByteString nameBytes;
+        if (!AtomToPrintableString(cx, bi.name(), &nameBytes))
             return false;
 
-        source = JS_sprintf_append(std::move(source), "%s: ", nameBytes.get());
+        source = JS_sprintf_append(std::move(source), "%s: ", nameBytes.ptr());
         if (!source) {
             ReportOutOfMemory(cx);
             return false;
         }
 
         BindingLocation loc = bi.location();
         switch (loc.kind()) {
           case BindingLocation::Kind::Global:
@@ -1226,17 +1244,17 @@ ToDisassemblySource(JSContext* cx, Handl
     }
 
     source = JS_sprintf_append(std::move(source), "}");
     if (!source) {
         ReportOutOfMemory(cx);
         return false;
     }
 
-    *bytes = std::move(source);
+    bytes->initBytes(std::move(source));
     return true;
 }
 
 static bool
 DumpJumpOrigins(HandleScript script, jsbytecode* pc, const BytecodeParser* parser, Sprinter* sp)
 {
     bool called = false;
     auto callback = [&script, &sp, &called](jsbytecode* pc, BytecodeParser::JumpKind kind) {
@@ -1398,83 +1416,83 @@ Disassemble1(JSContext* cx, HandleScript
         ptrdiff_t off = GET_JUMP_OFFSET(pc);
         if (!sp->jsprintf(" %u (%+d)", unsigned(loc + int(off)), int(off)))
             return 0;
         break;
       }
 
       case JOF_SCOPE: {
         RootedScope scope(cx, script->getScope(GET_UINT32_INDEX(pc)));
-        UniqueChars bytes;
+        JSAutoByteString bytes;
         if (!ToDisassemblySource(cx, scope, &bytes))
             return 0;
-        if (!sp->jsprintf(" %s", bytes.get()))
+        if (!sp->jsprintf(" %s", bytes.ptr()))
             return 0;
         break;
       }
 
       case JOF_ENVCOORD: {
         RootedValue v(cx,
             StringValue(EnvironmentCoordinateName(cx->caches().envCoordinateNameCache, script, pc)));
-        UniqueChars bytes = ToDisassemblySource(cx, v);
-        if (!bytes)
+        JSAutoByteString bytes;
+        if (!ToDisassemblySource(cx, v, &bytes))
             return 0;
         EnvironmentCoordinate ec(pc);
-        if (!sp->jsprintf(" %s (hops = %u, slot = %u)", bytes.get(), ec.hops(), ec.slot()))
+        if (!sp->jsprintf(" %s (hops = %u, slot = %u)", bytes.ptr(), ec.hops(), ec.slot()))
             return 0;
         break;
       }
 
       case JOF_ATOM: {
         RootedValue v(cx, StringValue(script->getAtom(GET_UINT32_INDEX(pc))));
-        UniqueChars bytes = ToDisassemblySource(cx, v);
-        if (!bytes)
+        JSAutoByteString bytes;
+        if (!ToDisassemblySource(cx, v, &bytes))
             return 0;
-        if (!sp->jsprintf(" %s", bytes.get()))
+        if (!sp->jsprintf(" %s", bytes.ptr()))
             return 0;
         break;
       }
 
       case JOF_DOUBLE: {
         RootedValue v(cx, script->getConst(GET_UINT32_INDEX(pc)));
-        UniqueChars bytes = ToDisassemblySource(cx, v);
-        if (!bytes)
+        JSAutoByteString bytes;
+        if (!ToDisassemblySource(cx, v, &bytes))
             return 0;
-        if (!sp->jsprintf(" %s", bytes.get()))
+        if (!sp->jsprintf(" %s", bytes.ptr()))
             return 0;
         break;
       }
 
       case JOF_OBJECT: {
         /* Don't call obj.toSource if analysis/inference is active. */
         if (script->zone()->types.activeAnalysis) {
             if (!sp->jsprintf(" object"))
                 return 0;
             break;
         }
 
         JSObject* obj = script->getObject(GET_UINT32_INDEX(pc));
         {
+            JSAutoByteString bytes;
             RootedValue v(cx, ObjectValue(*obj));
-            UniqueChars bytes = ToDisassemblySource(cx, v);
-            if (!bytes)
+            if (!ToDisassemblySource(cx, v, &bytes))
                 return 0;
-            if (!sp->jsprintf(" %s", bytes.get()))
+            if (!sp->jsprintf(" %s", bytes.ptr()))
                 return 0;
         }
         break;
       }
 
       case JOF_REGEXP: {
         js::RegExpObject* obj = script->getRegExp(pc);
+        JSAutoByteString bytes;
         RootedValue v(cx, ObjectValue(*obj));
-        UniqueChars bytes = ToDisassemblySource(cx, v);
-        if (!bytes)
+        if (!ToDisassemblySource(cx, v, &bytes))
             return 0;
-        if (!sp->jsprintf(" %s", bytes.get()))
+        if (!sp->jsprintf(" %s", bytes.ptr()))
             return 0;
         break;
       }
 
       case JOF_TABLESWITCH:
       {
         int32_t i, low, high;
 
@@ -1623,17 +1641,17 @@ struct ExpressionDecompiler
 #endif
     {}
     bool init();
     bool decompilePCForStackOperand(jsbytecode* pc, int i);
     bool decompilePC(jsbytecode* pc, uint8_t defIndex);
     bool decompilePC(const OffsetAndDefIndex& offsetAndDefIndex);
     JSAtom* getArg(unsigned slot);
     JSAtom* loadAtom(jsbytecode* pc);
-    bool quote(JSString* s, char quote);
+    bool quote(JSString* s, uint32_t quote);
     bool write(const char* s);
     bool write(JSString* str);
     UniqueChars getOutput();
 #if defined(DEBUG) || defined(JS_JITSPEW)
     void setStackDump() {
         isStackDump = true;
     }
 #endif
@@ -2092,19 +2110,19 @@ bool
 ExpressionDecompiler::write(JSString* str)
 {
     if (str == cx->names().dotThis)
         return write("this");
     return sprinter.putString(str);
 }
 
 bool
-ExpressionDecompiler::quote(JSString* s, char quote)
+ExpressionDecompiler::quote(JSString* s, uint32_t quote)
 {
-    return QuoteString(&sprinter, s, quote);
+    return QuoteString(&sprinter, s, quote) != nullptr;
 }
 
 JSAtom*
 ExpressionDecompiler::loadAtom(jsbytecode* pc)
 {
     return script->getAtom(pc);
 }
 
@@ -2294,17 +2312,17 @@ js::DecompileValueGenerator(JSContext* c
     if (!fallback) {
         if (v.isUndefined())
             return DuplicateString(cx, js_undefined_str); // Prevent users from seeing "(void 0)"
         fallback = ValueToSource(cx, v);
         if (!fallback)
             return nullptr;
     }
 
-    return StringToNewUTF8CharsZ(cx, *fallback);
+    return UniqueChars(JS_EncodeString(cx, fallback));
 }
 
 static bool
 DecompileArgumentFromStack(JSContext* cx, int formalIndex, UniqueChars* res)
 {
     MOZ_ASSERT(formalIndex >= 0);
 
     *res = nullptr;
@@ -2367,32 +2385,34 @@ DecompileArgumentFromStack(JSContext* cx
         return false;
     if (!ed.decompilePCForStackOperand(current, formalStackIndex))
         return false;
 
     *res = ed.getOutput();
     return *res != nullptr;
 }
 
-JSString*
+UniqueChars
 js::DecompileArgument(JSContext* cx, int formalIndex, HandleValue v)
 {
     {
         UniqueChars result;
         if (!DecompileArgumentFromStack(cx, formalIndex, &result))
             return nullptr;
-        if (result && strcmp(result.get(), "(intermediate value)")) {
-            JS::ConstUTF8CharsZ utf8chars(result.get(), strlen(result.get()));
-            return NewStringCopyUTF8Z<CanGC>(cx, utf8chars);
-        }
+        if (result && strcmp(result.get(), "(intermediate value)"))
+            return result;
     }
     if (v.isUndefined())
-        return cx->names().undefined; // Prevent users from seeing "(void 0)"
-
-    return ValueToSource(cx, v);
+        return DuplicateString(cx, js_undefined_str); // Prevent users from seeing "(void 0)"
+
+    RootedString fallback(cx, ValueToSource(cx, v));
+    if (!fallback)
+        return nullptr;
+
+    return UniqueChars(JS_EncodeString(cx, fallback));
 }
 
 extern bool
 js::IsValidBytecodeOffset(JSContext* cx, JSScript* script, size_t offset)
 {
     // This could be faster (by following jump instructions if the target is <= offset).
     for (BytecodeRange r(cx, script); !r.empty(); r.popFront()) {
         size_t here = r.frontOffset();
@@ -2689,18 +2709,17 @@ GetPCCountJSON(JSContext* cx, const Scri
             if (!ed.init())
                 return false;
             // defIndex passed here is not used.
             if (!ed.decompilePC(pc, /* defIndex = */ 0))
                 return false;
             UniqueChars text = ed.getOutput();
             if (!text)
                 return false;
-            JS::ConstUTF8CharsZ utf8chars(text.get(), strlen(text.get()));
-            JSString* str = NewStringCopyUTF8Z<CanGC>(cx, utf8chars);
+            JSString* str = NewLatin1StringZ(cx, std::move(text));
             if (!AppendJSONProperty(buf, "text"))
                 return false;
             if (!str || !(str = StringToSource(cx, str)))
                 return false;
             if (!buf.append(str))
                 return false;
         }
 
--- a/js/src/vm/BytecodeUtil.h
+++ b/js/src/vm/BytecodeUtil.h
@@ -527,17 +527,17 @@ GetVariableBytecodeLength(jsbytecode* pc
 UniqueChars
 DecompileValueGenerator(JSContext* cx, int spindex, HandleValue v,
                         HandleString fallback, int skipStackHits = 0);
 
 /*
  * Decompile the formal argument at formalIndex in the nearest non-builtin
  * stack frame, falling back with converting v to source.
  */
-JSString*
+UniqueChars
 DecompileArgument(JSContext* cx, int formalIndex, HandleValue v);
 
 static inline unsigned
 GetBytecodeLength(jsbytecode* pc)
 {
     JSOp op = (JSOp)*pc;
     MOZ_ASSERT(op < JSOP_LIMIT);
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -20,17 +20,17 @@
 #include "frontend/Parser.h"
 #include "gc/FreeOp.h"
 #include "gc/HashUtil.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "gc/PublicIterators.h"
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineJIT.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/Date.h"
 #include "js/SourceBufferHolder.h"
 #include "js/StableStringChars.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/Vector.h"
 #include "js/Wrapper.h"
 #include "proxy/ScriptedProxyHandler.h"
 #include "util/Text.h"
@@ -481,20 +481,20 @@ ParseEvalOptions(JSContext* cx, HandleVa
 
     RootedValue v(cx);
     if (!JS_GetProperty(cx, opts, "url", &v))
         return false;
     if (!v.isUndefined()) {
         RootedString url_str(cx, ToString<CanGC>(cx, v));
         if (!url_str)
             return false;
-        UniqueChars url_bytes = JS_EncodeStringToLatin1(cx, url_str);
+        JSAutoByteString url_bytes(cx, url_str);
         if (!url_bytes)
             return false;
-        if (!options.setFilename(cx, url_bytes.get()))
+        if (!options.setFilename(cx, url_bytes.ptr()))
             return false;
     }
 
     if (!JS_GetProperty(cx, opts, "lineNumber", &v))
         return false;
     if (!v.isUndefined()) {
         uint32_t lineno;
         if (!ToUint32(cx, v, &lineno))
@@ -4476,17 +4476,17 @@ class MOZ_STACK_CLASS Debugger::ScriptQu
 
     /* A script must be in one of these realms to match the query. */
     RealmSet realms;
 
     /* If this is a string, matching scripts have urls equal to it. */
     RootedValue url;
 
     /* url as a C string. */
-    UniqueChars urlCString;
+    JSAutoByteString urlCString;
 
     /* If this is a string, matching scripts' sources have displayURLs equal to
      * it. */
     RootedLinearString displayURLString;
 
     /*
      * If this is a source referent, matching scripts will have sources equal
      * to this instance. Ideally we'd use a Maybe here, but Maybe interacts
@@ -4562,18 +4562,17 @@ class MOZ_STACK_CLASS Debugger::ScriptQu
     /*
      * Given that parseQuery or omittedQuery has been called, prepare to match
      * scripts. Set urlCString and displayURLChars as appropriate.
      */
     bool prepareQuery() {
         // Compute urlCString and displayURLChars, if a url or displayURL was
         // given respectively.
         if (url.isString()) {
-            urlCString = JS_EncodeStringToLatin1(cx, url.toString());
-            if (!urlCString)
+            if (!urlCString.encodeLatin1(cx, url.toString()))
                 return false;
         }
 
         return true;
     }
 
     bool delazifyScripts() {
         // All scripts in debuggee realms must be visible, so delazify
@@ -4604,24 +4603,24 @@ class MOZ_STACK_CLASS Debugger::ScriptQu
         //
         // * hasLine
         //   Only JSScript supports GetScriptLineExtent.
         return innermost || hasLine;
     }
 
     template <typename T>
     MOZ_MUST_USE bool commonFilter(T script, const JS::AutoRequireNoGC& nogc) {
-        if (urlCString) {
+        if (urlCString.ptr()) {
             bool gotFilename = false;
-            if (script->filename() && strcmp(script->filename(), urlCString.get()) == 0)
+            if (script->filename() && strcmp(script->filename(), urlCString.ptr()) == 0)
                 gotFilename = true;
 
             bool gotSourceURL = false;
             if (!gotFilename && script->scriptSource()->introducerFilename() &&
-                strcmp(script->scriptSource()->introducerFilename(), urlCString.get()) == 0)
+                strcmp(script->scriptSource()->introducerFilename(), urlCString.ptr()) == 0)
             {
                 gotSourceURL = true;
             }
             if (!gotFilename && !gotSourceURL)
                 return false;
         }
         if (displayURLString) {
             if (!script->scriptSource() || !script->scriptSource()->hasDisplayURL())
@@ -4916,17 +4915,17 @@ class MOZ_STACK_CLASS Debugger::ObjectQu
 
         if (!referent.is<JSObject>() || referent.exposeToJS().isUndefined())
             return true;
 
         JSObject* obj = referent.as<JSObject>();
 
         if (!className.isUndefined()) {
             const char* objClassName = obj->getClass()->name;
-            if (strcmp(objClassName, classNameCString.get()) != 0)
+            if (strcmp(objClassName, classNameCString.ptr()) != 0)
                 return true;
         }
 
         return objects.append(obj);
     }
 
   private:
     /* The context in which we should do our work. */
@@ -4937,26 +4936,25 @@ class MOZ_STACK_CLASS Debugger::ObjectQu
 
     /*
      * If this is non-null, matching objects will have a class whose name is
      * this property.
      */
     RootedValue className;
 
     /* The className member, as a C string. */
-    UniqueChars classNameCString;
+    JSAutoByteString classNameCString;
 
     /*
      * Given that either omittedQuery or parseQuery has been called, prepare the
      * query for matching objects.
      */
     bool prepareQuery() {
         if (className.isString()) {
-            classNameCString = JS_EncodeStringToLatin1(cx, className.toString());
-            if (!classNameCString)
+            if (!classNameCString.encodeLatin1(cx, className.toString()))
                 return false;
         }
 
         return true;
     }
 };
 
 bool
@@ -5427,17 +5425,17 @@ Debugger::wrapLazyScript(JSContext* cx, 
 JSObject*
 Debugger::wrapWasmScript(JSContext* cx, Handle<WasmInstanceObject*> wasmInstance)
 {
     Rooted<DebuggerScriptReferent> referent(cx, wasmInstance.get());
     return wrapVariantReferent(cx, referent);
 }
 
 static JSObject*
-DebuggerScript_check(JSContext* cx, HandleValue v, const char* fnname)
+DebuggerScript_check(JSContext* cx, const Value& v, const char* fnname)
 {
     JSObject* thisobj = NonNullObject(cx, v);
     if (!thisobj)
         return nullptr;
     if (thisobj->getClass() != &DebuggerScript_class) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
                                   "Debugger.Script", fnname, thisobj->getClass()->name);
         return nullptr;
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -3,16 +3,17 @@
  * 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/EnvironmentObject-inl.h"
 
 #include "builtin/ModuleObject.h"
 #include "gc/Policy.h"
+#include "js/AutoByteString.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/AsyncFunction.h"
 #include "vm/GlobalObject.h"
 #include "vm/Iteration.h"
 #include "vm/ProxyObject.h"
 #include "vm/Realm.h"
 #include "vm/Shape.h"
 #include "vm/Xdr.h"
@@ -1409,19 +1410,20 @@ LiveEnvironmentVal::staticAsserts()
 
 /*****************************************************************************/
 
 namespace {
 
 static void
 ReportOptimizedOut(JSContext* cx, HandleId id)
 {
-    if (UniqueChars printable = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier)) {
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_OPTIMIZED_OUT,
-                                 printable.get());
+    JSAutoByteString printable;
+    if (ValueToPrintableLatin1(cx, IdToValue(id), &printable)) {
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_OPTIMIZED_OUT,
+                                   printable.ptr());
     }
 }
 
 /*
  * DebugEnvironmentProxy is the handler for DebugEnvironmentProxy proxy
  * objects. Having a custom handler (rather than trying to reuse js::Wrapper)
  * gives us several important abilities:
  *  - We want to pass the EnvironmentObject as the receiver to forwarded scope
@@ -3352,19 +3354,21 @@ js::CheckVarNameConflict(JSContext* cx, 
         return false;
     }
     return true;
 }
 
 static void
 ReportCannotDeclareGlobalBinding(JSContext* cx, HandlePropertyName name, const char* reason)
 {
-    if (UniqueChars printable = AtomToPrintableString(cx, name)) {
-        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_DECLARE_GLOBAL_BINDING,
-                                  printable.get(), reason);
+    JSAutoByteString printable;
+    if (AtomToPrintableString(cx, name, &printable)) {
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
+                                   JSMSG_CANT_DECLARE_GLOBAL_BINDING,
+                                   printable.ptr(), reason);
     }
 }
 
 bool
 js::CheckCanDeclareGlobalBinding(JSContext* cx, Handle<GlobalObject*> global,
                                  HandlePropertyName name, bool isFunction)
 {
     RootedId id(cx, NameToId(name));
--- a/js/src/vm/ErrorObject.cpp
+++ b/js/src/vm/ErrorObject.cpp
@@ -8,16 +8,17 @@
 #include "vm/ErrorObject-inl.h"
 
 #include "mozilla/Range.h"
 
 #include <utility>
 
 #include "jsexn.h"
 
+#include "js/AutoByteString.h"
 #include "js/CallArgs.h"
 #include "js/CharacterEncoding.h"
 #include "vm/GlobalObject.h"
 #include "vm/SelfHosting.h"
 #include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
@@ -140,20 +141,20 @@ js::ErrorObject::getOrCreateErrorReport(
     // the nitty-gritty malloc stuff.
     JSErrorReport report;
 
     // Type.
     JSExnType type_ = type();
     report.exnType = type_;
 
     // Filename.
-    UniqueChars filenameStr = JS_EncodeStringToLatin1(cx, fileName(cx));
-    if (!filenameStr)
+    JSAutoByteString filenameStr;
+    if (!filenameStr.encodeLatin1(cx, fileName(cx)))
         return nullptr;
-    report.filename = filenameStr.get();
+    report.filename = filenameStr.ptr();
 
     // Coordinates.
     report.lineno = lineNumber();
     report.column = columnNumber();
 
     // Message. Note that |new Error()| will result in an undefined |message|
     // slot, so we need to explicitly substitute the empty string in that case.
     RootedString message(cx, getMessage());
--- a/js/src/vm/ForOfIterator.cpp
+++ b/js/src/vm/ForOfIterator.cpp
@@ -62,17 +62,17 @@ ForOfIterator::init(HandleValue iterable
     // Throw if obj[@@iterator] isn't callable.
     // js::Invoke is about to check for this kind of error anyway, but it would
     // throw an inscrutable error message about |method| rather than this nice
     // one about |obj|.
     if (!callee.isObject() || !callee.toObject().isCallable()) {
         UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, iterable, nullptr);
         if (!bytes)
             return false;
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE, bytes.get());
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE, bytes.get());
         return false;
     }
 
     RootedValue res(cx);
     if (!js::Call(cx, callee, iterable, &res))
         return false;
 
     if (!res.isObject())
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -26,17 +26,17 @@
 #include "builtin/ModuleObject.h"
 #include "builtin/Promise.h"
 #include "builtin/String.h"
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonAnalysis.h"
 #include "jit/Jit.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "util/StringBuffer.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #ifdef ENABLE_BIGINT
 #include "vm/BigIntType.h"
 #endif
 #include "vm/BytecodeUtil.h"
 #include "vm/Debugger.h"
@@ -1856,33 +1856,33 @@ js::ReportInNotObjectError(JSContext* cx
             if (!buf.appendSubstring(str, 0, MaxStringLength))
                 return nullptr;
             if (!buf.append("..."))
                 return nullptr;
             str = buf.finishString();
             if (!str)
                 return nullptr;
         }
-        return StringToNewUTF8CharsZ(cx, *str);
+        return UniqueChars(JS_EncodeString(cx, str));
     };
 
     if (lref.isString() && rref.isString()) {
         UniqueChars lbytes = uniqueCharsFromString(cx, lref);
         if (!lbytes)
             return;
         UniqueChars rbytes = uniqueCharsFromString(cx, rref);
         if (!rbytes)
             return;
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_IN_STRING,
-                                 lbytes.get(), rbytes.get());
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_IN_STRING,
+                                   lbytes.get(), rbytes.get());
         return;
     }
 
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_IN_NOT_OBJECT,
-                              InformalValueTypeName(rref));
+    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_IN_NOT_OBJECT,
+                               InformalValueTypeName(rref));
 }
 
 static MOZ_NEVER_INLINE bool
 Interpret(JSContext* cx, RunState& state)
 {
 /*
  * Define macros for an interpreter loop. Opcode dispatch may be either by a
  * switch statement or by indirect goto (aka a threaded interpreter), depending
@@ -5334,18 +5334,19 @@ js::NewArrayOperationWithTemplate(JSCont
     return obj;
 }
 
 void
 js::ReportRuntimeLexicalError(JSContext* cx, unsigned errorNumber, HandleId id)
 {
     MOZ_ASSERT(errorNumber == JSMSG_UNINITIALIZED_LEXICAL ||
                errorNumber == JSMSG_BAD_CONST_ASSIGN);
-    if (UniqueChars printable = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier))
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, printable.get());
+    JSAutoByteString printable;
+    if (ValueToPrintableLatin1(cx, IdToValue(id), &printable))
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, errorNumber, printable.ptr());
 }
 
 void
 js::ReportRuntimeLexicalError(JSContext* cx, unsigned errorNumber, HandlePropertyName name)
 {
     RootedId id(cx, NameToId(name));
     ReportRuntimeLexicalError(cx, errorNumber, id);
 }
@@ -5376,19 +5377,20 @@ js::ReportRuntimeLexicalError(JSContext*
     }
 
     ReportRuntimeLexicalError(cx, errorNumber, name);
 }
 
 void
 js::ReportRuntimeRedeclaration(JSContext* cx, HandlePropertyName name, const char* redeclKind)
 {
-    if (UniqueChars printable = AtomToPrintableString(cx, name)) {
-        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_REDECLARED_VAR, redeclKind,
-                                  printable.get());
+    JSAutoByteString printable;
+    if (AtomToPrintableString(cx, name, &printable)) {
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_REDECLARED_VAR,
+                                   redeclKind, printable.ptr());
     }
 }
 
 bool
 js::ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind)
 {
     switch (kind) {
       case CheckIsObjectKind::IteratorNext:
@@ -5454,30 +5456,29 @@ js::ThrowUninitializedThis(JSContext* cx
                 break;
             }
         }
         MOZ_ASSERT(fun);
     }
 
     if (fun->isDerivedClassConstructor()) {
         const char* name = "anonymous";
-        UniqueChars str;
+        JSAutoByteString str;
         if (fun->explicitName()) {
-            str = AtomToPrintableString(cx, fun->explicitName());
-            if (!str)
+            if (!AtomToPrintableString(cx, fun->explicitName(), &str))
                 return false;
-            name = str.get();
+            name = str.ptr();
         }
 
-        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNINITIALIZED_THIS, name);
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNINITIALIZED_THIS, name);
         return false;
     }
 
     MOZ_ASSERT(fun->isArrow());
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNINITIALIZED_THIS_ARROW);
+    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNINITIALIZED_THIS_ARROW);
     return false;
 }
 
 JSObject*
 js::HomeObjectSuperBase(JSContext* cx, HandleObject homeObj)
 {
     RootedObject superBase(cx);
 
--- a/js/src/vm/JSAtom.cpp
+++ b/js/src/vm/JSAtom.cpp
@@ -16,17 +16,17 @@
 #include "mozilla/Unused.h"
 
 #include <string.h>
 
 #include "jstypes.h"
 
 #include "builtin/String.h"
 #include "gc/Marking.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "util/Text.h"
 #include "vm/JSContext.h"
 #include "vm/SymbolType.h"
 #include "vm/Xdr.h"
 
 #include "gc/AtomMarking-inl.h"
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
@@ -111,20 +111,24 @@ inline JSAtom*
 js::AtomStateEntry::asPtr(JSContext* cx) const
 {
     JSAtom* atom = asPtrUnbarriered();
     if (!cx->helperThread())
         JSString::readBarrier(atom);
     return atom;
 }
 
-UniqueChars
-js::AtomToPrintableString(JSContext* cx, JSAtom* atom)
+const char*
+js::AtomToPrintableString(JSContext* cx, JSAtom* atom, JSAutoByteString* bytes)
 {
-    return QuoteString(cx, atom);
+    JSString* str = QuoteString(cx, atom, 0);
+    if (!str)
+        return nullptr;
+    bytes->initBytes(EncodeLatin1(cx, str));
+    return bytes->ptr();
 }
 
 #define DEFINE_PROTO_STRING(name,init,clasp) const char js_##name##_str[] = #name;
 JS_FOR_EACH_PROTOTYPE(DEFINE_PROTO_STRING)
 #undef DEFINE_PROTO_STRING
 
 #define CONST_CHAR_STR(idpart, id, text) const char js_##idpart##_str[] = text;
 FOR_EACH_COMMON_PROPERTYNAME(CONST_CHAR_STR)
--- a/js/src/vm/JSAtom.h
+++ b/js/src/vm/JSAtom.h
@@ -6,27 +6,28 @@
 
 #ifndef vm_JSAtom_h
 #define vm_JSAtom_h
 
 #include "mozilla/Maybe.h"
 
 #include "gc/Rooting.h"
 #include "js/TypeDecls.h"
-#include "js/Utility.h"
 #include "vm/CommonPropertyNames.h"
 
+class JSAutoByteString;
+
 namespace js {
 
 /*
  * Return a printable, lossless char[] representation of a string-type atom.
- * The returned string is guaranteed to contain only ASCII characters.
+ * The lifetime of the result matches the lifetime of bytes.
  */
-extern UniqueChars
-AtomToPrintableString(JSContext* cx, JSAtom* atom);
+extern const char*
+AtomToPrintableString(JSContext* cx, JSAtom* atom, JSAutoByteString* bytes);
 
 class PropertyName;
 
 }  /* namespace js */
 
 /* Well-known predefined C strings. */
 #define DECLARE_PROTO_STR(name,init,clasp) extern const char js_##name##_str[];
 JS_FOR_EACH_PROTOTYPE(DECLARE_PROTO_STR)
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -32,16 +32,17 @@
 #include "jspubtd.h"
 #include "jstypes.h"
 
 #include "builtin/String.h"
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
 #include "jit/Ion.h"
 #include "jit/PcScriptCache.h"
+#include "js/AutoByteString.h"
 #include "js/CharacterEncoding.h"
 #include "js/Printf.h"
 #ifdef JS_SIMULATOR_ARM64
 # include "jit/arm64/vixl/Simulator-vixl.h"
 #endif
 #ifdef JS_SIMULATOR_ARM
 # include "jit/arm/Simulator-arm.h"
 #endif
@@ -436,20 +437,20 @@ js::ReportUsageErrorASCII(JSContext* cx,
     RootedValue usage(cx);
     if (!JS_GetProperty(cx, callee, "usage", &usage))
         return;
 
     if (!usage.isString()) {
         JS_ReportErrorASCII(cx, "%s", msg);
     } else {
         RootedString usageStr(cx, usage.toString());
-        UniqueChars str = JS_EncodeStringToUTF8(cx, usageStr);
-        if (!str)
+        JSAutoByteString str;
+        if (!str.encodeUtf8(cx, usageStr))
             return;
-        JS_ReportErrorUTF8(cx, "%s. Usage: %s", msg, str.get());
+        JS_ReportErrorUTF8(cx, "%s. Usage: %s", msg, str.ptr());
     }
 }
 
 enum class PrintErrorKind {
     Error,
     Warning,
     StrictWarning,
     Note
@@ -887,18 +888,20 @@ js::ReportErrorNumberUCArray(JSContext* 
     ReportError(cx, &report, callback, userRef);
 
     return warning;
 }
 
 void
 js::ReportIsNotDefined(JSContext* cx, HandleId id)
 {
-    if (UniqueChars printable = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier))
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_DEFINED, printable.get());
+    JSAutoByteString printable;
+    if (!ValueToPrintableUTF8(cx, IdToValue(id), &printable))
+        return;
+    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_DEFINED, printable.ptr());
 }
 
 void
 js::ReportIsNotDefined(JSContext* cx, HandlePropertyName name)
 {
     RootedId id(cx, NameToId(name));
     ReportIsNotDefined(cx, id);
 }
@@ -914,91 +917,104 @@ js::ReportIsNullOrUndefinedForPropertyAc
         return;
     }
 
     UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
     if (!bytes)
         return;
 
     if (strcmp(bytes.get(), js_undefined_str) == 0 || strcmp(bytes.get(), js_null_str) == 0) {
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NO_PROPERTIES, bytes.get());
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NO_PROPERTIES,
+                                   bytes.get());
     } else if (v.isUndefined()) {
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, bytes.get(),
-                                 js_undefined_str);
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
+                                   bytes.get(), js_undefined_str);
     } else {
         MOZ_ASSERT(v.isNull());
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, bytes.get(),
-                                 js_null_str);
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
+                                   bytes.get(), js_null_str);
     }
 }
 
+char*
+EncodeIdAsLatin1(JSContext* cx, HandleId id, JSAutoByteString& bytes)
+{
+    RootedValue idVal(cx, IdToValue(id));
+    RootedString idStr(cx, ValueToSource(cx, idVal));
+    if (!idStr)
+        return nullptr;
+
+    return bytes.encodeLatin1(cx, idStr);
+}
+
 void
 js::ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx, HandleValue v, HandleId key,
                                              bool reportScanStack)
 {
     MOZ_ASSERT(v.isNullOrUndefined());
 
-    UniqueChars keyBytes = IdToPrintableUTF8(cx, key, IdToPrintableBehavior::IdIsPropertyKey);
-    if (!keyBytes)
+    JSAutoByteString keyBytes;
+    if (!EncodeIdAsLatin1(cx, key, keyBytes))
         return;
 
     if (!reportScanStack) {
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
-                                 keyBytes.get(),
-                                 v.isUndefined() ? js_undefined_str : js_null_str);
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
+                                   keyBytes.ptr(),
+                                   v.isUndefined() ? js_undefined_str : js_null_str);
         return;
     }
 
     UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
     if (!bytes)
         return;
 
     if (strcmp(bytes.get(), js_undefined_str) == 0 || strcmp(bytes.get(), js_null_str) == 0) {
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
-                                 keyBytes.get(), bytes.get());
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL,
+                                   keyBytes.ptr(), bytes.get());
     } else if (v.isUndefined()) {
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL_EXPR,
-                                 bytes.get(), js_undefined_str, keyBytes.get());
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL_EXPR,
+                                   bytes.get(), js_undefined_str, keyBytes.ptr());
     } else {
         MOZ_ASSERT(v.isNull());
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL_EXPR,
-                                 bytes.get(), js_null_str, keyBytes.get());
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_PROPERTY_FAIL_EXPR,
+                                   bytes.get(), js_null_str, keyBytes.ptr());
     }
 }
 
 void
 js::ReportMissingArg(JSContext* cx, HandleValue v, unsigned arg)
 {
     char argbuf[11];
     UniqueChars bytes;
 
     SprintfLiteral(argbuf, "%u", arg);
     if (IsFunctionObject(v)) {
         RootedAtom name(cx, v.toObject().as<JSFunction>().explicitName());
         bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, name);
         if (!bytes)
             return;
     }
-    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_MISSING_FUN_ARG,
-                             argbuf, bytes ? bytes.get() : "");
+    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
+                               JSMSG_MISSING_FUN_ARG,
+                               argbuf, bytes ? bytes.get() : "");
 }
 
 bool
 js::ReportValueErrorFlags(JSContext* cx, unsigned flags, const unsigned errorNumber,
                           int spindex, HandleValue v, HandleString fallback,
                           const char* arg1, const char* arg2)
 {
     MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount >= 1);
     MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount <= 3);
     UniqueChars bytes = DecompileValueGenerator(cx, spindex, v, fallback);
     if (!bytes)
         return false;
 
-    return JS_ReportErrorFlagsAndNumberUTF8(cx, flags, GetErrorMessage, nullptr, errorNumber,
-                                            bytes.get(), arg1, arg2);
+    return JS_ReportErrorFlagsAndNumberLatin1(cx, flags, GetErrorMessage, nullptr, errorNumber,
+                                              bytes.get(), arg1, arg2);
 }
 
 JSObject*
 js::CreateErrorNotesArray(JSContext* cx, JSErrorReport* report)
 {
     RootedArrayObject notesArray(cx, NewDenseEmptyArray(cx));
     if (!notesArray)
         return nullptr;
--- a/js/src/vm/JSFunction-inl.h
+++ b/js/src/vm/JSFunction-inl.h
@@ -6,30 +6,29 @@
 
 #ifndef vm_JSFunction_inl_h
 #define vm_JSFunction_inl_h
 
 #include "vm/JSFunction.h"
 
 #include "gc/Allocator.h"
 #include "gc/GCTrace.h"
-#include "js/CharacterEncoding.h"
 #include "vm/EnvironmentObject.h"
 
 #include "vm/JSObject-inl.h"
 
+class JSAutoByteString;
+
 namespace js {
 
 inline const char*
-GetFunctionNameBytes(JSContext* cx, JSFunction* fun, UniqueChars* bytes)
+GetFunctionNameBytes(JSContext* cx, JSFunction* fun, JSAutoByteString* bytes)
 {
-    if (JSAtom* name = fun->explicitName()) {
-        *bytes = EncodeLatin1(cx, name);
-        return bytes->get();
-    }
+    if (JSAtom* name = fun->explicitName())
+        return bytes->encodeLatin1(cx, name);
     return js_anonymous_str;
 }
 
 inline bool
 CanReuseFunctionForClone(JSContext* cx, HandleFunction fun)
 {
     if (!fun->isSingleton())
         return false;
--- a/js/src/vm/JSFunction.cpp
+++ b/js/src/vm/JSFunction.cpp
@@ -26,16 +26,17 @@
 #include "builtin/SelfHostingDefines.h"
 #include "builtin/String.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/TokenStream.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "jit/InlinableNatives.h"
 #include "jit/Ion.h"
+#include "js/AutoByteString.h"
 #include "js/CallNonGenericMethod.h"
 #include "js/CompileOptions.h"
 #include "js/Proxy.h"
 
 #include "js/SourceBufferHolder.h"
 #include "js/StableStringChars.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
@@ -2493,29 +2494,29 @@ js::ReportIncompatibleMethod(JSContext* 
     } else if (thisv.isSymbol()) {
         MOZ_ASSERT(clasp != &SymbolObject::class_);
     } else {
         MOZ_ASSERT(thisv.isUndefined() || thisv.isNull());
     }
 #endif
 
     if (JSFunction* fun = ReportIfNotFunction(cx, args.calleev())) {
-        UniqueChars funNameBytes;
+        JSAutoByteString funNameBytes;
         if (const char* funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
             JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
                                        clasp->name, funName, InformalValueTypeName(thisv));
         }
     }
 }
 
 void
 js::ReportIncompatible(JSContext* cx, const CallArgs& args)
 {
     if (JSFunction* fun = ReportIfNotFunction(cx, args.calleev())) {
-        UniqueChars funNameBytes;
+        JSAutoByteString funNameBytes;
         if (const char* funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
             JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_METHOD,
                                        funName, "method", InformalValueTypeName(args.thisv()));
         }
     }
 }
 
 namespace JS {
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -30,17 +30,17 @@
 #endif
 #include "builtin/Eval.h"
 #include "builtin/Object.h"
 #include "builtin/String.h"
 #include "builtin/Symbol.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/Policy.h"
 #include "jit/BaselineJIT.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.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"
@@ -74,44 +74,45 @@
 #include "vm/StringObject-inl.h"
 #include "vm/TypedArrayObject-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 void
-js::ReportNotObject(JSContext* cx, HandleValue v)
+js::ReportNotObject(JSContext* cx, const Value& v)
 {
     MOZ_ASSERT(!v.isObject());
 
-    if (UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr)) {
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
-                                 bytes.get());
-    }
+    RootedValue value(cx, v);
+    UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, value, nullptr);
+    if (bytes)
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
+                                   bytes.get());
 }
 
 void
 js::ReportNotObjectArg(JSContext* cx, const char* nth, const char* fun, HandleValue v)
 {
     MOZ_ASSERT(!v.isObject());
 
-    UniqueChars bytes;
+    JSAutoByteString bytes;
     if (const char* chars = ValueToSourceForError(cx, v, bytes)) {
         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT_ARG,
                                    nth, fun, chars);
     }
 }
 
 void
 js::ReportNotObjectWithName(JSContext* cx, const char* name, HandleValue v)
 {
     MOZ_ASSERT(!v.isObject());
 
-    UniqueChars bytes;
+    JSAutoByteString bytes;
     if (const char* chars = ValueToSourceForError(cx, v, bytes)) {
         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT_NAME,
                                    name, chars);
     }
 }
 
 JS_PUBLIC_API(const char*)
 JS::InformalValueTypeName(const Value& v)
@@ -223,18 +224,18 @@ js::GetFirstArgumentAsObject(JSContext* 
         return false;
     }
 
     HandleValue v = args[0];
     if (!v.isObject()) {
         UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr);
         if (!bytes)
             return false;
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
-                                 bytes.get(), "not an object");
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
+                                   bytes.get(), "not an object");
         return false;
     }
 
     objp.set(&v.toObject());
     return true;
 }
 
 static bool
@@ -247,29 +248,34 @@ GetPropertyIfPresent(JSContext* cx, Hand
         vp.setUndefined();
         return true;
     }
 
     return GetProperty(cx, obj, obj, id, vp);
 }
 
 bool
-js::Throw(JSContext* cx, HandleId id, unsigned errorNumber, const char* details)
+js::Throw(JSContext* cx, jsid id, unsigned errorNumber, const char* details)
 {
     MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount == (details ? 2 : 1));
-    MOZ_ASSERT_IF(details, JS::StringIsASCII(details));
-
-    UniqueChars bytes = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
+
+    RootedValue idVal(cx, IdToValue(id));
+    JSString* idstr = ValueToSource(cx, idVal);
+    if (!idstr)
+       return false;
+    JSAutoByteString bytes(cx, idstr);
     if (!bytes)
         return false;
 
-    if (details)
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, bytes.get(), details);
-    else
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, bytes.get());
+    if (details) {
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, errorNumber, bytes.ptr(),
+                                   details);
+    } else {
+        JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, errorNumber, bytes.ptr());
+    }
 
     return false;
 }
 
 
 /*** PropertyDescriptor operations and DefineProperties ******************************************/
 
 static const char js_getter_str[] = "getter";
--- a/js/src/vm/JSObject.h
+++ b/js/src/vm/JSObject.h
@@ -1236,20 +1236,20 @@ template<XDRMode mode>
 XDRResult
 XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj);
 
 /*
  * Report a TypeError: "so-and-so is not an object".
  * Using NotNullObject is usually less code.
  */
 extern void
-ReportNotObject(JSContext* cx, HandleValue v);
+ReportNotObject(JSContext* cx, const Value& v);
 
 inline JSObject*
-NonNullObject(JSContext* cx, HandleValue v)
+NonNullObject(JSContext* cx, const Value& v)
 {
     if (v.isObject())
         return &v.toObject();
     ReportNotObject(cx, v);
     return nullptr;
 }
 
 
@@ -1287,17 +1287,17 @@ NonNullObjectWithName(JSContext* cx, con
 
 
 extern bool
 GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args, const char* method,
                          MutableHandleObject objp);
 
 /* Helper for throwing, always returns false. */
 extern bool
-Throw(JSContext* cx, HandleId id, unsigned errorNumber, const char* details = nullptr);
+Throw(JSContext* cx, jsid id, unsigned errorNumber, const char* details = nullptr);
 
 /*
  * ES6 rev 29 (6 Dec 2014) 7.3.13. Mark obj as non-extensible, and adjust each
  * of obj's own properties' attributes appropriately: each property becomes
  * non-configurable, and if level == Frozen, data properties become
  * non-writable as well.
  */
 extern bool
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -7,17 +7,17 @@
 #include "vm/NativeObject-inl.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Casting.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 
 #include "gc/Marking.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/Value.h"
 #include "vm/Debugger.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/UnboxedObject.h"
 
 #include "gc/Nursery-inl.h"
 #include "vm/ArrayObject-inl.h"
 #include "vm/EnvironmentObject-inl.h"
@@ -2299,22 +2299,19 @@ GetNonexistentProperty(JSContext* cx, Ha
     pc += CodeSpec[*pc].length;
     if (Detecting(cx, script, pc))
         return true;
 
     unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT;
     script->setWarnedAboutUndefinedProp();
 
     // Ok, bad undefined property reference: whine about it.
-    UniqueChars bytes = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
-    if (!bytes)
-        return false;
-
-    return JS_ReportErrorFlagsAndNumberUTF8(cx, flags, GetErrorMessage, nullptr,
-                                            JSMSG_UNDEFINED_PROP, bytes.get());
+    RootedValue val(cx, IdToValue(id));
+    return ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP, JSDVG_IGNORE_STACK, val,
+                                    nullptr, nullptr, nullptr);
 }
 
 /* The NoGC version of GetNonexistentProperty, present only to make types line up. */
 bool
 GetNonexistentProperty(JSContext* cx, const jsid& id, IsNameLookup nameLookup,
                        FakeMutableHandle<Value> vp)
 {
     return false;
@@ -2454,17 +2451,17 @@ js::GetNameBoundInEnvironment(JSContext*
         return GeneralizedGetProperty(cx, env, id, receiver, NameLookup, vp);
     return NativeGetPropertyInline<CanGC>(cx, env.as<NativeObject>(), receiver, id, NameLookup, vp);
 }
 
 
 /*** [[Set]] *************************************************************************************/
 
 static bool
-MaybeReportUndeclaredVarAssignment(JSContext* cx, HandleId id)
+MaybeReportUndeclaredVarAssignment(JSContext* cx, HandleString propname)
 {
     unsigned flags;
     {
         jsbytecode* pc;
         JSScript* script = cx->currentScript(&pc, JSContext::AllowCrossRealm::Allow);
         if (!script)
             return true;
 
@@ -2473,21 +2470,21 @@ MaybeReportUndeclaredVarAssignment(JSCon
         if (IsStrictSetPC(pc))
             flags = JSREPORT_ERROR;
         else if (cx->realm()->behaviors().extraWarnings(cx))
             flags = JSREPORT_WARNING | JSREPORT_STRICT;
         else
             return true;
     }
 
-    UniqueChars bytes = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier);
-    if (!bytes)
+    JSAutoByteString bytes;
+    if (!bytes.encodeUtf8(cx, propname))
         return false;
     return JS_ReportErrorFlagsAndNumberUTF8(cx, flags, GetErrorMessage, nullptr,
-                                            JSMSG_UNDECLARED_VAR, bytes.get());
+                                            JSMSG_UNDECLARED_VAR, bytes.ptr());
 }
 
 /*
  * Finish assignment to a shapeful data property of a native object obj. This
  * conforms to no standard and there is a lot of legacy baggage here.
  */
 static bool
 NativeSetExistingDataProperty(JSContext* cx, HandleNativeObject obj, HandleShape shape,
@@ -2593,17 +2590,18 @@ js::SetPropertyOnProto(JSContext* cx, Ha
  * steps 4.d.i and 5.
  */
 template <QualifiedBool IsQualified>
 static bool
 SetNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v,
                        HandleValue receiver, ObjectOpResult& result)
 {
     if (!IsQualified && receiver.isObject() && receiver.toObject().isUnqualifiedVarObj()) {
-        if (!MaybeReportUndeclaredVarAssignment(cx, id))
+        RootedString idStr(cx, JSID_TO_STRING(id));
+        if (!MaybeReportUndeclaredVarAssignment(cx, idStr))
             return false;
     }
 
     // Pure optimization for the common case. There's no point performing the
     // lookup in step 5.c again, as our caller just did it for us.
     if (IsQualified && receiver.isObject() && obj == &receiver.toObject()) {
         // Ensure that a custom GetOwnPropertyOp, if present, doesn't
         // introduce additional properties which weren't previously found by
--- a/js/src/vm/Printer.cpp
+++ b/js/src/vm/Printer.cpp
@@ -3,26 +3,24 @@
  * 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/Printer.h"
 
 #include "mozilla/PodOperations.h"
 #include "mozilla/Printf.h"
-#include "mozilla/RangedPtr.h"
 
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdio.h>
 
 #include "jsutil.h"
 
 #include "ds/LifoAlloc.h"
-#include "js/CharacterEncoding.h"
 #include "util/Text.h"
 #include "util/Windows.h"
 #include "vm/JSContext.h"
 
 using mozilla::PodCopy;
 
 namespace
 {
@@ -96,17 +94,17 @@ Sprinter::realloc_(size_t newSize)
     MOZ_ASSERT(newSize > (size_t) offset);
     char* newBuf = (char*) js_realloc(base, newSize);
     if (!newBuf) {
         reportOutOfMemory();
         return false;
     }
     base = newBuf;
     size = newSize;
-    base[size - 1] = '\0';
+    base[size - 1] = 0;
     return true;
 }
 
 Sprinter::Sprinter(JSContext* cx, bool shouldReportOOM)
   : context(cx),
 #ifdef DEBUG
     initialized(false),
 #endif
@@ -130,44 +128,44 @@ Sprinter::init()
     base = js_pod_malloc<char>(DefaultSize);
     if (!base) {
         reportOutOfMemory();
         return false;
     }
 #ifdef DEBUG
     initialized = true;
 #endif
-    *base = '\0';
+    *base = 0;
     size = DefaultSize;
-    base[size - 1] = '\0';
+    base[size - 1] = 0;
     return true;
 }
 
 void
 Sprinter::checkInvariants() const
 {
     MOZ_ASSERT(initialized);
     MOZ_ASSERT((size_t) offset < size);
-    MOZ_ASSERT(base[size - 1] == '\0');
+    MOZ_ASSERT(base[size - 1] == 0);
 }
 
-UniqueChars
+char*
 Sprinter::release()
 {
     checkInvariants();
     if (hadOOM_)
         return nullptr;
 
     char* str = base;
     base = nullptr;
     offset = size = 0;
 #ifdef DEBUG
     initialized = false;
 #endif
-    return UniqueChars(str);
+    return str;
 }
 
 char*
 Sprinter::stringAt(ptrdiff_t off) const
 {
     MOZ_ASSERT(off >= 0 && (size_t) off < size);
     return base + off;
 }
@@ -211,38 +209,45 @@ Sprinter::put(const char* s, size_t len)
         /* buffer was realloc'ed */
         if (base != oldBase)
             s = stringAt(s - oldBase);  /* this is where it lives now */
         memmove(bp, s, len);
     } else {
         js_memcpy(bp, s, len);
     }
 
-    bp[len] = '\0';
+    bp[len] = 0;
     return true;
 }
 
 bool
 Sprinter::putString(JSString* s)
 {
     InvariantChecker ic(this);
 
-    JSFlatString* flat = s->ensureFlat(context);
-    if (!flat)
-        return false;
-
-    size_t length = JS::GetDeflatedUTF8StringLength(flat);
+    size_t length = s->length();
 
     char* buffer = reserve(length);
     if (!buffer)
         return false;
 
-    JS::DeflateStringToUTF8Buffer(flat, mozilla::RangedPtr<char>(buffer, length));
+    JSLinearString* linear = s->ensureLinear(context);
+    if (!linear)
+        return false;
 
-    buffer[length] = '\0';
+    JS::AutoCheckCannotGC nogc;
+    if (linear->hasLatin1Chars()) {
+        PodCopy(reinterpret_cast<Latin1Char*>(buffer), linear->latin1Chars(nogc), length);
+    } else {
+        const char16_t* src = linear->twoByteChars(nogc);
+        for (size_t i = 0; i < length; i++)
+            buffer[i] = char(src[i]);
+    }
+
+    buffer[length] = 0;
     return true;
 }
 
 ptrdiff_t
 Sprinter::getOffset() const
 {
     return offset;
 }
@@ -278,24 +283,27 @@ const char js_EscapeMap[] = {
     '\v', 'v',
     '"',  '"',
     '\'', '\'',
     '\\', '\\',
     '\0'
 };
 
 template <typename CharT>
-static bool
-QuoteString(Sprinter* sp, const mozilla::Range<const CharT> chars, char quote)
+static char*
+QuoteString(Sprinter* sp, const mozilla::Range<const CharT> chars, char16_t quote)
 {
     using CharPtr = mozilla::RangedPtr<const CharT>;
 
+    /* Sample off first for later return value pointer computation. */
+    ptrdiff_t offset = sp->getOffset();
+
     if (quote) {
-        if (!sp->putChar(quote))
-            return false;
+        if (!sp->jsprintf("%c", char(quote)))
+            return nullptr;
     }
 
     const CharPtr end = chars.end();
 
     /* Loop control variables: end points at end of string sentinel. */
     for (CharPtr t = chars.begin(); t < end; ++t) {
         /* Move t forward from s past un-quote-worthy characters. */
         const CharPtr s = t;
@@ -306,73 +314,83 @@ QuoteString(Sprinter* sp, const mozilla:
                 break;
             c = *t;
         }
 
         {
             ptrdiff_t len = t - s;
             ptrdiff_t base = sp->getOffset();
             if (!sp->reserve(len))
-                return false;
+                return nullptr;
 
             for (ptrdiff_t i = 0; i < len; ++i)
                 (*sp)[base + i] = char(s[i]);
-            (*sp)[base + len] = '\0';
+            (*sp)[base + len] = 0;
         }
 
         if (t == end)
             break;
 
         /* Use js_EscapeMap, \u, or \x only if necessary. */
         const char* escape;
         if (!(c >> 8) && c != 0 && (escape = strchr(js_EscapeMap, int(c))) != nullptr) {
             if (!sp->jsprintf("\\%c", escape[1]))
-                return false;
+                return nullptr;
         } else {
             /*
              * Use \x only if the high byte is 0 and we're in a quoted string,
              * because ECMA-262 allows only \u, not \x, in Unicode identifiers
              * (see bug 621814).
              */
             if (!sp->jsprintf((quote && !(c >> 8)) ? "\\x%02X" : "\\u%04X", c))
-                return false;
+                return nullptr;
         }
     }
 
     /* Sprint the closing quote and return the quoted string. */
     if (quote) {
-        if (!sp->putChar(quote))
-            return false;
+        if (!sp->jsprintf("%c", char(quote)))
+            return nullptr;
     }
 
-    return true;
+    /*
+     * If we haven't Sprint'd anything yet, Sprint an empty string so that
+     * the return below gives a valid result.
+     */
+    if (offset == sp->getOffset()) {
+        if (!sp->put(""))
+            return nullptr;
+    }
+
+    return sp->stringAt(offset);
 }
 
-bool
-QuoteString(Sprinter* sp, JSString* str, char quote /*= '\0' */)
+char*
+QuoteString(Sprinter* sp, JSString* str, char16_t quote)
 {
     JSLinearString* linear = str->ensureLinear(sp->context);
     if (!linear)
-        return false;
+        return nullptr;
 
     JS::AutoCheckCannotGC nogc;
     return linear->hasLatin1Chars()
            ? QuoteString(sp, linear->latin1Range(nogc), quote)
            : QuoteString(sp, linear->twoByteRange(nogc), quote);
 }
 
-UniqueChars
-QuoteString(JSContext* cx, JSString* str, char quote /* = '\0' */)
+JSString*
+QuoteString(JSContext* cx, JSString* str, char16_t quote)
 {
     Sprinter sprinter(cx);
     if (!sprinter.init())
         return nullptr;
-    if (!QuoteString(&sprinter, str, quote))
+    char* bytes = QuoteString(&sprinter, str, quote);
+    if (!bytes)
         return nullptr;
-    return sprinter.release();
+    return NewStringCopyZ<CanGC>(cx, bytes);
 }
 
 Fprinter::Fprinter(FILE* fp)
   : file_(nullptr),
     init_(false)
 {
     init(fp);
 }
--- a/js/src/vm/Printer.h
+++ b/js/src/vm/Printer.h
@@ -10,17 +10,16 @@
 #include "mozilla/Attributes.h"
 
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <string.h>
 
 #include "js/TypeDecls.h"
-#include "js/Utility.h"
 
 namespace js {
 
 class LifoAlloc;
 
 // Generic printf interface, similar to an ostream in the standard library.
 //
 // This class is useful to make generic printers which can work either with a
@@ -96,17 +95,17 @@ class Sprinter final : public GenericPri
 
     // Initialize this sprinter, returns false on error.
     MOZ_MUST_USE bool init();
 
     void checkInvariants() const;
 
     const char* string() const { return base; }
     const char* stringEnd() const { return base + offset; }
-    JS::UniqueChars release();
+    char* release();
 
     // Returns the string at offset |off|.
     char* stringAt(ptrdiff_t off) const;
     // Returns the char at offset |off|.
     char& operator[](size_t off);
 
     // Attempt to reserve len + 1 space (for a trailing nullptr byte). If the
     // attempt succeeds, return a pointer to the start of that space and adjust the
@@ -207,24 +206,21 @@ class LSprinter final : public GenericPr
     // return true on success, false on failure.
     virtual bool put(const char* s, size_t len) override;
     using GenericPrinter::put; // pick up |inline bool put(const char* s);|
 };
 
 // Map escaped code to the letter/symbol escaped with a backslash.
 extern const char       js_EscapeMap[];
 
-// Return a C-string containing the chars in str, with any non-printing chars
-// escaped. If the optional quote parameter is present and is not '\0', quotes
-// (as specified by the quote argument) are also escaped, and the quote
-// character is appended at the beginning and end of the result string.
-// The returned string is guaranteed to contain only ASCII characters.
-extern JS::UniqueChars
-QuoteString(JSContext* cx, JSString* str, char quote = '\0');
+// Return a GC'ed string containing the chars in str, with any non-printing
+// chars or quotes (' or " as specified by the quote argument) escaped, and
+// with the quote character at the beginning and end of the result string.
+extern JSString*
+QuoteString(JSContext* cx, JSString* str, char16_t quote);
 
-// Appends the quoted string to the given Sprinter. Follows the same semantics
-// as QuoteString from above.
-extern bool
-QuoteString(Sprinter* sp, JSString* str, char quote = '\0');
+extern char*
+QuoteString(Sprinter* sp, JSString* str, char16_t quote);
+
 
 } // namespace js
 
 #endif // vm_Printer_h
--- a/js/src/vm/Probes.cpp
+++ b/js/src/vm/Probes.cpp
@@ -1,17 +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 "vm/Probes-inl.h"
 
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "vm/JSContext.h"
 
 #ifdef INCLUDE_MOZILLA_DTRACE
 #include "vm/JSScript-inl.h"
 #endif
 
 #define TYPEOF(cx,v)    (v.isNull() ? JSTYPE_NULL : JS_TypeOfValue(cx,v))
 
@@ -29,41 +29,40 @@ ScriptFilename(const JSScript* script)
     if (!script)
         return probes::nullName;
     if (!script->filename())
         return probes::anonymousName;
     return script->filename();
 }
 
 static const char*
-FunctionName(JSContext* cx, JSFunction* fun, UniqueChars* bytes)
+FunctionName(JSContext* cx, JSFunction* fun, JSAutoByteString* bytes)
 {
     if (!fun)
         return probes::nullName;
     if (!fun->displayAtom())
         return probes::anonymousName;
-    *bytes = JS_EncodeStringToLatin1(cx, fun->displayAtom());
-    return *bytes ? bytes->get() : probes::nullName;
+    return bytes->encodeLatin1(cx, fun->displayAtom()) ? bytes->ptr() : probes::nullName;
 }
 
 /*
  * These functions call the DTrace macros for the JavaScript USDT probes.
  * Originally this code was inlined in the JavaScript code; however since
  * a number of operations are called, these have been placed into functions
  * to reduce any negative compiler optimization effect that the addition of
  * a number of usually unused lines of code would cause.
  */
 void
 probes::DTraceEnterJSFun(JSContext* cx, JSFunction* fun, JSScript* script)
 {
-    UniqueChars funNameBytes;
+    JSAutoByteString funNameBytes;
     JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(script), probes::nullName,
                               FunctionName(cx, fun, &funNameBytes));
 }
 
 void
 probes::DTraceExitJSFun(JSContext* cx, JSFunction* fun, JSScript* script)
 {
-    UniqueChars funNameBytes;
+    JSAutoByteString funNameBytes;
     JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(script), probes::nullName,
                                FunctionName(cx, fun, &funNameBytes));
 }
 #endif
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -1815,24 +1815,25 @@ SavedStacks::MetadataBuilder::build(JSCo
     return frame;
 }
 
 const SavedStacks::MetadataBuilder SavedStacks::metadataBuilder;
 
 /* static */ ReconstructedSavedFramePrincipals ReconstructedSavedFramePrincipals::IsSystem;
 /* static */ ReconstructedSavedFramePrincipals ReconstructedSavedFramePrincipals::IsNotSystem;
 
-UniqueChars
+UTF8CharsZ
 BuildUTF8StackString(JSContext* cx, JSPrincipals* principals, HandleObject stack)
 {
     RootedString stackStr(cx);
     if (!JS::BuildStackString(cx, principals, stack, &stackStr))
-        return nullptr;
+        return UTF8CharsZ();
 
-    return JS_EncodeStringToUTF8(cx, stackStr);
+    char* chars = JS_EncodeStringToUTF8(cx, stackStr);
+    return UTF8CharsZ(chars, strlen(chars));
 }
 
 uint32_t
 FixupColumnForDisplay(uint32_t column)
 {
     // As described in WasmFrameIter::computeLine(), for wasm frames, the
     // function index is returned as the column with the high bit set. In paths
     // that format error stacks into strings, this information can be used to
--- a/js/src/vm/SavedStacks.h
+++ b/js/src/vm/SavedStacks.h
@@ -315,17 +315,17 @@ struct MutableWrappedPtrOperations<Saved
     void setColumn(uint32_t v) { loc().column = v; }
 
   private:
     SavedStacks::LocationValue& loc() {
         return static_cast<Wrapper*>(this)->get();
     }
 };
 
-JS::UniqueChars
+UTF8CharsZ
 BuildUTF8StackString(JSContext* cx, JSPrincipals* principals, HandleObject stack);
 
 uint32_t
 FixupColumnForDisplay(uint32_t column);
 
 } /* namespace js */
 
 #endif /* vm_SavedStacks_h */
--- a/js/src/vm/Scope.cpp
+++ b/js/src/vm/Scope.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/ScopeExit.h"
 
 #include <memory>
 #include <new>
 
 #include "builtin/ModuleObject.h"
 #include "gc/Allocator.h"
 #include "gc/FreeOp.h"
+#include "js/AutoByteString.h"
 #include "util/StringBuffer.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/JSScript.h"
 #include "wasm/WasmInstance.h"
 
 #include "gc/ObjectKind-inl.h"
 #include "vm/Shape-inl.h"
 
@@ -1514,20 +1515,20 @@ PositionalFormalParameterIter::Positiona
     settle();
 }
 
 void
 js::DumpBindings(JSContext* cx, Scope* scopeArg)
 {
     RootedScope scope(cx, scopeArg);
     for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) {
-        UniqueChars bytes = AtomToPrintableString(cx, bi.name());
-        if (!bytes)
+        JSAutoByteString bytes;
+        if (!AtomToPrintableString(cx, bi.name(), &bytes))
             return;
-        fprintf(stderr, "%s %s ", BindingKindString(bi.kind()), bytes.get());
+        fprintf(stderr, "%s %s ", BindingKindString(bi.kind()), bytes.ptr());
         switch (bi.location().kind()) {
           case BindingLocation::Kind::Global:
             if (bi.isTopLevelFunction())
                 fprintf(stderr, "global function\n");
             else
                 fprintf(stderr, "global\n");
             break;
           case BindingLocation::Kind::Argument:
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -33,16 +33,17 @@
 #include "builtin/String.h"
 #include "builtin/TypedObject.h"
 #include "builtin/WeakMapObject.h"
 #include "gc/HashUtil.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "jit/AtomicOperations.h"
 #include "jit/InlinableNatives.h"
+#include "js/AutoByteString.h"
 #include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/Date.h"
 #include "js/StableStringChars.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Compression.h"
@@ -287,33 +288,38 @@ ThrowErrorWithType(JSContext* cx, JSExnT
     uint32_t errorNumber = args[0].toInt32();
 
 #ifdef DEBUG
     const JSErrorFormatString* efs = GetErrorMessage(nullptr, errorNumber);
     MOZ_ASSERT(efs->argCount == args.length() - 1);
     MOZ_ASSERT(efs->exnType == type, "error-throwing intrinsic and error number are inconsistent");
 #endif
 
-    UniqueChars errorArgs[3];
+    JSAutoByteString errorArgs[3];
     for (unsigned i = 1; i < 4 && i < args.length(); i++) {
-        HandleValue val = args[i];
-        if (val.isInt32() || val.isString()) {
+        RootedValue val(cx, args[i]);
+        if (val.isInt32()) {
             JSString* str = ToString<CanGC>(cx, val);
             if (!str)
                 return;
-            errorArgs[i - 1] = StringToNewUTF8CharsZ(cx, *str);
+            errorArgs[i - 1].encodeLatin1(cx, str);
+        } else if (val.isString()) {
+            errorArgs[i - 1].encodeLatin1(cx, val.toString());
         } else {
-            errorArgs[i - 1] = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, nullptr);
+            UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, nullptr);
+            if (!bytes)
+                return;
+            errorArgs[i - 1].initBytes(std::move(bytes));
         }
         if (!errorArgs[i - 1])
             return;
     }
 
-    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber,
-                             errorArgs[0].get(), errorArgs[1].get(), errorArgs[2].get());
+    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, errorNumber,
+                               errorArgs[0].ptr(), errorArgs[1].ptr(), errorArgs[2].ptr());
 }
 
 static bool
 intrinsic_ThrowRangeError(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() >= 1);
 
@@ -518,20 +524,23 @@ intrinsic_FinishBoundFunctionInit(JSCont
  */
 static bool
 intrinsic_DecompileArg(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
 
     HandleValue value = args[1];
-    JSString* str = DecompileArgument(cx, args[0].toInt32(), value);
+    UniqueChars str = DecompileArgument(cx, args[0].toInt32(), value);
     if (!str)
         return false;
-    args.rval().setString(str);
+    JSString* result = NewStringCopyZ<CanGC>(cx, str.get());
+    if (!result)
+        return false;
+    args.rval().setString(result);
     return true;
 }
 
 static bool
 intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -1855,17 +1864,17 @@ js::ReportIncompatibleSelfHostedMethod(J
     // like array.sort(somethingSelfHosted), where we want to report the error
     // in the somethingSelfHosted, not in the sort() call.
     ScriptFrameIter iter(cx);
     MOZ_ASSERT(iter.isFunctionFrame());
 
     while (!iter.done()) {
         MOZ_ASSERT(iter.callee(cx)->isSelfHostedOrIntrinsic() &&
                    !iter.callee(cx)->isBoundFunction());
-        UniqueChars funNameBytes;
+        JSAutoByteString funNameBytes;
         const char* funName = GetFunctionNameBytes(cx, iter.callee(cx), &funNameBytes);
         if (!funName)
             return false;
         if (strcmp(funName, "IsTypedArrayEnsuringArrayBuffer") != 0) {
             JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_METHOD,
                                        funName, "method", InformalValueTypeName(args.thisv()));
             return false;
         }
@@ -2852,22 +2861,18 @@ VerifyGlobalNames(JSContext* cx, Handle<
                     nameMissing = true;
                     break;
                 }
             }
         }
     }
 
     if (nameMissing) {
-        UniqueChars bytes = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey);
-        if (!bytes)
-            return false;
-
-        JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NO_SUCH_SELF_HOSTED_PROP,
-                                 bytes.get());
+        RootedValue value(cx, IdToValue(id));
+        ReportValueError(cx, JSMSG_NO_SUCH_SELF_HOSTED_PROP, JSDVG_IGNORE_STACK, value, nullptr);
         return false;
     }
 #endif // DEBUG
 
     return true;
 }
 
 bool
--- a/js/src/vm/StringType.cpp
+++ b/js/src/vm/StringType.cpp
@@ -12,23 +12,20 @@
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/RangedPtr.h"
 #include "mozilla/TextUtils.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Unused.h"
 
-#include "jsfriendapi.h"
-
-#include "frontend/BytecodeCompiler.h"
 #include "gc/GCInternals.h"
 #include "gc/Marking.h"
 #include "gc/Nursery.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/StableStringChars.h"
 #include "js/UbiNode.h"
 #include "util/StringBuffer.h"
 #include "vm/GeckoProfiler.h"
 
 #include "vm/GeckoProfiler-inl.h"
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
@@ -2060,33 +2057,46 @@ js::EncodeLatin1(JSContext* cx, JSString
     if (!buf)
         return nullptr;
 
     mozilla::PodCopy(buf, linear->latin1Chars(nogc), len);
     buf[len] = '\0';
     return UniqueChars(reinterpret_cast<char*>(buf));
 }
 
-UniqueChars
-js::IdToPrintableUTF8(JSContext* cx, HandleId id, IdToPrintableBehavior behavior)
+const char*
+js::ValueToPrintableLatin1(JSContext* cx, const Value& vArg, JSAutoByteString* bytes,
+                           bool asSource)
 {
-    // ToString(<symbol>) throws a TypeError, therefore require that callers
-    // request source representation when |id| is a property key.
-    MOZ_ASSERT_IF(behavior == IdToPrintableBehavior::IdIsIdentifier,
-                  JSID_IS_ATOM(id) && frontend::IsIdentifier(JSID_TO_ATOM(id)));
-
-    RootedValue v(cx, IdToValue(id));
+    RootedValue v(cx, vArg);
     JSString* str;
-    if (behavior == IdToPrintableBehavior::IdIsPropertyKey)
+    if (asSource)
         str = ValueToSource(cx, v);
     else
         str = ToString<CanGC>(cx, v);
     if (!str)
         return nullptr;
-    return StringToNewUTF8CharsZ(cx, *str);
+    str = QuoteString(cx, str, 0);
+    if (!str)
+        return nullptr;
+    return bytes->encodeLatin1(cx, str);
+}
+
+const char*
+js::ValueToPrintableUTF8(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;
+    return bytes->encodeUtf8(cx, RootedString(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());
@@ -2157,18 +2167,18 @@ SymbolToSource(JSContext* cx, Symbol* sy
         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) {
-        UniqueChars quoted = QuoteString(cx, desc, '"');
-        if (!quoted || !buf.append(quoted.get(), strlen(quoted.get())))
+        desc = StringToSource(cx, desc);
+        if (!desc || !buf.append(desc))
             return nullptr;
     }
     if (!buf.append(')'))
         return nullptr;
     return buf.finishString();
 }
 
 JSString*
@@ -2207,13 +2217,10 @@ js::ValueToSource(JSContext* cx, HandleV
     }
 
     return ObjectToSource(cx, obj);
 }
 
 JSString*
 js::StringToSource(JSContext* cx, JSString* str)
 {
-    UniqueChars chars = QuoteString(cx, str, '"');
-    if (!chars)
-        return nullptr;
-    return NewStringCopyZ<CanGC>(cx, chars.get());
+    return QuoteString(cx, str, '"');
 }
--- a/js/src/vm/StringType.h
+++ b/js/src/vm/StringType.h
@@ -21,16 +21,17 @@
 #include "gc/Nursery.h"
 #include "gc/Rooting.h"
 #include "js/CharacterEncoding.h"
 #include "js/RootingAPI.h"
 #include "js/UniquePtr.h"
 #include "util/Text.h"
 #include "vm/Printer.h"
 
+class JSAutoByteString;
 class JSDependentString;
 class JSExtensibleString;
 class JSExternalString;
 class JSInlineString;
 class JSRope;
 
 namespace JS {
 
@@ -1672,33 +1673,33 @@ SubstringKernel(JSContext* cx, HandleStr
 /*** Conversions *********************************************************************************/
 
 /*
  * Convert a string to a printable C string.
  */
 UniqueChars
 EncodeLatin1(JSContext* cx, JSString* str);
 
-enum class IdToPrintableBehavior : bool {
-    /*
-     * Request the printable representation of an identifier.
-     */
-    IdIsIdentifier,
-
-    /*
-     * Request the printable representation of a property key.
-     */
-    IdIsPropertyKey
-};
+/*
+ * Convert a value to a printable C string.
+ *
+ * As the function name implies, any characters in a converted printable string will be Latin1
+ * characters. If there are any non-Latin1 characters in the original value, then those characters
+ * will be changed to Unicode escape sequences(I.e. \udddd, dddd are 4 hex digits) in the printable
+ * string.
+ */
+extern const char*
+ValueToPrintableLatin1(JSContext* cx, const Value&, JSAutoByteString* bytes,
+                       bool asSource = false);
 
 /*
- * Convert a jsid to a printable C string encoded in UTF-8.
+ * Convert a value to a printable C string encoded in UTF-8.
  */
-extern UniqueChars
-IdToPrintableUTF8(JSContext* cx, HandleId id, IdToPrintableBehavior behavior);
+extern const char*
+ValueToPrintableUTF8(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);
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -1319,18 +1319,18 @@ TypedArrayObjectTemplate<T>::fromObject(
     RootedObject arrayLike(cx);
     if (!callee.isNullOrUndefined()) {
         // Throw if other[Symbol.iterator] isn't callable.
         if (!callee.isObject() || !callee.toObject().isCallable()) {
             RootedValue otherVal(cx, ObjectValue(*other));
             UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, otherVal, nullptr);
             if (!bytes)
                 return nullptr;
-            JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE,
-                                     bytes.get());
+            JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE,
+                                       bytes.get());
             return nullptr;
         }
 
         FixedInvokeArgs<2> args2(cx);
         args2[0].setObject(*other);
         args2[1].set(callee);
 
         // Step 6.a.
--- a/js/src/vm/UbiNodeCensus.cpp
+++ b/js/src/vm/UbiNodeCensus.cpp
@@ -1,21 +1,20 @@
 /* -*- 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 "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/StableStringChars.h"
 #include "util/Text.h"
 #include "vm/JSContext.h"
-#include "vm/Printer.h"
 #include "vm/Realm.h"
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 namespace JS {
@@ -1257,22 +1256,26 @@ ParseBreakdown(JSContext* cx, HandleValu
         CountTypePtr noFilenameType(ParseChildBreakdown(cx, breakdown, cx->names().noFilename));
         if (!noFilenameType)
             return nullptr;
 
         return CountTypePtr(cx->new_<ByFilename>(std::move(thenType), std::move(noFilenameType)));
     }
 
     // We didn't recognize the breakdown type; complain.
-    UniqueChars byBytes = QuoteString(cx, by, '"');
+    RootedString bySource(cx, ValueToSource(cx, byValue));
+    if (!bySource)
+        return nullptr;
+
+    JSAutoByteString byBytes(cx, bySource);
     if (!byBytes)
         return nullptr;
 
-    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_CENSUS_BREAKDOWN,
-                              byBytes.get());
+    JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_CENSUS_BREAKDOWN,
+                               byBytes.ptr());
     return nullptr;
 }
 
 // Get the default census breakdown:
 //
 // { by: "coarseType",
 //   objects: { by: "objectClass" },
 //   other:   { by: "internalType" },
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -27,16 +27,17 @@
 #include <new>
 
 #include "jsmath.h"
 #include "jsutil.h"
 
 #include "builtin/String.h"
 #include "frontend/Parser.h"
 #include "gc/Policy.h"
+#include "js/AutoByteString.h"
 #include "js/MemoryMetrics.h"
 #include "js/Printf.h"
 #include "js/SourceBufferHolder.h"
 #include "js/StableStringChars.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
 #include "util/Text.h"
 #include "vm/ErrorReporting.h"
@@ -2009,18 +2010,19 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
         failfVAOffset(pn->pn_pos.begin, fmt, ap);
         va_end(ap);
         return false;
     }
 
     bool failNameOffset(uint32_t offset, const char* fmt, PropertyName* name) {
         // This function is invoked without the caller properly rooting its locals.
         gc::AutoSuppressGC suppress(cx_);
-        if (UniqueChars bytes = AtomToPrintableString(cx_, name))
-            failfOffset(offset, fmt, bytes.get());
+        JSAutoByteString bytes;
+        if (AtomToPrintableString(cx_, name, &bytes))
+            failfOffset(offset, fmt, bytes.ptr());
         return false;
     }
 
     bool failName(ParseNode* pn, const char* fmt, PropertyName* name) {
         return failNameOffset(pn->pn_pos.begin, fmt, name);
     }
 
     bool failOverRecursed() {
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -12,17 +12,17 @@
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 #ifdef XP_WIN
 #include <windows.h>
 #endif
 
 #include "jsapi.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/Printf.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsExceptionHandler.h"
 #include "nsIComponentManager.h"
 #include "mozilla/Module.h"
 #include "nsIFile.h"
@@ -101,32 +101,32 @@ Dump(JSContext* cx, unsigned argc, Value
 
     if (args.length() == 0)
         return true;
 
     RootedString str(cx, JS::ToString(cx, args[0]));
     if (!str)
         return false;
 
-    JS::UniqueChars utf8str = JS_EncodeStringToUTF8(cx, str);
-    if (!utf8str)
+    JSAutoByteString utf8str;
+    if (!utf8str.encodeUtf8(cx, str))
         return false;
 
 #ifdef ANDROID
-    __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.get());
+    __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.ptr());
 #endif
 #ifdef XP_WIN
     if (IsDebuggerPresent()) {
         nsAutoJSString wstr;
         if (!wstr.init(cx, str))
             return false;
         OutputDebugStringW(wstr.get());
     }
 #endif
-    fputs(utf8str.get(), stdout);
+    fputs(utf8str.ptr(), stdout);
     fflush(stdout);
     return true;
 }
 
 static bool
 Debug(JSContext* cx, unsigned argc, Value* vp)
 {
 #ifdef DEBUG
@@ -1236,43 +1236,43 @@ mozJSComponentLoader::ExtractExports(JSC
             !value.isString() ||
             !JS_ValueToId(cx, value, &symbolId)) {
             return ReportOnCallerUTF8(cxhelper, ERROR_ARRAY_ELEMENT, aInfo, i);
         }
 
         symbolHolder = ResolveModuleObjectPropertyById(cx, aMod->obj, symbolId);
         if (!symbolHolder ||
             !JS_GetPropertyById(cx, symbolHolder, symbolId, &value)) {
+            JSAutoByteString bytes;
             RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
-            JS::UniqueChars bytes = JS_EncodeStringToUTF8(cx, symbolStr);
-            if (!bytes)
+            if (!bytes.encodeUtf8(cx, symbolStr))
                 return NS_ERROR_FAILURE;
             return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_SYMBOL,
-                                      aInfo, bytes.get());
+                                      aInfo, bytes.ptr());
         }
 
         if (value.isUndefined()) {
             missing = true;
         }
 
         if (!JS_SetPropertyById(cx, aExports, symbolId, value)) {
+            JSAutoByteString bytes;
             RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
-            JS::UniqueChars bytes = JS_EncodeStringToUTF8(cx, symbolStr);
-            if (!bytes)
+            if (!bytes.encodeUtf8(cx, symbolStr))
                 return NS_ERROR_FAILURE;
             return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_SYMBOL,
-                                      aInfo, bytes.get());
+                                      aInfo, bytes.ptr());
         }
 #ifdef DEBUG
         if (i == 0) {
             logBuffer.AssignLiteral("Installing symbols [ ");
         }
-        JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, JSID_TO_STRING(symbolId));
+        JSAutoByteString bytes(cx, JSID_TO_STRING(symbolId));
         if (!!bytes)
-            logBuffer.Append(bytes.get());
+            logBuffer.Append(bytes.ptr());
         logBuffer.Append(' ');
         if (i == symbolCount - 1) {
             nsCString location;
             MOZ_TRY(aInfo.GetLocation(location));
             LOG(("%s] from %s\n", logBuffer.get(), location.get()));
         }
 #endif
     }
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * The Components.Sandbox object.
  */
 
 #include "AccessCheck.h"
 #include "jsfriendapi.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/Proxy.h"
 #include "js/SourceBufferHolder.h"
 #include "js/StructuredClone.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsIException.h" // for nsIStackFrame
 #include "nsIScriptContext.h"
@@ -135,18 +135,18 @@ SandboxDump(JSContext* cx, unsigned argc
 
     if (args.length() == 0)
         return true;
 
     RootedString str(cx, ToString(cx, args[0]));
     if (!str)
         return false;
 
-    JS::UniqueChars utf8str = JS_EncodeStringToUTF8(cx, str);
-    char* cstr = utf8str.get();
+    JSAutoByteString utf8str;
+    char* cstr = utf8str.encodeUtf8(cx, str);
     if (!cstr)
         return false;
 
 #if defined(XP_MACOSX)
     // Be nice and convert all \r to \n.
     char* c = cstr;
     char* cEnd = cstr + strlen(cstr);
     while (c < cEnd) {
@@ -849,84 +849,80 @@ xpc::GlobalProperties::Parse(JSContext* 
     for (uint32_t i = 0; i < length; i++) {
         RootedValue nameValue(cx);
         ok = JS_GetElement(cx, obj, i, &nameValue);
         NS_ENSURE_TRUE(ok, false);
         if (!nameValue.isString()) {
             JS_ReportErrorASCII(cx, "Property names must be strings");
             return false;
         }
-        JSFlatString* nameStr = JS_FlattenString(cx, nameValue.toString());
-        if (!nameStr)
+        RootedString nameStr(cx, nameValue.toString());
+        JSAutoByteString name;
+        if (!name.encodeUtf8(cx, nameStr))
             return false;
-        if (JS_FlatStringEqualsAscii(nameStr, "Blob")) {
+        if (!strcmp(name.ptr(), "Blob")) {
             Blob = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "ChromeUtils")) {
+        } else if (!strcmp(name.ptr(), "ChromeUtils")) {
             ChromeUtils = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "CSS")) {
+        } else if (!strcmp(name.ptr(), "CSS")) {
             CSS = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "CSSRule")) {
+        } else if (!strcmp(name.ptr(), "CSSRule")) {
             CSSRule = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "Directory")) {
+        } else if (!strcmp(name.ptr(), "Directory")) {
             Directory = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "DOMParser")) {
+        } else if (!strcmp(name.ptr(), "DOMParser")) {
             DOMParser = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "Element")) {
+        } else if (!strcmp(name.ptr(), "Element")) {
             Element = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "Event")) {
+        } else if (!strcmp(name.ptr(), "Event")) {
             Event = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "File")) {
+        } else if (!strcmp(name.ptr(), "File")) {
             File = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "FileReader")) {
+        } else if (!strcmp(name.ptr(), "FileReader")) {
             FileReader = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "FormData")) {
+        } else if (!strcmp(name.ptr(), "FormData")) {
             FormData = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "InspectorUtils")) {
+        } else if (!strcmp(name.ptr(), "InspectorUtils")) {
             InspectorUtils = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "MessageChannel")) {
+        } else if (!strcmp(name.ptr(), "MessageChannel")) {
             MessageChannel = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "Node")) {
+        } else if (!strcmp(name.ptr(), "Node")) {
             Node = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "NodeFilter")) {
+        } else if (!strcmp(name.ptr(), "NodeFilter")) {
             NodeFilter = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "TextDecoder")) {
+        } else if (!strcmp(name.ptr(), "TextDecoder")) {
             TextDecoder = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "TextEncoder")) {
+        } else if (!strcmp(name.ptr(), "TextEncoder")) {
             TextEncoder = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "URL")) {
+        } else if (!strcmp(name.ptr(), "URL")) {
             URL = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "URLSearchParams")) {
+        } else if (!strcmp(name.ptr(), "URLSearchParams")) {
             URLSearchParams = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "XMLHttpRequest")) {
+        } else if (!strcmp(name.ptr(), "XMLHttpRequest")) {
             XMLHttpRequest = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "XMLSerializer")) {
+        } else if (!strcmp(name.ptr(), "XMLSerializer")) {
             XMLSerializer = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "atob")) {
+        } else if (!strcmp(name.ptr(), "atob")) {
             atob = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "btoa")) {
+        } else if (!strcmp(name.ptr(), "btoa")) {
             btoa = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "caches")) {
+        } else if (!strcmp(name.ptr(), "caches")) {
             caches = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "crypto")) {
+        } else if (!strcmp(name.ptr(), "crypto")) {
             crypto = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "fetch")) {
+        } else if (!strcmp(name.ptr(), "fetch")) {
             fetch = true;
-        } else if (JS_FlatStringEqualsAscii(nameStr, "indexedDB")) {
+        } else if (!strcmp(name.ptr(), "indexedDB")) {
             indexedDB = true;
 #ifdef MOZ_WEBRTC
-        } else if (JS_FlatStringEqualsAscii(nameStr, "rtcIdentityProvider")) {
+        } else if (!strcmp(name.ptr(), "rtcIdentityProvider")) {
             rtcIdentityProvider = true;
 #endif
         } else {
-            RootedString nameStr(cx, nameValue.toString());
-            JS::UniqueChars name = JS_EncodeStringToUTF8(cx, nameStr);
-            if (!name)
-                return false;
-
-            JS_ReportErrorUTF8(cx, "Unknown property name: %s", name.get());
+            JS_ReportErrorUTF8(cx, "Unknown property name: %s", name.ptr());
             return false;
         }
     }
     return true;
 }
 
 bool
 xpc::GlobalProperties::Define(JSContext* cx, JS::HandleObject obj)
@@ -1539,19 +1535,20 @@ OptionsBase::ParseString(const char* nam
     if (!found)
         return true;
 
     if (!value.isString()) {
         JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name);
         return false;
     }
 
-    JS::UniqueChars tmp = JS_EncodeStringToLatin1(mCx, value.toString());
+    char* tmp = JS_EncodeString(mCx, value.toString());
     NS_ENSURE_TRUE(tmp, false);
-    prop.Assign(tmp.get(), strlen(tmp.get()));
+    prop.Assign(tmp, strlen(tmp));
+    js_free(tmp);
     return true;
 }
 
 /*
  * Helper that tries to get a string property from the options object.
  */
 bool
 OptionsBase::ParseString(const char* name, nsString& prop)
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -10,17 +10,17 @@
 #include "xpc_make_class.h"
 #include "XPCJSWeakReference.h"
 #include "WrapperFactory.h"
 #include "nsJSUtils.h"
 #include "mozJSComponentLoader.h"
 #include "nsContentUtils.h"
 #include "nsCycleCollector.h"
 #include "jsfriendapi.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/SavedFrameAPI.h"
 #include "js/StructuredClone.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/Preferences.h"
 #include "nsJSEnvironment.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/ResultExtensions.h"
@@ -241,22 +241,22 @@ nsXPCComponents_Interfaces::Resolve(nsIX
                                     bool* _retval)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, idArg);
 
     if (!JSID_IS_STRING(id))
         return NS_OK;
 
+    JSAutoByteString name;
     RootedString str(cx, JSID_TO_STRING(id));
-    JS::UniqueChars name = JS_EncodeStringToLatin1(cx, str);
 
     // we only allow interfaces by name here
-    if (name && name[0] != '{') {
-        const nsXPTInterfaceInfo* info = nsXPTInterfaceInfo::ByName(name.get());
+    if (name.encodeLatin1(cx, str) && name.ptr()[0] != '{') {
+        const nsXPTInterfaceInfo* info = nsXPTInterfaceInfo::ByName(name.ptr());
         if (!info)
             return NS_OK;
 
         nsCOMPtr<nsIJSIID> nsid = nsJSIID::NewID(info);
 
         if (nsid) {
             nsXPConnect* xpc = nsXPConnect::XPConnect();
             RootedObject idobj(cx);
@@ -426,20 +426,20 @@ nsXPCComponents_InterfacesByID::Resolve(
 
     if (!JSID_IS_STRING(id))
         return NS_OK;
 
     RootedString str(cx, JSID_TO_STRING(id));
     if (38 != JS_GetStringLength(str))
         return NS_OK;
 
-    JS::UniqueChars utf8str = JS_EncodeStringToUTF8(cx, str);
-    if (utf8str) {
+    JSAutoByteString utf8str;
+    if (utf8str.encodeUtf8(cx, str)) {
         nsID iid;
-        if (!iid.Parse(utf8str.get()))
+        if (!iid.Parse(utf8str.ptr()))
             return NS_OK;
 
         const nsXPTInterfaceInfo* info = nsXPTInterfaceInfo::ByIID(iid);
         if (!info)
             return NS_OK;
 
         nsCOMPtr<nsIJSIID> nsid = nsJSIID::NewID(info);
 
@@ -618,23 +618,21 @@ nsXPCComponents_Classes::Resolve(nsIXPCo
                                  JSContext* cx, JSObject* objArg,
                                  jsid idArg, bool* resolvedp,
                                  bool* _retval)
 
 {
     RootedId id(cx, idArg);
     RootedObject obj(cx, objArg);
 
-    if (!JSID_IS_STRING(id))
-        return NS_OK;
-
-    JS::UniqueChars name = JS_EncodeStringToLatin1(cx, JSID_TO_STRING(id));
-    if (name &&
-        name[0] != '{') { // we only allow contractids here
-        nsCOMPtr<nsIJSCID> nsid = nsJSCID::NewID(name.get());
+    JSAutoByteString name;
+    if (JSID_IS_STRING(id) &&
+        name.encodeLatin1(cx, JSID_TO_STRING(id)) &&
+        name.ptr()[0] != '{') { // we only allow contractids here
+        nsCOMPtr<nsIJSCID> nsid = nsJSCID::NewID(name.ptr());
         if (nsid) {
             nsXPConnect* xpc = nsXPConnect::XPConnect();
             RootedObject idobj(cx);
             if (NS_SUCCEEDED(xpc->WrapNative(cx, obj,
                                              static_cast<nsIJSCID*>(nsid),
                                              NS_GET_IID(nsIJSCID),
                                              idobj.address()))) {
                 if (idobj) {
@@ -829,22 +827,22 @@ nsXPCComponents_ClassesByID::Resolve(nsI
                                      bool* _retval)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, idArg);
 
     if (!JSID_IS_STRING(id))
         return NS_OK;
 
+    JSAutoByteString name;
     RootedString str(cx, JSID_TO_STRING(id));
-    JS::UniqueChars name = JS_EncodeStringToLatin1(cx, str);
-    if (name && name[0] == '{' &&
-        IsRegisteredCLSID(name.get())) // we only allow canonical CLSIDs here
+    if (name.encodeLatin1(cx, str) && name.ptr()[0] == '{' &&
+        IsRegisteredCLSID(name.ptr())) // we only allow canonical CLSIDs here
     {
-        nsCOMPtr<nsIJSCID> nsid = nsJSCID::NewID(name.get());
+        nsCOMPtr<nsIJSCID> nsid = nsJSCID::NewID(name.ptr());
         if (nsid) {
             nsXPConnect* xpc = nsXPConnect::XPConnect();
             RootedObject idobj(cx);
             if (NS_SUCCEEDED(xpc->WrapNative(cx, obj,
                                              static_cast<nsIJSCID*>(nsid),
                                              NS_GET_IID(nsIJSCID),
                                              idobj.address()))) {
                 if (idobj) {
@@ -995,26 +993,24 @@ nsXPCComponents_Results::NewEnumerate(ns
 NS_IMETHODIMP
 nsXPCComponents_Results::Resolve(nsIXPConnectWrappedNative* wrapper,
                                  JSContext* cx, JSObject* objArg,
                                  jsid idArg, bool* resolvedp,
                                  bool* _retval)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, idArg);
-    if (!JSID_IS_STRING(id))
-        return NS_OK;
-
-    JS::UniqueChars name = JS_EncodeStringToLatin1(cx, JSID_TO_STRING(id));
-    if (name) {
+    JSAutoByteString name;
+
+    if (JSID_IS_STRING(id) && name.encodeLatin1(cx, JSID_TO_STRING(id))) {
         const char* rv_name;
         const void* iter = nullptr;
         nsresult rv;
         while (nsXPCException::IterateNSResults(&rv, &rv_name, nullptr, &iter)) {
-            if (!strcmp(name.get(), rv_name)) {
+            if (!strcmp(name.ptr(), rv_name)) {
                 *resolvedp = true;
                 if (!JS_DefinePropertyById(cx, obj, id, (uint32_t)rv,
                                            JSPROP_ENUMERATE |
                                            JSPROP_READONLY |
                                            JSPROP_PERMANENT |
                                            JSPROP_RESOLVING)) {
                     return NS_ERROR_UNEXPECTED;
                 }
@@ -1160,27 +1156,25 @@ nsXPCComponents_ID::CallOrConstruct(nsIX
     if (NS_FAILED(nsXPConnect::SecurityManager()->CanCreateInstance(cx, nsJSID::GetCID()))) {
         // the security manager vetoed. It should have set an exception.
         *_retval = false;
         return NS_OK;
     }
 
     // convert the first argument into a string and see if it looks like an id
 
-    JSString* jsstr = ToString(cx, args[0]);
-    if (!jsstr)
+    JSString* jsstr;
+    JSAutoByteString bytes;
+    nsID id;
+
+    if (!(jsstr = ToString(cx, args[0])) ||
+        !bytes.encodeLatin1(cx, jsstr) ||
+        !id.Parse(bytes.ptr())) {
         return ThrowAndFail(NS_ERROR_XPC_BAD_ID_STRING, cx, _retval);
-
-    JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, jsstr);
-    if (!bytes)
-        return ThrowAndFail(NS_ERROR_XPC_BAD_ID_STRING, cx, _retval);
-
-    nsID id;
-    if (!id.Parse(bytes.get()))
-        return ThrowAndFail(NS_ERROR_XPC_BAD_ID_STRING, cx, _retval);
+    }
 
     // make the new object and return it.
 
     JSObject* newobj = xpc_NewIDObject(cx, obj, id);
     if (!newobj)
         return NS_ERROR_UNEXPECTED;
 
     args.rval().setObject(*newobj);
@@ -1383,18 +1377,17 @@ struct MOZ_STACK_CLASS ExceptionArgParse
     /*
      * Parsing helpers.
      */
 
     bool parseMessage(HandleValue v) {
         JSString* str = ToString(cx, v);
         if (!str)
            return false;
-        messageBytes = JS_EncodeStringToLatin1(cx, str);
-        eMsg = messageBytes.get();
+        eMsg = messageBytes.encodeLatin1(cx, str);
         return !!eMsg;
     }
 
     bool parseResult(HandleValue v) {
         return JS::ToUint32(cx, v, (uint32_t*) &eResult);
     }
 
     bool parseStack(HandleValue v) {
@@ -1455,17 +1448,17 @@ struct MOZ_STACK_CLASS ExceptionArgParse
         return JS_GetProperty(cx, obj, name, rv);
     }
 
     /*
      * Internal data members.
      */
 
     // If there's a non-default exception string, hold onto the allocated bytes.
-    JS::UniqueChars messageBytes;
+    JSAutoByteString messageBytes;
 
     // Various bits and pieces that are helpful to have around.
     JSContext* cx;
     nsXPConnect* xpc;
 };
 
 // static
 nsresult
@@ -1873,27 +1866,22 @@ nsXPCComponents_Constructor::CallOrConst
         *_retval = false;
         return NS_OK;
     }
 
     // initialization params for the Constructor object we will create
     nsCOMPtr<nsIJSCID> cClassID;
     nsCOMPtr<nsIJSIID> cInterfaceID;
     const char*        cInitializer = nullptr;
-    JS::UniqueChars cInitializerBytes;
+    JSAutoByteString  cInitializerBytes;
 
     if (args.length() >= 3) {
         // args[2] is an initializer function or property name
         RootedString str(cx, ToString(cx, args[2]));
-        if (!str)
-            return ThrowAndFail(NS_ERROR_XPC_BAD_CONVERT_JS, cx, _retval);
-
-        cInitializerBytes = JS_EncodeStringToLatin1(cx, str);
-        cInitializer = cInitializerBytes.get();
-        if (!cInitializer)
+        if (!str || !(cInitializer = cInitializerBytes.encodeLatin1(cx, str)))
             return ThrowAndFail(NS_ERROR_XPC_BAD_CONVERT_JS, cx, _retval);
     }
 
     if (args.length() >= 2) {
         // args[1] is an iid name string
         // XXXjband support passing "Components.interfaces.foo"?
 
         nsCOMPtr<nsIXPCComponents_Interfaces> ifaces;
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -16,16 +16,17 @@
 #include "nsQueryObject.h"
 #include "nsScriptError.h"
 #include "WrapperFactory.h"
 
 #include "nsWrapperCacheInlines.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "js/AutoByteString.h"
 #include "js/CharacterEncoding.h"
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/PrimitiveConversions.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 
@@ -1327,41 +1328,41 @@ XPCConvert::JSValToXPCException(MutableH
                                       exceptn, nullptr, nullptr);
         } else {
             // It is a JSObject, but not a wrapped native...
 
             // If it is an engine Error with an error report then let's
             // extract the report and build an xpcexception from that
             const JSErrorReport* report;
             if (nullptr != (report = JS_ErrorFromException(cx, obj))) {
-                JS::UniqueChars toStringResult;
+                JSAutoByteString toStringResult;
                 RootedString str(cx, ToString(cx, s));
                 if (str)
-                    toStringResult = JS_EncodeStringToUTF8(cx, str);
-                return JSErrorToXPCException(toStringResult.get(), ifaceName,
+                    toStringResult.encodeUtf8(cx, str);
+                return JSErrorToXPCException(toStringResult.ptr(), ifaceName,
                                              methodName, report, exceptn);
             }
 
             // XXX we should do a check against 'js_ErrorClass' here and
             // do the right thing - even though it has no JSErrorReport,
             // The fact that it is a JSError exceptions means we can extract
             // particular info and our 'result' should reflect that.
 
             // otherwise we'll just try to convert it to a string
 
             JSString* str = ToString(cx, s);
             if (!str)
                 return NS_ERROR_FAILURE;
 
-            JS::UniqueChars strBytes = JS_EncodeStringToLatin1(cx, str);
+            JSAutoByteString strBytes(cx, str);
             if (!strBytes)
                 return NS_ERROR_FAILURE;
 
             return ConstructException(NS_ERROR_XPC_JS_THREW_JS_OBJECT,
-                                      strBytes.get(), ifaceName, methodName,
+                                      strBytes.ptr(), ifaceName, methodName,
                                       nullptr, exceptn, cx, s.address());
         }
     }
 
     if (s.isUndefined() || s.isNull()) {
         return ConstructException(NS_ERROR_XPC_JS_THREW_NULL,
                                   nullptr, ifaceName, methodName, nullptr,
                                   exceptn, cx, s.address());
@@ -1414,19 +1415,20 @@ XPCConvert::JSValToXPCException(MutableH
         }
     }
 
     // otherwise we'll just try to convert it to a string
     // Note: e.g., bools get converted to JSStrings by this code.
 
     JSString* str = ToString(cx, s);
     if (str) {
-        if (JS::UniqueChars strBytes = JS_EncodeStringToLatin1(cx, str)) {
+        JSAutoByteString strBytes(cx, str);
+        if (!!strBytes) {
             return ConstructException(NS_ERROR_XPC_JS_THREW_STRING,
-                                      strBytes.get(), ifaceName, methodName,
+                                      strBytes.ptr(), ifaceName, methodName,
                                       nullptr, exceptn, cx, s.address());
         }
     }
     return NS_ERROR_FAILURE;
 }
 
 /***************************************************************************/
 
--- a/js/xpconnect/src/XPCDebug.cpp
+++ b/js/xpconnect/src/XPCDebug.cpp
@@ -48,15 +48,15 @@ xpc_DumpJSStack(bool showArgs, bool show
 }
 
 JS::UniqueChars
 xpc_PrintJSStack(JSContext* cx, bool showArgs, bool showLocals,
                  bool showThisProps)
 {
     JS::AutoSaveExceptionState state(cx);
 
-    JS::UniqueChars buf = JS::FormatStackDump(cx, showArgs, showLocals, showThisProps);
+    JS::UniqueChars buf = JS::FormatStackDump(cx, nullptr, showArgs, showLocals, showThisProps);
     if (!buf)
         DebugDump("%s", "Failed to format JavaScript stack for dump\n");
 
     state.restore();
     return buf;
 }
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -2,17 +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 "nsXULAppAPI.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/Printf.h"
 #include "mozilla/ChaosMode.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/Preferences.h"
 #include "nsServiceManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsExceptionHandler.h"
@@ -245,18 +245,18 @@ ReadLine(JSContext* cx, unsigned argc, V
         str = JS::ToString(cx, args[0]);
         if (!str)
             return false;
     } else {
         str = JS_GetEmptyString(cx);
     }
 
     /* Get a line from the infile */
-    JS::UniqueChars strBytes = JS_EncodeStringToLatin1(cx, str);
-    if (!strBytes || !GetLine(cx, buf, gInFile, strBytes.get()))
+    JSAutoByteString strBytes(cx, str);
+    if (!strBytes || !GetLine(cx, buf, gInFile, strBytes.ptr()))
         return false;
 
     /* Strip newline character added by GetLine() */
     unsigned int buflen = strlen(buf);
     if (buflen == 0) {
         if (feof(gInFile)) {
             args.rval().setNull();
             return true;
@@ -283,23 +283,23 @@ Print(JSContext* cx, unsigned argc, Valu
     RootedString str(cx);
     nsAutoCString utf8output;
 
     for (unsigned i = 0; i < args.length(); i++) {
         str = ToString(cx, args[i]);
         if (!str)
             return false;
 
-        JS::UniqueChars utf8str = JS_EncodeStringToUTF8(cx, str);
-        if (!utf8str)
+        JSAutoByteString utf8str;
+        if (!utf8str.encodeUtf8(cx, str))
             return false;
 
         if (i)
             utf8output.Append(' ');
-        utf8output.Append(utf8str.get(), strlen(utf8str.get()));
+        utf8output.Append(utf8str.ptr(), utf8str.length());
     }
     utf8output.Append('\n');
     fputs(utf8output.get(), gOutFile);
     fflush(gOutFile);
     return true;
 }
 
 static bool
@@ -310,32 +310,32 @@ Dump(JSContext* cx, unsigned argc, Value
 
     if (!args.length())
          return true;
 
     RootedString str(cx, ToString(cx, args[0]));
     if (!str)
         return false;
 
-    JS::UniqueChars utf8str = JS_EncodeStringToUTF8(cx, str);
-    if (!utf8str)
+    JSAutoByteString utf8str;
+    if (!utf8str.encodeUtf8(cx, str))
         return false;
 
 #ifdef ANDROID
-    __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.get());
+    __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.ptr());
 #endif
 #ifdef XP_WIN
     if (IsDebuggerPresent()) {
         nsAutoJSString wstr;
         if (!wstr.init(cx, str))
             return false;
         OutputDebugStringW(wstr.get());
     }
 #endif
-    fputs(utf8str.get(), gOutFile);
+    fputs(utf8str.ptr(), gOutFile);
     fflush(gOutFile);
     return true;
 }
 
 static bool
 Load(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -348,31 +348,31 @@ Load(JSContext* cx, unsigned argc, Value
         return false;
     }
 
     RootedString str(cx);
     for (unsigned i = 0; i < args.length(); i++) {
         str = ToString(cx, args[i]);
         if (!str)
             return false;
-        JS::UniqueChars filename = JS_EncodeStringToLatin1(cx, str);
+        JSAutoByteString filename(cx, str);
         if (!filename)
             return false;
-        FILE* file = fopen(filename.get(), "r");
+        FILE* file = fopen(filename.ptr(), "r");
         if (!file) {
-            filename = JS_EncodeStringToUTF8(cx, str);
-            if (!filename)
+            filename.clear();
+            if (!filename.encodeUtf8(cx, str))
                 return false;
             JS_ReportErrorUTF8(cx, "cannot open file '%s' for reading",
-                               filename.get());
+                               filename.ptr());
             return false;
         }
         JS::CompileOptions options(cx);
         options.setUTF8(true)
-               .setFileAndLine(filename.get(), 1)
+               .setFileAndLine(filename.ptr(), 1)
                .setIsRunOnce(true);
         JS::Rooted<JSScript*> script(cx);
         JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
         JS::Compile(cx, options, file, &script);
         fclose(file);
         if (!script)
             return false;
 
@@ -474,35 +474,35 @@ SendCommand(JSContext* cx, unsigned argc
 
 static bool
 Options(JSContext* cx, unsigned argc, Value* vp)
 {
     JS::CallArgs args = CallArgsFromVp(argc, vp);
     ContextOptions oldContextOptions = ContextOptionsRef(cx);
 
     RootedString str(cx);
-    JS::UniqueChars opt;
+    JSAutoByteString opt;
     for (unsigned i = 0; i < args.length(); ++i) {
         str = ToString(cx, args[i]);
         if (!str)
             return false;
 
-        opt = JS_EncodeStringToUTF8(cx, str);
-        if (!opt)
+        opt.clear();
+        if (!opt.encodeUtf8(cx, str))
             return false;
 
-        if (strcmp(opt.get(), "strict") == 0)
+        if (strcmp(opt.ptr(), "strict") == 0)
             ContextOptionsRef(cx).toggleExtraWarnings();
-        else if (strcmp(opt.get(), "werror") == 0)
+        else if (strcmp(opt.ptr(), "werror") == 0)
             ContextOptionsRef(cx).toggleWerror();
-        else if (strcmp(opt.get(), "strict_mode") == 0)
+        else if (strcmp(opt.ptr(), "strict_mode") == 0)
             ContextOptionsRef(cx).toggleStrictMode();
         else {
             JS_ReportErrorUTF8(cx, "unknown option name '%s'. The valid names are "
-                               "strict, werror, and strict_mode.", opt.get());
+                               "strict, werror, and strict_mode.", opt.ptr());
             return false;
         }
     }
 
     UniqueChars names;
     if (oldContextOptions.extraWarnings()) {
         names = JS_sprintf_append(std::move(names), "%s", "strict");
         if (!names) {
@@ -721,21 +721,21 @@ ProcessLine(AutoJSAPI& jsapi, const char
     if (!JS_ExecuteScript(cx, script, &result))
         return false;
 
     if (result.isUndefined())
         return true;
     RootedString str(cx);
     if (!(str = ToString(cx, result)))
         return false;
-    JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, str);
-    if (!bytes)
+    JSAutoByteString bytes;
+    if (!bytes.encodeLatin1(cx, str))
         return false;
 
-    fprintf(gOutFile, "%s\n", bytes.get());
+    fprintf(gOutFile, "%s\n", bytes.ptr());
     return true;
 }
 
 static bool
 ProcessFile(AutoJSAPI& jsapi, const char* filename, FILE* file, bool forceTTY)
 {
     JSContext* cx = jsapi.cx();
     JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
--- a/js/xpconnect/src/XPCThrower.cpp
+++ b/js/xpconnect/src/XPCThrower.cpp
@@ -3,17 +3,17 @@
 /* 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/. */
 
 /* Code for throwing errors into JavaScript. */
 
 #include "xpcprivate.h"
 #include "XPCWrapper.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/Printf.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/Exceptions.h"
 #include "nsString.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -159,23 +159,20 @@ void
 XPCThrower::Verbosify(XPCCallContext& ccx,
                       char** psz, bool own)
 {
     char* sz = nullptr;
 
     if (ccx.HasInterfaceAndMember()) {
         XPCNativeInterface* iface = ccx.GetInterface();
         jsid id = ccx.GetMember()->GetName();
-        const char* name;
-        JS::UniqueChars bytes;
-        if (!JSID_IS_VOID(id)) {
-            bytes = JS_EncodeStringToLatin1(ccx, JSID_TO_STRING(id));
-            name = bytes ? bytes.get() : "";
-        } else {
-            name = "Unknown";
+        JSAutoByteString bytes;
+        const char* name = JSID_IS_VOID(id) ? "Unknown" : bytes.encodeLatin1(ccx, JSID_TO_STRING(id));
+        if (!name) {
+            name = "";
         }
         sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name).release();
     }
 
     if (sz) {
         if (own)
             js_free(*psz);
         *psz = sz;
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* JavaScript JSClasses and JSOps for our Wrapped Native JS Objects. */
 
 #include "xpcprivate.h"
 #include "xpc_make_class.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/Preferences.h"
-#include "js/CharacterEncoding.h"
+#include "js/AutoByteString.h"
 #include "js/Class.h"
 #include "js/Printf.h"
 
 using namespace mozilla;
 using namespace JS;
 
 /***************************************************************************/
 
@@ -284,47 +284,30 @@ DefinePropertyIfFound(XPCCallContext& cc
             }
         }
         // This *might* be a tearoff name that is not yet part of our
         // set. Let's lookup the name and see if it is the name of an
         // interface. Then we'll see if the object actually *does* this
         // interface and add a tearoff as necessary.
 
         if (wrapperToReflectInterfaceNames) {
-            JS::UniqueChars name;
+            JSAutoByteString name;
             RefPtr<XPCNativeInterface> iface2;
             XPCWrappedNativeTearOff* to;
             RootedObject jso(ccx);
             nsresult rv = NS_OK;
 
-            bool defineProperty = false;
-            do {
-                if (!JSID_IS_STRING(id))
-                    break;
-
-                name = JS_EncodeStringToLatin1(ccx, JSID_TO_STRING(id));
-                if (!name)
-                    break;
-
-                iface2 = XPCNativeInterface::GetNewOrUsed(name.get());
-                if (!iface2)
-                    break;
+            if (JSID_IS_STRING(id) &&
+                name.encodeLatin1(ccx, JSID_TO_STRING(id)) &&
+                (iface2 = XPCNativeInterface::GetNewOrUsed(name.ptr())) &&
+                nullptr != (to = wrapperToReflectInterfaceNames->
+                           FindTearOff(iface2, true, &rv)) &&
+                nullptr != (jso = to->GetJSObject()))
 
-                to = wrapperToReflectInterfaceNames->FindTearOff(iface2, true, &rv);
-                if (!to)
-                    break;
-
-                jso = to->GetJSObject();
-                if (!jso)
-                    break;
-
-                defineProperty = true;
-            } while (false);
-
-            if (defineProperty) {
+            {
                 AutoResolveName arn(ccx, id);
                 if (resolved)
                     *resolved = true;
                 return JS_DefinePropertyById(ccx, obj, id, jso,
                                              propFlags & ~JSPROP_ENUMERATE);
             } else if (NS_FAILED(rv) && rv != NS_ERROR_NO_INTERFACE) {
                 return Throw(rv, ccx);
             }
--- a/toolkit/recordreplay/ipc/JSControl.cpp
+++ b/toolkit/recordreplay/ipc/JSControl.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 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 "JSControl.h"
 
-#include "js/CharacterEncoding.h"
 #include "js/Conversions.h"
 #include "js/JSON.h"
 #include "ChildInternal.h"
 #include "ParentInternal.h"
 #include "xpcprivate.h"
 
 using namespace JS;
 
@@ -798,21 +797,22 @@ RecordReplay_Dump(JSContext* aCx, unsign
   // This method is an alternative to dump() that can be used in places where
   // thread events are disallowed.
   CallArgs args = CallArgsFromVp(aArgc, aVp);
   for (size_t i = 0; i < args.length(); i++) {
     RootedString str(aCx, ToString(aCx, args[i]));
     if (!str) {
       return false;
     }
-    JS::UniqueChars cstr = JS_EncodeStringToLatin1(aCx, str);
+    char* cstr = JS_EncodeString(aCx, str);
     if (!cstr) {
       return false;
     }
-    Print("%s", cstr.get());
+    Print("%s", cstr);
+    JS_free(aCx, cstr);
   }
 
   args.rval().setUndefined();
   return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Plumbing
--- a/tools/fuzzing/messagemanager/MessageManagerFuzzer.cpp
+++ b/tools/fuzzing/messagemanager/MessageManagerFuzzer.cpp
@@ -4,17 +4,16 @@
  * 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 <climits>
 #include <cmath>
 #include "FuzzingTraits.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
-#include "js/CharacterEncoding.h"
 #include "prenv.h"
 #include "MessageManagerFuzzer.h"
 #include "mozilla/ErrorResult.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsFrameMessageManager.h"
 #include "nsJSUtils.h"
 #include "nsXULAppAPI.h"
@@ -191,20 +190,19 @@ MessageManagerFuzzer::MutateValue(JSCont
 
   if (aValue.isString()) {
     nsCString x = GetFuzzValueFromFile();
     if (x.IsEmpty()) {
       return false;
     }
     JSString* str = JS_NewStringCopyZ(aCx, x.get());
     aOutMutationValue.set(JS::StringValue(str));
-    JS::UniqueChars valueChars = JS_EncodeStringToUTF8(aCx, aValue.toString());
     MSGMGR_FUZZER_LOG("%*s! Mutated value of type |string|: '%s' to '%s'",
                       aRecursionCounter * 4, "",
-                      valueChars.get(), x.get());
+                      JS_EncodeString(aCx, aValue.toString()), x.get());
     return true;
   }
 
   if (aValue.isObject()) {
     aRecursionCounter++;
     MSGMGR_FUZZER_LOG("%*s<Enumerating found object>",
                       aRecursionCounter * 4, "");
     MutateObject(aCx, aValue, aRecursionCounter);
@@ -255,20 +253,19 @@ MessageManagerFuzzer::Mutate(JSContext* 
   }
 
   // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1346040
   aData->Copy(mutatedStructuredCloneData);
 
   /* Mutated and successfully written to StructuredCloneData object. */
   if (isMutated) {
     JS::RootedString str(aCx, JS_ValueToSource(aCx, scdMutationContent));
-    JS::UniqueChars strChars = JS_EncodeStringToUTF8(aCx, str);
     MSGMGR_FUZZER_LOG("Mutated '%s' Message: %s",
                       NS_ConvertUTF16toUTF8(aMessageName).get(),
-                      strChars.get());
+                      JS_EncodeStringToUTF8(aCx, str));
   }
 
   return true;
 }
 
 /* static */
 unsigned int
 MessageManagerFuzzer::DefaultMutationProbability()
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -1637,17 +1637,17 @@ CycleCollectedJSRuntime::ErrorIntercepto
   details.mType = *type;
   // If `exn` isn't an exception object, `ExtractErrorValues` could end up calling
   // `toString()`, which could in turn end up throwing an error. While this should
   // work, we want to avoid that complex use case.
   // Fortunately, we have already checked above that `exn` is an exception object,
   // so nothing such should happen.
   nsContentUtils::ExtractErrorValues(cx, value, details.mFilename, &details.mLine, &details.mColumn, details.mMessage);
 
-  JS::UniqueChars buf = JS::FormatStackDump(cx, /* showArgs = */ false, /* showLocals = */ false, /* showThisProps = */ false);
+  JS::UniqueChars buf = JS::FormatStackDump(cx, nullptr, /* showArgs = */ false, /* showLocals = */ false, /* showThisProps = */ false);
   CopyUTF8toUTF16(mozilla::MakeStringSpan(buf.get()), details.mStack);
 
   mThrownError.emplace(std::move(details));
 }
 
 void
 CycleCollectedJSRuntime::ClearRecentDevError()
 {