Bug 1283710 - Part 5: Rename message to toStringResult if it is the result of toString. r=bholley,jwalden,froydnj
☠☠ backed out by 971d67779565 ☠ ☠
authorTooru Fujisawa <arai_a@mac.com>
Sun, 14 Aug 2016 20:39:31 +0900
changeset 318407 d72527b7d3c0c328e65bdaf4c259921a1e8481c2
parent 318406 ee5215f1a38e702d7335da05316ead5978e64986
child 318408 51b8d69edca019360cfc445812d51fbbba56450e
push id82921
push userarai_a@mac.com
push dateTue, 18 Oct 2016 17:13:02 +0000
treeherdermozilla-inbound@f727edc4be48 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley, jwalden, froydnj
bugs1283710
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1283710 - Part 5: Rename message to toStringResult if it is the result of toString. r=bholley,jwalden,froydnj
dom/base/ScriptSettings.cpp
dom/promise/Promise.cpp
dom/workers/ScriptLoader.cpp
dom/workers/ServiceWorkerEvents.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
js/src/jsapi-tests/testParseJSON.cpp
js/src/jsapi-tests/testUncaughtSymbol.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsexn.cpp
js/src/jsfriendapi.h
js/src/shell/js.cpp
js/src/vm/SelfHosting.cpp
js/xpconnect/src/XPCConvert.cpp
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/src/xpcpublic.h
xpcom/string/nsUTF8Utils.h
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -528,17 +528,17 @@ WarningOnlyErrorReporter(JSContext* aCx,
     // just go through the worker ReportError codepath here.
     //
     // That said, it feels like we should be able to short-circuit things a bit
     // here by posting an appropriate runnable to the main thread directly...
     // Worth looking into sometime.
     workers::WorkerPrivate* worker = workers::GetWorkerPrivateFromContext(aCx);
     MOZ_ASSERT(worker);
 
-    worker->ReportError(aCx, nullptr, aRep);
+    worker->ReportError(aCx, JS::ConstUTF8CharsZ(), aRep);
     return;
   }
 
   RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
   nsGlobalWindow* win = xpc::CurrentWindowOrNull(aCx);
   if (!win) {
     // We run addons in a separate privileged compartment, but if we're in an
     // addon compartment we should log warnings to the console of the associated
@@ -580,17 +580,17 @@ AutoJSAPI::ReportException()
 
       RefPtr<nsGlobalWindow> win = xpc::WindowGlobalOrNull(errorGlobal);
       if (!win) {
         // We run addons in a separate privileged compartment, but they still
         // expect to trigger the onerror handler of their associated DOM Window.
         win = xpc::AddonWindowOrNull(errorGlobal);
       }
       nsPIDOMWindowInner* inner = win ? win->AsInner() : nullptr;
-      xpcReport->Init(jsReport.report(), jsReport.message().c_str(),
+      xpcReport->Init(jsReport.report(), jsReport.toStringResult().c_str(),
                       nsContentUtils::IsCallerChrome(),
                       inner ? inner->WindowID() : 0);
       if (inner && jsReport.report()->errorNumber != JSMSG_OUT_OF_MEMORY) {
         JS::RootingContext* rcx = JS::RootingContext::get(cx());
         DispatchScriptErrorEvent(inner, rcx, xpcReport, exn);
       } else {
         JS::Rooted<JSObject*> stack(cx(),
           xpc::FindExceptionStackForConsoleReport(inner, exn));
@@ -604,17 +604,17 @@ AutoJSAPI::ReportException()
       workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate();
       MOZ_ASSERT(worker);
       MOZ_ASSERT(worker->GetJSContext() == cx());
       // Before invoking ReportError, put the exception back on the context,
       // because it may want to put it in its error events and has no other way
       // to get hold of it.  After we invoke ReportError, clear the exception on
       // cx(), just in case ReportError didn't.
       JS_SetPendingException(cx(), exn);
-      worker->ReportError(cx(), jsReport.message().c_str(), jsReport.report());
+      worker->ReportError(cx(), jsReport.toStringResult(), jsReport.report());
       ClearException();
     }
   } else {
     NS_WARNING("OOMed while acquiring uncaught exception from JSAPI");
     ClearException();
   }
 }
 
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -1017,17 +1017,17 @@ Promise::ReportRejectedPromise(JSContext
     return;
   }
 
   RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
   bool isMainThread = MOZ_LIKELY(NS_IsMainThread());
   bool isChrome = isMainThread ? nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(aPromise))
                                : GetCurrentThreadWorkerPrivate()->IsChromeWorker();
   nsGlobalWindow* win = isMainThread ? xpc::WindowGlobalOrNull(aPromise) : nullptr;
-  xpcReport->Init(report.report(), report.message().c_str(), isChrome,
+  xpcReport->Init(report.report(), report.toStringResult().c_str(), isChrome,
                   win ? win->AsInner()->WindowID() : 0);
 
   // Now post an event to do the real reporting async
   NS_DispatchToMainThread(new AsyncErrorReporter(xpcReport));
 }
 #endif // defined(SPIDERMONKEY_PROMISE)
 
 bool
@@ -2640,17 +2640,18 @@ Promise::MaybeReportRejected()
   bool isMainThread = MOZ_LIKELY(NS_IsMainThread());
   bool isChrome = isMainThread ? nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(obj))
                                : GetCurrentThreadWorkerPrivate()->IsChromeWorker();
   nsGlobalWindow* win = isMainThread ? xpc::WindowGlobalOrNull(obj) : nullptr;
   uint64_t windowID = win ? win->AsInner()->WindowID() : 0;
   if (exp) {
     xpcReport->Init(cx, exp, isChrome, windowID);
   } else {
-    xpcReport->Init(report.report(), report.message(), isChrome, windowID);
+    xpcReport->Init(report.report(), report.toStringResult(),
+                    isChrome, windowID);
   }
 
   // Now post an event to do the real reporting async
   // Since Promises preserve their wrapper, it is essential to RefPtr<> the
   // AsyncErrorReporter, otherwise if the call to DispatchToMainThread fails, it
   // will leak. See Bug 958684.  So... don't use DispatchToMainThread()
   nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
   if (NS_WARN_IF(!mainThread)) {
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -2089,17 +2089,17 @@ ScriptExecutorRunnable::LogExceptionToCo
 
   js::ErrorReport report(aCx);
   if (!report.init(aCx, exn, js::ErrorReport::WithSideEffects)) {
     JS_ClearPendingException(aCx);
     return;
   }
 
   RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
-  xpcReport->Init(report.report(), report.message().c_str(),
+  xpcReport->Init(report.report(), report.toStringResult().c_str(),
                   aWorkerPrivate->IsChromeWorker(), aWorkerPrivate->WindowID());
 
   RefPtr<AsyncErrorReporter> r = new AsyncErrorReporter(xpcReport);
   NS_DispatchToMainThread(r);
 }
 
 void
 LoadAllScripts(WorkerPrivate* aWorkerPrivate,
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -421,17 +421,17 @@ ExtractErrorValues(JSContext* aCx, JS::H
     // from the Error as they will be more specific to the root cause of
     // the problem.
     JSErrorReport* err = obj ? JS_ErrorFromException(aCx, obj) : nullptr;
     if (err) {
       // Use xpc to extract the error message only.  We don't actually send
       // this report anywhere.
       RefPtr<xpc::ErrorReport> report = new xpc::ErrorReport();
       report->Init(err,
-                   "<unknown>", // fallback message
+                   "<unknown>", // toString result
                    false,       // chrome
                    0);          // window ID
 
       if (!report->mFileName.IsEmpty()) {
         CopyUTF16toUTF8(report->mFileName, aSourceSpecOut);
         *aLineOut = report->mLineNumber;
         *aColumnOut = report->mColumn;
       }
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -81,16 +81,17 @@
 #include "nsHostObjectProtocolHandler.h"
 #include "nsJSEnvironment.h"
 #include "nsJSUtils.h"
 #include "nsNetUtil.h"
 #include "nsPrintfCString.h"
 #include "nsProxyRelease.h"
 #include "nsQueryObject.h"
 #include "nsSandboxFlags.h"
+#include "nsUTF8Utils.h"
 #include "prthread.h"
 #include "xpcpublic.h"
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 
 #ifdef DEBUG
@@ -5806,17 +5807,17 @@ WorkerPrivate::NotifyInternal(JSContext*
              aStatus == Canceling ||
              aStatus == Killing);
 
   // Always abort the script.
   return false;
 }
 
 void
-WorkerPrivate::ReportError(JSContext* aCx, const char* aFallbackMessage,
+WorkerPrivate::ReportError(JSContext* aCx, JS::ConstUTF8CharsZ aToStringResult,
                            JSErrorReport* aReport)
 {
   AssertIsOnWorkerThread();
 
   if (!MayContinueRunning() || mErrorHandlerRecursionCount == 2) {
     return;
   }
 
@@ -5850,23 +5851,29 @@ WorkerPrivate::ReportError(JSContext* aC
     MOZ_ASSERT(aReport->exnType >= JSEXN_FIRST && aReport->exnType < JSEXN_LIMIT);
     exnType = JSExnType(aReport->exnType);
   }
   else {
     lineNumber = columnNumber = errorNumber = 0;
     flags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
   }
 
-  if (message.IsEmpty() && aFallbackMessage) {
-    nsDependentCString fallbackMessage(aFallbackMessage);
-    if (!AppendUTF8toUTF16(fallbackMessage, message, mozilla::fallible)) {
+  if (message.IsEmpty() && aToStringResult) {
+    nsDependentCString toStringResult(aToStringResult.c_str());
+    if (!AppendUTF8toUTF16(toStringResult, message, mozilla::fallible)) {
       // Try again, with only a 1 KB string. Do this infallibly this time.
       // If the user doesn't have 1 KB to spare we're done anyways.
-      nsDependentCString truncatedFallbackMessage(aFallbackMessage, 1024);
-      AppendUTF8toUTF16(truncatedFallbackMessage, message);
+      uint32_t index = std::min(uint32_t(1024), toStringResult.Length());
+
+      // Drop the last code point that may be cropped.
+      index = RewindToPriorUTF8Codepoint(toStringResult.BeginReading(), index);
+
+      nsDependentCString truncatedToStringResult(aToStringResult.c_str(),
+                                                 index);
+      AppendUTF8toUTF16(truncatedToStringResult, message);
     }
   }
 
   mErrorHandlerRecursionCount++;
 
   // Don't want to run the scope's error handler if this is a recursive error or
   // if we ran out of memory.
   bool fireAtScope = mErrorHandlerRecursionCount == 1 &&
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_workers_workerprivate_h__
 #define mozilla_dom_workers_workerprivate_h__
 
 #include "Workers.h"
 
+#include "js/CharacterEncoding.h"
 #include "nsIContentPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsILoadGroup.h"
 #include "nsIWorkerDebugger.h"
 #include "nsPIDOMWindow.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
@@ -1159,17 +1160,18 @@ public:
   void
   ReportErrorToDebugger(const nsAString& aFilename, uint32_t aLineno,
                         const nsAString& aMessage);
 
   bool
   NotifyInternal(JSContext* aCx, Status aStatus);
 
   void
-  ReportError(JSContext* aCx, const char* aMessage, JSErrorReport* aReport);
+  ReportError(JSContext* aCx, JS::ConstUTF8CharsZ aToStringResult,
+              JSErrorReport* aReport);
 
   static void
   ReportErrorToConsole(const char* aMessage);
 
   int32_t
   SetTimeout(JSContext* aCx, nsIScriptTimeoutHandler* aHandler,
              int32_t aTimeout, bool aIsInterval,
              ErrorResult& aRv);
--- a/js/src/jsapi-tests/testParseJSON.cpp
+++ b/js/src/jsapi-tests/testParseJSON.cpp
@@ -302,17 +302,17 @@ Error(JSContext* cx, const char (&input)
     CHECK(JS_GetPendingException(cx, &exn));
     JS_ClearPendingException(cx);
 
     js::ErrorReport report(cx);
     CHECK(report.init(cx, exn, js::ErrorReport::WithSideEffects));
     CHECK(report.report()->errorNumber == JSMSG_JSON_BAD_PARSE);
 
     const char* lineAndColumnASCII = JS_smprintf("line %d column %d", expectedLine, expectedColumn);
-    CHECK(strstr(report.message().c_str(), lineAndColumnASCII) != nullptr);
+    CHECK(strstr(report.toStringResult().c_str(), lineAndColumnASCII) != nullptr);
     js_free((void*)lineAndColumnASCII);
 
     /* We do not execute JS, so there should be no exception thrown. */
     CHECK(!JS_IsExceptionPending(cx));
 
     return true;
 }
 END_TEST(testParseJSON_error)
--- a/js/src/jsapi-tests/testUncaughtSymbol.cpp
+++ b/js/src/jsapi-tests/testUncaughtSymbol.cpp
@@ -36,18 +36,18 @@ GetSymbolExceptionType(JSContext* cx)
     JS::RootedValue exn(cx);
     MOZ_RELEASE_ASSERT(JS_GetPendingException(cx, &exn));
     MOZ_RELEASE_ASSERT(exn.isSymbol());
     JS_ClearPendingException(cx);
 
     js::ErrorReport report(cx);
     MOZ_RELEASE_ASSERT(report.init(cx, exn, js::ErrorReport::WithSideEffects));
 
-    if (strcmp(report.message().c_str(), "uncaught exception: Symbol(Symbol.iterator)") == 0)
+    if (strcmp(report.toStringResult().c_str(), "uncaught exception: Symbol(Symbol.iterator)") == 0)
         return SYMBOL_ITERATOR;
-    if (strcmp(report.message().c_str(), "uncaught exception: Symbol(foo)") == 0)
+    if (strcmp(report.toStringResult().c_str(), "uncaught exception: Symbol(foo)") == 0)
         return SYMBOL_FOO;
-    if (strcmp(report.message().c_str(), "uncaught exception: Symbol()") == 0)
+    if (strcmp(report.toStringResult().c_str(), "uncaught exception: Symbol()") == 0)
         return SYMBOL_EMPTY;
     MOZ_CRASH("Unexpected symbol");
 }
 
 END_TEST(testUncaughtSymbol)
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -377,18 +377,18 @@ js::ReportUsageErrorASCII(JSContext* cx,
         JSAutoByteString str;
         if (!str.encodeUtf8(cx, usageStr))
             return;
         JS_ReportErrorUTF8(cx, "%s. Usage: %s", msg, str.ptr());
     }
 }
 
 bool
-js::PrintError(JSContext* cx, FILE* file, const char* message, JSErrorReport* report,
-               bool reportWarnings)
+js::PrintError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult,
+               JSErrorReport* report, bool reportWarnings)
 {
     MOZ_ASSERT(report);
 
     /* Conditionally ignore reported warnings. */
     if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
         return false;
 
     char* prefix = nullptr;
@@ -402,18 +402,17 @@ js::PrintError(JSContext* cx, FILE* file
     if (JSREPORT_IS_WARNING(report->flags)) {
         char* tmp = prefix;
         prefix = JS_smprintf("%s%swarning: ",
                              tmp ? tmp : "",
                              JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
         JS_free(cx, tmp);
     }
 
-    if (!message)
-        message = report->message().c_str();
+    const char* message = toStringResult ? toStringResult.c_str() : report->message().c_str();
 
     /* embedded newlines -- argh! */
     const char* ctmp;
     while ((ctmp = strchr(message, '\n')) != 0) {
         ctmp++;
         if (prefix)
             fputs(prefix, file);
         fwrite(message, 1, ctmp - message, file);
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -626,18 +626,18 @@ ReportUsageErrorASCII(JSContext* cx, Han
 
 /*
  * Prints a full report and returns true if the given report is non-nullptr
  * and the report doesn't have the JSREPORT_WARNING flag set or reportWarnings
  * is true.
  * Returns false otherwise.
  */
 extern bool
-PrintError(JSContext* cx, FILE* file, const char* message, JSErrorReport* report,
-           bool reportWarnings);
+PrintError(JSContext* cx, FILE* file, JS::ConstUTF8CharsZ toStringResult,
+           JSErrorReport* report, bool reportWarnings);
 
 /*
  * Send a JSErrorReport to the warningReporter callback.
  */
 void
 CallWarningReporter(JSContext* cx, JSErrorReport* report);
 
 extern bool
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -23,16 +23,17 @@
 #include "jsobj.h"
 #include "jsprf.h"
 #include "jsscript.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jswrapper.h"
 
 #include "gc/Marking.h"
+#include "js/CharacterEncoding.h"
 #include "vm/ErrorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/SavedStacks.h"
 #include "vm/StringBuffer.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/ErrorObject-inl.h"
@@ -507,17 +508,17 @@ js::ErrorToException(JSContext* cx, JSEr
 {
     MOZ_ASSERT(reportp);
     MOZ_ASSERT(!JSREPORT_IS_WARNING(reportp->flags));
 
     // We cannot throw a proper object inside the self-hosting compartment, as
     // we cannot construct the Error constructor without self-hosted code. Just
     // print the error to stderr to help debugging.
     if (cx->runtime()->isSelfHostingCompartment(cx->compartment())) {
-        PrintError(cx, stderr, nullptr, reportp, true);
+        PrintError(cx, stderr, JS::ConstUTF8CharsZ(), reportp, true);
         return;
     }
 
     // Find the exception index associated with this error.
     JSErrNum errorNumber = static_cast<JSErrNum>(reportp->errorNumber);
     if (!callback)
         callback = GetErrorMessage;
     const JSErrorFormatString* errorString = callback(userRef, errorNumber);
@@ -847,17 +848,17 @@ ErrorReport::init(JSContext* cx, HandleV
                 cx->clearPendingException();
                 str = nullptr;
             }
         }
     }
 
     const char* utf8Message = nullptr;
     if (str)
-        utf8Message = bytesStorage.encodeUtf8(cx, 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);
@@ -865,17 +866,17 @@ ErrorReport::init(JSContext* cx, HandleV
         // but without the reporting bits.  Instead it just puts all
         // the stuff we care about in our ownedReport and message_.
         if (!populateUncaughtExceptionReportUTF8(cx, utf8Message)) {
             // Just give up.  We're out of memory or something; not much we can
             // do here.
             return false;
         }
     } else {
-        message_ = JS::ConstUTF8CharsZ(utf8Message, strlen(utf8Message));
+        toStringResult_ = JS::ConstUTF8CharsZ(utf8Message, strlen(utf8Message));
         /* Flag the error as an exception. */
         reportp->flags |= JSREPORT_EXCEPTION;
     }
 
     return true;
 }
 
 bool
@@ -909,17 +910,17 @@ ErrorReport::populateUncaughtExceptionRe
     }
 
     if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr,
                                 JSMSG_UNCAUGHT_EXCEPTION,
                                 nullptr, ArgumentsAreUTF8, &ownedReport, ap)) {
         return false;
     }
 
-    message_ = ownedReport.message();
+    toStringResult_ = ownedReport.message();
     reportp = &ownedReport;
     return true;
 }
 
 JSObject*
 js::CopyErrorObject(JSContext* cx, Handle<ErrorObject*> err)
 {
     js::ScopedJSFreePtr<JSErrorReport> copyReport;
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1447,19 +1447,19 @@ struct MOZ_STACK_CLASS JS_FRIEND_API(Err
     bool init(JSContext* cx, JS::HandleValue exn,
               SniffingBehavior sniffingBehavior);
 
     JSErrorReport* report()
     {
         return reportp;
     }
 
-    const JS::ConstUTF8CharsZ message()
+    const JS::ConstUTF8CharsZ toStringResult()
     {
-        return message_;
+        return toStringResult_;
     }
 
   private:
     // More or less an equivalent of JS_ReportErrorNumber/js::ReportErrorNumberVA
     // but fills in an ErrorReport instead of reporting it.  Uses varargs to
     // make it simpler to call js::ExpandErrorArgumentsVA.
     //
     // Returns false if we fail to actually populate the ErrorReport
@@ -1468,37 +1468,37 @@ struct MOZ_STACK_CLASS JS_FRIEND_API(Err
     bool populateUncaughtExceptionReportUTF8VA(JSContext* cx, va_list ap);
 
     // Reports exceptions from add-on scopes to telementry.
     void ReportAddonExceptionToTelementry(JSContext* cx);
 
     // We may have a provided JSErrorReport, so need a way to represent that.
     JSErrorReport* reportp;
 
-    // And we may have a message.
-    JS::ConstUTF8CharsZ message_;
-
     // Or we may need to synthesize a JSErrorReport one of our own.
     JSErrorReport ownedReport;
 
     // And we have a string to maybe keep alive that has pointers into
     // it from ownedReport.
     JS::RootedString str;
 
     // And keep its chars alive too.
     AutoStableStringChars strChars;
 
     // And we need to root our exception value.
     JS::RootedObject exnObject;
 
-    // And possibly some byte storage for our message_.
-    JSAutoByteString bytesStorage;
-
     // And for our 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_;
+    JSAutoByteString toStringResultBytesStorage;
 };
 
 /* Implemented in vm/StructuredClone.cpp. */
 extern JS_FRIEND_API(uint64_t)
 GetSCOffset(JSStructuredCloneWriter* writer);
 
 namespace Scalar {
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -6398,17 +6398,17 @@ js::shell::AutoReportException::~AutoRep
         fflush(stderr);
         JS_ClearPendingException(cx);
         return;
     }
 
     MOZ_ASSERT(!JSREPORT_IS_WARNING(report.report()->flags));
 
     FILE* fp = ErrorFilePointer();
-    PrintError(cx, fp, report.message().c_str(), report.report(), reportWarnings);
+    PrintError(cx, fp, report.toStringResult(), report.report(), reportWarnings);
 
     {
         JS::AutoSaveExceptionState savedExc(cx);
         if (!PrintStackTrace(cx, exn))
             fputs("(Unable to print stack trace)\n", fp);
         savedExc.restore();
     }
 
@@ -6434,17 +6434,17 @@ js::shell::WarningReporter(JSContext* cx
         if (!CreateLastWarningObject(cx, report)) {
             fputs("Unhandled error happened while creating last warning object.\n", fp);
             fflush(fp);
         }
         savedExc.restore();
     }
 
     // Print the warning.
-    PrintError(cx, fp, nullptr, report, reportWarnings);
+    PrintError(cx, fp, JS::ConstUTF8CharsZ(), report, reportWarnings);
 }
 
 static bool
 global_enumerate(JSContext* cx, HandleObject obj)
 {
 #ifdef LAZY_STANDARD_CLASSES
     return JS_EnumerateStandardClasses(cx, obj);
 #else
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -32,16 +32,17 @@
 #include "builtin/SelfHostingDefines.h"
 #include "builtin/SIMD.h"
 #include "builtin/TypedObject.h"
 #include "builtin/WeakSetObject.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "jit/AtomicOperations.h"
 #include "jit/InlinableNatives.h"
+#include "js/CharacterEncoding.h"
 #include "js/Date.h"
 #include "vm/Compression.h"
 #include "vm/GeneratorObject.h"
 #include "vm/Interpreter.h"
 #include "vm/RegExpObject.h"
 #include "vm/String.h"
 #include "vm/TypedArrayCommon.h"
 #include "vm/WrapperObject.h"
@@ -66,17 +67,17 @@ using mozilla::PodMove;
 using mozilla::Maybe;
 
 static void
 selfHosting_WarningReporter(JSContext* cx, JSErrorReport* report)
 {
     MOZ_ASSERT(report);
     MOZ_ASSERT(JSREPORT_IS_WARNING(report->flags));
 
-    PrintError(cx, stderr, nullptr, report, true);
+    PrintError(cx, stderr, JS::ConstUTF8CharsZ(), report, true);
 }
 
 static bool
 intrinsic_ToObject(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedValue val(cx, args[0]);
     RootedObject obj(cx, ToObject(cx, val));
@@ -2677,17 +2678,17 @@ MaybePrintAndClearPendingException(JSCon
 
     ErrorReport report(cx);
     if (!report.init(cx, exn, js::ErrorReport::WithSideEffects)) {
         fprintf(file, "out of memory initializing ErrorReport\n");
         return;
     }
 
     MOZ_ASSERT(!JSREPORT_IS_WARNING(report.report()->flags));
-    PrintError(cx, file, report.message().c_str(), report.report(), true);
+    PrintError(cx, file, report.toStringResult(), report.report(), true);
 }
 
 class MOZ_STACK_CLASS AutoSelfHostingErrorReporter
 {
     JSContext* cx_;
     JS::WarningReporter oldReporter_;
 
   public:
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -1085,21 +1085,21 @@ XPCConvert::JSValToXPCException(MutableH
             }
         } 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))) {
-                JSAutoByteString message;
-                JSString* str;
-                if (nullptr != (str = ToString(cx, s)))
-                    message.encodeLatin1(cx, str);
-                return JSErrorToXPCException(message.ptr(), ifaceName,
+                JSAutoByteString toStringResult;
+                RootedString str(cx, ToString(cx, s));
+                if (str)
+                    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.
 
@@ -1186,31 +1186,31 @@ XPCConvert::JSValToXPCException(MutableH
     }
     return NS_ERROR_FAILURE;
 }
 
 /********************************/
 
 // static
 nsresult
-XPCConvert::JSErrorToXPCException(const char* message,
+XPCConvert::JSErrorToXPCException(const char* toStringResult,
                                   const char* ifaceName,
                                   const char* methodName,
                                   const JSErrorReport* report,
                                   nsIException** exceptn)
 {
     AutoJSContext cx;
     nsresult rv = NS_ERROR_FAILURE;
     RefPtr<nsScriptError> data;
     if (report) {
         nsAutoString bestMessage;
         if (report && report->message()) {
             CopyUTF8toUTF16(report->message().c_str(), bestMessage);
-        } else if (message) {
-            CopyASCIItoUTF16(message, bestMessage);
+        } else if (toStringResult) {
+            CopyUTF8toUTF16(toStringResult, bestMessage);
         } else {
             bestMessage.AssignLiteral("JavaScript Error");
         }
 
         const char16_t* linebuf = report->linebuf();
 
         data = new nsScriptError();
         data->InitWithWindowID(
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -164,26 +164,26 @@ nsXPConnect::IsISupportsDescendant(nsIIn
 {
     bool found = false;
     if (info)
         info->HasAncestor(&NS_GET_IID(nsISupports), &found);
     return found;
 }
 
 void
-xpc::ErrorReport::Init(JSErrorReport* aReport, const char* aFallbackMessage,
+xpc::ErrorReport::Init(JSErrorReport* aReport, const char* aToStringResult,
                        bool aIsChrome, uint64_t aWindowID)
 {
     mCategory = aIsChrome ? NS_LITERAL_CSTRING("chrome javascript")
                           : NS_LITERAL_CSTRING("content javascript");
     mWindowID = aWindowID;
 
     ErrorReportToMessageString(aReport, mErrorMsg);
-    if (mErrorMsg.IsEmpty() && aFallbackMessage) {
-        mErrorMsg.AssignWithConversion(aFallbackMessage);
+    if (mErrorMsg.IsEmpty() && aToStringResult) {
+        AppendUTF8toUTF16(aToStringResult, mErrorMsg);
     }
 
     if (!aReport->filename) {
         mFileName.SetIsVoid(true);
     } else {
         mFileName.AssignWithConversion(aReport->filename);
     }
 
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -2371,17 +2371,17 @@ public:
                                         uint32_t count, const nsXPTType& type,
                                         nsresult* pErr);
 
     static nsresult JSValToXPCException(JS::MutableHandleValue s,
                                         const char* ifaceName,
                                         const char* methodName,
                                         nsIException** exception);
 
-    static nsresult JSErrorToXPCException(const char* message,
+    static nsresult JSErrorToXPCException(const char* toStringResult,
                                           const char* ifaceName,
                                           const char* methodName,
                                           const JSErrorReport* report,
                                           nsIException** exception);
 
     static nsresult ConstructException(nsresult rv, const char* message,
                                        const char* ifaceName,
                                        const char* methodName,
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -511,17 +511,17 @@ class ErrorReport {
 
     ErrorReport() : mWindowID(0)
                   , mLineNumber(0)
                   , mColumn(0)
                   , mFlags(0)
                   , mIsMuted(false)
     {}
 
-    void Init(JSErrorReport* aReport, const char* aFallbackMessage,
+    void Init(JSErrorReport* aReport, const char* aToStringResult,
               bool aIsChrome, uint64_t aWindowID);
     void Init(JSContext* aCx, mozilla::dom::Exception* aException,
               bool aIsChrome, uint64_t aWindowID);
     // Log the error report to the console.  Which console will depend on the
     // window id it was initialized with.
     void LogToConsole();
     // Log to console, using the given stack object (which should be a stack of
     // the sort that JS::CaptureCurrentStack produces).  aStack is allowed to be
--- a/xpcom/string/nsUTF8Utils.h
+++ b/xpcom/string/nsUTF8Utils.h
@@ -8,16 +8,17 @@
 
 // This file may be used in two ways: if MOZILLA_INTERNAL_API is defined, this
 // file will provide signatures for the Mozilla abstract string types. It will
 // use XPCOM assertion/debugging macros, etc.
 
 #include "nscore.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/SSE.h"
+#include "mozilla/TypeTraits.h"
 
 #include "nsCharTraits.h"
 
 class UTF8traits
 {
 public:
   static bool isASCII(char aChar)
   {
@@ -717,9 +718,25 @@ public:
     *mDestination = '\0';
   }
 
 private:
   char* mDestination;
 };
 #endif // MOZILLA_INTERNAL_API
 
+
+template<typename Char, typename UnsignedT>
+inline UnsignedT
+RewindToPriorUTF8Codepoint(const Char* utf8Chars, UnsignedT index)
+{
+  static_assert(mozilla::IsSame<Char, char>::value ||
+                mozilla::IsSame<Char, unsigned char>::value ||
+                mozilla::IsSame<Char, signed char>::value,
+                "UTF-8 data must be in 8-bit units");
+  static_assert(mozilla::IsUnsigned<UnsignedT>::value, "index type must be unsigned");
+  while (index > 0 && (utf8Chars[index] & 0xC0) == 0x80)
+    --index;
+
+  return index;
+}
+
 #endif /* !defined(nsUTF8Utils_h_) */