Bug 1283710 - Part 4: Do not duplicate message in ExpandErrorArgumentsVA, and remove message parameter from WarningReporter. r=jwalden
☠☠ backed out by 971d67779565 ☠ ☠
authorTooru Fujisawa <arai_a@mac.com>
Sun, 14 Aug 2016 20:39:31 +0900
changeset 318502 ee5215f1a38e702d7335da05316ead5978e64986
parent 318501 dcedbaefe39987d5f802b2c579391d7352df2891
child 318503 d72527b7d3c0c328e65bdaf4c259921a1e8481c2
push id30843
push usercbook@mozilla.com
push dateWed, 19 Oct 2016 15:02:57 +0000
treeherdermozilla-central@f40960c63bfa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
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 4: Do not duplicate message in ExpandErrorArgumentsVA, and remove message parameter from WarningReporter. r=jwalden
dom/base/ScriptSettings.cpp
dom/promise/Promise.cpp
dom/workers/ScriptLoader.cpp
dom/workers/WorkerPrivate.cpp
js/src/frontend/TokenStream.cpp
js/src/frontend/TokenStream.h
js/src/gdb/gdb-tests.cpp
js/src/jsapi-tests/testParseJSON.cpp
js/src/jsapi-tests/testUncaughtSymbol.cpp
js/src/jsapi-tests/tests.h
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsexn.cpp
js/src/jsexn.h
js/src/jsfriendapi.h
js/src/shell/js.cpp
js/src/shell/jsshell.h
js/src/vm/SelfHosting.cpp
netwerk/base/ProxyAutoConfig.cpp
xpcom/base/CycleCollectedJSContext.cpp
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -327,18 +327,17 @@ AutoJSAPI::~AutoJSAPI()
   if (mIsMainThread) {
     mAutoRequest.reset();
   }
 
   ScriptSettingsStack::Pop(this);
 }
 
 void
-WarningOnlyErrorReporter(JSContext* aCx, const char* aMessage,
-                         JSErrorReport* aRep);
+WarningOnlyErrorReporter(JSContext* aCx, JSErrorReport* aRep);
 
 void
 AutoJSAPI::InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal,
                         JSContext* aCx, bool aIsMainThread)
 {
   MOZ_ASSERT(aCx);
   MOZ_ASSERT(aCx == danger::GetJSContext());
   MOZ_ASSERT(aIsMainThread == NS_IsMainThread());
@@ -515,43 +514,43 @@ AutoJSAPI::Init(nsGlobalWindow* aWindow)
 
 // Even with autoJSAPIOwnsErrorReporting, the JS engine still sends warning
 // reports to the JSErrorReporter as soon as they are generated. These go
 // directly to the console, so we can handle them easily here.
 //
 // Eventually, SpiderMonkey will have a special-purpose callback for warnings
 // only.
 void
-WarningOnlyErrorReporter(JSContext* aCx, const char* aMessage, JSErrorReport* aRep)
+WarningOnlyErrorReporter(JSContext* aCx, JSErrorReport* aRep)
 {
   MOZ_ASSERT(JSREPORT_IS_WARNING(aRep->flags));
   if (!NS_IsMainThread()) {
     // Reporting a warning on workers is a bit complicated because we have to
     // climb our parent chain until we get to the main thread.  So go ahead and
     // 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, aMessage, aRep);
+    worker->ReportError(aCx, nullptr, 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
     // DOM Window.
     win = xpc::AddonWindowOrNull(JS::CurrentGlobalOrNull(aCx));
   }
-  xpcReport->Init(aRep, aMessage, nsContentUtils::IsCallerChrome(),
+  xpcReport->Init(aRep, nullptr, nsContentUtils::IsCallerChrome(),
                   win ? win->AsInner()->WindowID() : 0);
   xpcReport->LogToConsole();
 }
 
 void
 AutoJSAPI::ReportException()
 {
   if (!HasException()) {
@@ -581,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(),
+      xpcReport->Init(jsReport.report(), jsReport.message().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));
@@ -605,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(), jsReport.report());
+      worker->ReportError(cx(), jsReport.message().c_str(), 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,18 @@ 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(), isChrome, win ? win->AsInner()->WindowID() : 0);
+  xpcReport->Init(report.report(), report.message().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
 Promise::PerformMicroTaskCheckpoint()
--- 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(),
+  xpcReport->Init(report.report(), report.message().c_str(),
                   aWorkerPrivate->IsChromeWorker(), aWorkerPrivate->WindowID());
 
   RefPtr<AsyncErrorReporter> r = new AsyncErrorReporter(xpcReport);
   NS_DispatchToMainThread(r);
 }
 
 void
 LoadAllScripts(WorkerPrivate* aWorkerPrivate,
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -5850,17 +5850,17 @@ 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()) {
+  if (message.IsEmpty() && aFallbackMessage) {
     nsDependentCString fallbackMessage(aFallbackMessage);
     if (!AppendUTF8toUTF16(fallbackMessage, 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);
     }
   }
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -563,38 +563,36 @@ TokenStream::reportStrictModeErrorNumber
 
     return reportCompileErrorNumberVA(offset, flags, errorNumber, args);
 }
 
 void
 CompileError::throwError(JSContext* cx)
 {
     if (JSREPORT_IS_WARNING(report.flags)) {
-        CallWarningReporter(cx, message, &report);
+        CallWarningReporter(cx, &report);
         return;
     }
 
     // If there's a runtime exception type associated with this error
     // number, set that as the pending exception.  For errors occuring at
     // compile time, this is very likely to be a JSEXN_SYNTAXERR.
     //
     // If an exception is thrown but not caught, the JSREPORT_EXCEPTION
     // flag will be set in report.flags.  Proper behavior for an error
     // reporter is to ignore a report with this flag for all but top-level
     // compilation errors.  The exception will remain pending, and so long
     // as the non-top-level "load", "eval", or "compile" native function
     // returns false, the top-level reporter will eventually receive the
     // uncaught exception report.
-    ErrorToException(cx, message, &report, nullptr, nullptr);
+    ErrorToException(cx, &report, nullptr, nullptr);
 }
 
 CompileError::~CompileError()
 {
-    js_free(message);
-    message = nullptr;
 }
 
 bool
 TokenStream::reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigned errorNumber,
                                         va_list args)
 {
     bool warning = JSREPORT_IS_WARNING(flags);
 
@@ -631,17 +629,17 @@ TokenStream::reportCompileErrorNumberVA(
                                  cx->compartment()->principals());
         if (!iter.done() && iter.filename()) {
             callerFilename = true;
             err.report.filename = iter.filename();
             err.report.lineno = iter.computeLine(&err.report.column);
         }
     }
 
-    if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr, errorNumber, &err.message,
+    if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr, errorNumber,
                                 nullptr, ArgumentsAreLatin1, &err.report, args))
     {
         return false;
     }
 
     // Given a token, T, that we want to complain about: if T's (starting)
     // lineno doesn't match TokenStream's lineno, that means we've scanned past
     // the line that T starts on, which makes it hard to print some or all of
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -234,18 +234,17 @@ struct Token
     DecimalPoint decimalPoint() const {
         MOZ_ASSERT(type == TOK_NUMBER);
         return u.number.decimalPoint;
     }
 };
 
 struct CompileError {
     JSErrorReport report;
-    char* message;
-    CompileError() : message(nullptr) {}
+    CompileError() {}
     ~CompileError();
     void throwError(JSContext* cx);
 
   private:
     // CompileError owns raw allocated memory, so disable assignment and copying
     // for safety.
     void operator=(const CompileError&) = delete;
     CompileError(const CompileError&) = delete;
--- a/js/src/gdb/gdb-tests.cpp
+++ b/js/src/gdb/gdb-tests.cpp
@@ -38,22 +38,22 @@ checkPtr(T* ptr)
 static void
 checkBool(bool success)
 {
   if (! success)
     abort();
 }
 
 /* The warning reporter callback. */
-void reportWarning(JSContext* cx, const char* message, JSErrorReport* report)
+void reportWarning(JSContext* cx, JSErrorReport* report)
 {
     fprintf(stderr, "%s:%u: %s\n",
             report->filename ? report->filename : "<no filename>",
             (unsigned int) report->lineno,
-            message);
+            report->message().c_str());
 }
 
 // prologue.py sets a breakpoint on this function; test functions can call it
 // to easily return control to GDB where desired.
 void breakpoint() {
     // If we leave this function empty, the linker will unify it with other
     // empty functions throughout SpiderMonkey. If we then set a GDB
     // breakpoint on it, that breakpoint will hit at all sorts of random
--- 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(), lineAndColumnASCII) != nullptr);
+    CHECK(strstr(report.message().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(), "uncaught exception: Symbol(Symbol.iterator)") == 0)
+    if (strcmp(report.message().c_str(), "uncaught exception: Symbol(Symbol.iterator)") == 0)
         return SYMBOL_ITERATOR;
-    if (strcmp(report.message(), "uncaught exception: Symbol(foo)") == 0)
+    if (strcmp(report.message().c_str(), "uncaught exception: Symbol(foo)") == 0)
         return SYMBOL_FOO;
-    if (strcmp(report.message(), "uncaught exception: Symbol()") == 0)
+    if (strcmp(report.message().c_str(), "uncaught exception: Symbol()") == 0)
         return SYMBOL_EMPTY;
     MOZ_CRASH("Unexpected symbol");
 }
 
 END_TEST(testUncaughtSymbol)
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -293,24 +293,24 @@ class JSAPITest
     }
 
     virtual void destroyContext() {
         MOZ_RELEASE_ASSERT(cx);
         JS_DestroyContext(cx);
         cx = nullptr;
     }
 
-    static void reportWarning(JSContext* cx, const char* message, JSErrorReport* report) {
+    static void reportWarning(JSContext* cx, JSErrorReport* report) {
         MOZ_RELEASE_ASSERT(report);
         MOZ_RELEASE_ASSERT(JSREPORT_IS_WARNING(report->flags));
 
         fprintf(stderr, "%s:%u:%s\n",
                 report->filename ? report->filename : "<no filename>",
                 (unsigned int) report->lineno,
-                message);
+                report->message().c_str());
     }
 
     virtual const JSClass * getGlobalClass() {
         return basicGlobalClass();
     }
 
     virtual JSObject * createGlobal(JSPrincipals* principals = nullptr);
 };
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -5391,18 +5391,17 @@ class JSErrorReport
  * appropriate.
  */
 #define JSREPORT_IS_WARNING(flags)      (((flags) & JSREPORT_WARNING) != 0)
 #define JSREPORT_IS_EXCEPTION(flags)    (((flags) & JSREPORT_EXCEPTION) != 0)
 #define JSREPORT_IS_STRICT(flags)       (((flags) & JSREPORT_STRICT) != 0)
 
 namespace JS {
 
-typedef void
-(* WarningReporter)(JSContext* cx, const char* message, JSErrorReport* report);
+using WarningReporter = void (*)(JSContext* cx, JSErrorReport* report);
 
 extern JS_PUBLIC_API(WarningReporter)
 SetWarningReporter(JSContext* cx, WarningReporter reporter);
 
 extern JS_PUBLIC_API(WarningReporter)
 GetWarningReporter(JSContext* cx);
 
 extern JS_PUBLIC_API(bool)
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -154,38 +154,38 @@ AutoResolving::alreadyStartedSlow() cons
         MOZ_ASSERT(this != cursor);
         if (object.get() == cursor->object && id.get() == cursor->id && kind == cursor->kind)
             return true;
     } while (!!(cursor = cursor->link));
     return false;
 }
 
 static void
-ReportError(JSContext* cx, const char* message, JSErrorReport* reportp,
-            JSErrorCallback callback, void* userRef)
+ReportError(JSContext* cx, JSErrorReport* reportp, JSErrorCallback callback,
+            void* userRef)
 {
     /*
      * Check the error report, and set a JavaScript-catchable exception
      * if the error is defined to have an associated exception.  If an
      * exception is thrown, then the JSREPORT_EXCEPTION flag will be set
      * on the error report, and exception-aware hosts should ignore it.
      */
     MOZ_ASSERT(reportp);
     if ((!callback || callback == GetErrorMessage) &&
         reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)
     {
         reportp->flags |= JSREPORT_EXCEPTION;
     }
 
     if (JSREPORT_IS_WARNING(reportp->flags)) {
-        CallWarningReporter(cx, message, reportp);
+        CallWarningReporter(cx, reportp);
         return;
     }
 
-    ErrorToException(cx, message, reportp, callback, userRef);
+    ErrorToException(cx, reportp, callback, userRef);
 }
 
 /*
  * The given JSErrorReport object have been zeroed and must not outlive
  * cx->fp() (otherwise owned fields may become invalid).
  */
 static void
 PopulateReportBlame(JSContext* cx, JSErrorReport* report)
@@ -317,17 +317,16 @@ checkReportFlags(JSContext* cx, unsigned
     return false;
 }
 
 bool
 js::ReportErrorVA(JSContext* cx, unsigned flags, const char* format,
                   ErrorArgumentsType argumentsType, va_list ap)
 {
     JSErrorReport report;
-    bool warning;
 
     if (checkReportFlags(cx, &flags))
         return true;
 
     UniqueChars message(JS_vsmprintf(format, ap));
     if (!message) {
         ReportOutOfMemory(cx);
         return false;
@@ -344,19 +343,19 @@ js::ReportErrorVA(JSContext* cx, unsigne
         Latin1Chars latin1(message.get(), strlen(message.get()));
         UTF8CharsZ utf8(JS::CharsToNewUTF8CharsZ(cx, latin1));
         if (!utf8)
             return false;
         report.initOwnedMessage(reinterpret_cast<const char*>(utf8.get()));
     }
     PopulateReportBlame(cx, &report);
 
-    warning = JSREPORT_IS_WARNING(report.flags);
+    bool warning = JSREPORT_IS_WARNING(report.flags);
 
-    ReportError(cx, report.message().c_str(), &report, nullptr, nullptr);
+    ReportError(cx, &report, nullptr, nullptr);
     return warning;
 }
 
 /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
 void
 js::ReportUsageErrorASCII(JSContext* cx, HandleObject callee, const char* msg)
 {
     const char* usageStr = "usage";
@@ -381,21 +380,17 @@ js::ReportUsageErrorASCII(JSContext* cx,
         JS_ReportErrorUTF8(cx, "%s. Usage: %s", msg, str.ptr());
     }
 }
 
 bool
 js::PrintError(JSContext* cx, FILE* file, const char* message, JSErrorReport* report,
                bool reportWarnings)
 {
-    if (!report) {
-        fprintf(file, "%s\n", message);
-        fflush(file);
-        return false;
-    }
+    MOZ_ASSERT(report);
 
     /* Conditionally ignore reported warnings. */
     if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)
         return false;
 
     char* prefix = nullptr;
     if (report->filename)
         prefix = JS_smprintf("%s:", report->filename);
@@ -407,16 +402,19 @@ 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();
+
     /* embedded newlines -- argh! */
     const char* ctmp;
     while ((ctmp = strchr(message, '\n')) != 0) {
         ctmp++;
         if (prefix)
             fputs(prefix, file);
         fwrite(message, 1, ctmp - message, file);
         message = ctmp;
@@ -579,24 +577,22 @@ class MOZ_RAII AutoMessageArgs
  * is to be replaced by the Nth argument from the va_list. The complete
  * message is placed into reportp->message_.
  *
  * Returns true if the expansion succeeds (can fail if out of memory).
  */
 bool
 js::ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback,
                            void* userRef, const unsigned errorNumber,
-                           char** messagep, const char16_t** messageArgs,
+                           const char16_t** messageArgs,
                            ErrorArgumentsType argumentsType,
                            JSErrorReport* reportp, va_list ap)
 {
     const JSErrorFormatString* efs;
 
-    *messagep = nullptr;
-
     if (!callback)
         callback = GetErrorMessage;
 
     {
         AutoSuppressGC suppressGC(cx);
         efs = callback(userRef, errorNumber);
     }
 
@@ -624,29 +620,29 @@ js::ExpandErrorArgumentsVA(ExclusiveCont
                 size_t len = strlen(efs->format);
 
                 AutoMessageArgs args;
                 if (!args.init(cx, messageArgs, argCount, argumentsType, ap))
                     return false;
 
                 buffer = fmt = InflateString(cx, efs->format, &len);
                 if (!buffer)
-                    goto error;
+                    return false;
                 expandedLength = len
                                  - (3 * args.count()) /* exclude the {n} */
                                  + args.totalLength();
 
                 /*
                 * Note - the above calculation assumes that each argument
                 * is used once and only once in the expansion !!!
                 */
                 ucmessage = out = cx->pod_malloc<char16_t>(expandedLength + 1);
                 if (!out) {
                     js_free(buffer);
-                    goto error;
+                    return false;
                 }
                 while (*fmt) {
                     if (*fmt == '{') {
                         if (isdigit(fmt[1])) {
                             int d = JS7_UNDEC(fmt[1]);
                             MOZ_RELEASE_ASSERT(d < args.count());
                             js_strncpy(out, args.args(d), args.lengths(d));
                             out += args.lengths(d);
@@ -659,104 +655,87 @@ js::ExpandErrorArgumentsVA(ExclusiveCont
                     }
                     *out++ = *fmt++;
                 }
                 MOZ_ASSERT(expandedArgs == args.count());
                 *out = 0;
                 js_free(buffer);
                 size_t msgLen = PointerRangeSize(ucmessage, static_cast<const char16_t*>(out));
                 mozilla::Range<const char16_t> ucmsg(ucmessage, msgLen);
-                *messagep = JS::LossyTwoByteCharsToNewLatin1CharsZ(cx, ucmsg).c_str();
-                if (!*messagep)
-                    goto error;
-
                 char* utf8 = JS::CharsToNewUTF8CharsZ(cx, ucmsg).c_str();
                 if (!utf8)
-                    goto error;
+                    return false;
                 reportp->initOwnedMessage(utf8);
             }
         } else {
             /* Non-null messageArgs should have at least one non-null arg. */
             MOZ_ASSERT(!messageArgs);
             /*
              * Zero arguments: the format string (if it exists) is the
              * entire message.
              */
             if (efs->format) {
-                *messagep = DuplicateString(cx, efs->format).release();
-                if (!*messagep)
-                    goto error;
-
                 char* message = DuplicateString(cx, efs->format).release();
                 if (!message)
-                    goto error;
+                    return false;
                 reportp->initOwnedMessage(message);
             }
         }
     }
-    if (*messagep == nullptr) {
+    if (!reportp->message()) {
         /* where's the right place for this ??? */
         const char* defaultErrorMessage
             = "No error message available for error number %d";
         size_t nbytes = strlen(defaultErrorMessage) + 16;
-        *messagep = cx->pod_malloc<char>(nbytes);
-        if (!*messagep)
-            goto error;
-        snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);
+        char* message = cx->pod_malloc<char>(nbytes);
+        if (!message)
+            return false;
+        snprintf(message, nbytes, defaultErrorMessage, errorNumber);
+        reportp->initOwnedMessage(message);
     }
     return true;
-
-error:
-    if (*messagep) {
-        js_free((void*)*messagep);
-        *messagep = nullptr;
-    }
-    return false;
 }
 
 bool
 js::ReportErrorNumberVA(JSContext* cx, unsigned flags, JSErrorCallback callback,
                         void* userRef, const unsigned errorNumber,
                         ErrorArgumentsType argumentsType, va_list ap)
 {
     JSErrorReport report;
-    char* message;
     bool warning;
 
     if (checkReportFlags(cx, &flags))
         return true;
     warning = JSREPORT_IS_WARNING(flags);
 
     report.flags = flags;
     report.errorNumber = errorNumber;
     PopulateReportBlame(cx, &report);
 
     if (!ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
-                                &message, nullptr, argumentsType, &report, ap)) {
+                                nullptr, argumentsType, &report, ap)) {
         return false;
     }
 
-    ReportError(cx, message, &report, callback, userRef);
-
-    js_free(message);
+    ReportError(cx, &report, callback, userRef);
 
     return warning;
 }
 
 static bool
 ExpandErrorArguments(ExclusiveContext* cx, JSErrorCallback callback,
                      void* userRef, const unsigned errorNumber,
-                     char** messagep, const char16_t** messageArgs,
+                     const char16_t** messageArgs,
                      ErrorArgumentsType argumentsType,
                      JSErrorReport* reportp, ...)
 {
     va_list ap;
     va_start(ap, reportp);
     bool expanded = js::ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
-                                               messagep, messageArgs, argumentsType, reportp, ap);
+                                               messageArgs, argumentsType, reportp, ap);
     va_end(ap);
     return expanded;
 }
 
 bool
 js::ReportErrorNumberUCArray(JSContext* cx, unsigned flags, JSErrorCallback callback,
                              void* userRef, const unsigned errorNumber,
                              const char16_t** args)
@@ -765,39 +744,35 @@ js::ReportErrorNumberUCArray(JSContext* 
         return true;
     bool warning = JSREPORT_IS_WARNING(flags);
 
     JSErrorReport report;
     report.flags = flags;
     report.errorNumber = errorNumber;
     PopulateReportBlame(cx, &report);
 
-    char* message;
     if (!ExpandErrorArguments(cx, callback, userRef, errorNumber,
-                              &message, args, ArgumentsAreUnicode, &report))
+                              args, ArgumentsAreUnicode, &report))
     {
         return false;
     }
 
-    ReportError(cx, message, &report, callback, userRef);
-
-    js_free(message);
+    ReportError(cx, &report, callback, userRef);
 
     return warning;
 }
 
 void
-js::CallWarningReporter(JSContext* cx, const char* message, JSErrorReport* reportp)
+js::CallWarningReporter(JSContext* cx, JSErrorReport* reportp)
 {
-    MOZ_ASSERT(message);
     MOZ_ASSERT(reportp);
     MOZ_ASSERT(JSREPORT_IS_WARNING(reportp->flags));
 
     if (JS::WarningReporter warningReporter = cx->runtime()->warningReporter)
-        warningReporter(cx, message, reportp);
+        warningReporter(cx, reportp);
 }
 
 bool
 js::ReportIsNotDefined(JSContext* cx, HandleId id)
 {
     JSAutoByteString printable;
     if (ValueToPrintable(cx, IdToValue(id), &printable)) {
         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_NOT_DEFINED,
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -611,39 +611,39 @@ extern bool
 ReportErrorNumberUCArray(JSContext* cx, unsigned flags, JSErrorCallback callback,
                          void* userRef, const unsigned errorNumber,
                          const char16_t** args);
 #endif
 
 extern bool
 ExpandErrorArgumentsVA(ExclusiveContext* cx, JSErrorCallback callback,
                        void* userRef, const unsigned errorNumber,
-                       char** message, const char16_t** messageArgs,
+                       const char16_t** messageArgs,
                        ErrorArgumentsType argumentsType,
                        JSErrorReport* reportp, va_list ap);
 
 /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
 extern void
 ReportUsageErrorASCII(JSContext* cx, HandleObject callee, const char* msg);
 
 /*
  * 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, printing just the message if the report is nullptr.
+ * Returns false otherwise.
  */
 extern bool
 PrintError(JSContext* cx, FILE* file, const char* message, JSErrorReport* report,
            bool reportWarnings);
 
 /*
  * Send a JSErrorReport to the warningReporter callback.
  */
 void
-CallWarningReporter(JSContext* cx, const char* message, JSErrorReport* report);
+CallWarningReporter(JSContext* cx, JSErrorReport* report);
 
 extern bool
 ReportIsNotDefined(JSContext* cx, HandlePropertyName name);
 
 extern bool
 ReportIsNotDefined(JSContext* cx, HandleId id);
 
 /*
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -497,27 +497,27 @@ js::GetErrorTypeName(JSContext* cx, int1
     {
         return nullptr;
     }
     JSProtoKey key = GetExceptionProtoKey(JSExnType(exnType));
     return ClassName(key, cx);
 }
 
 void
-js::ErrorToException(JSContext* cx, const char* message, JSErrorReport* reportp,
+js::ErrorToException(JSContext* cx, JSErrorReport* reportp,
                      JSErrorCallback callback, void* userRef)
 {
     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, message, reportp, true);
+        PrintError(cx, stderr, nullptr, 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);
@@ -629,30 +629,24 @@ ErrorReportToString(JSContext* cx, JSErr
     if (!str)
         return message;
 
     return ConcatStrings<CanGC>(cx, str, message);
 }
 
 ErrorReport::ErrorReport(JSContext* cx)
   : reportp(nullptr),
-    message_(nullptr),
-    ownedMessage(nullptr),
     str(cx),
     strChars(cx),
     exnObject(cx)
 {
 }
 
 ErrorReport::~ErrorReport()
 {
-    if (!ownedMessage)
-        return;
-
-    js_free(ownedMessage);
 }
 
 void
 ErrorReport::ReportAddonExceptionToTelementry(JSContext* cx)
 {
     MOZ_ASSERT(exnObject);
     RootedObject unwrapped(cx, UncheckedUnwrap(exnObject));
     MOZ_ASSERT(unwrapped, "UncheckedUnwrap failed?");
@@ -851,35 +845,37 @@ ErrorReport::init(JSContext* cx, HandleV
                 ownedReport.initOwnedMessage(utf8);
             } else {
                 cx->clearPendingException();
                 str = nullptr;
             }
         }
     }
 
+    const char* utf8Message = nullptr;
     if (str)
-        message_ = bytesStorage.encodeUtf8(cx, str);
-    if (!message_)
-        message_ = "unknown (can't convert to string)";
+        utf8Message = bytesStorage.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, message_);
+        //                            JSMSG_UNCAUGHT_EXCEPTION, utf8Message);
         //
         // but without the reporting bits.  Instead it just puts all
         // the stuff we care about in our ownedReport and message_.
-        if (!populateUncaughtExceptionReportUTF8(cx, 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));
         /* Flag the error as an exception. */
         reportp->flags |= JSREPORT_EXCEPTION;
     }
 
     return true;
 }
 
 bool
@@ -908,24 +904,23 @@ ErrorReport::populateUncaughtExceptionRe
         // XXX: Make the column 1-based as in other browsers, instead of 0-based
         // which is how SpiderMonkey stores it internally. This will be
         // unnecessary once bug 1144340 is fixed.
         ++ownedReport.column;
         ownedReport.isMuted = iter.mutedErrors();
     }
 
     if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr,
-                                JSMSG_UNCAUGHT_EXCEPTION, &ownedMessage,
+                                JSMSG_UNCAUGHT_EXCEPTION,
                                 nullptr, ArgumentsAreUTF8, &ownedReport, ap)) {
         return false;
     }
 
+    message_ = ownedReport.message();
     reportp = &ownedReport;
-    message_ = ownedMessage;
-    ownsMessageAndReport = true;
     return true;
 }
 
 JSObject*
 js::CopyErrorObject(JSContext* cx, Handle<ErrorObject*> err)
 {
     js::ScopedJSFreePtr<JSErrorReport> copyReport;
     if (JSErrorReport* errorReport = err->getErrorReport()) {
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -34,17 +34,17 @@ ComputeStackString(JSContext* cx);
  * cx->exception to a different exception. The original error described by
  * *reportp typically won't be reported anywhere in this case.
  *
  * If the error code is unrecognized, or if we decided to do nothing in order to
  * avoid recursion, we simply return and this error is just being swept under
  * the rug.
  */
 extern void
-ErrorToException(JSContext* cx, const char* message, JSErrorReport* reportp,
+ErrorToException(JSContext* cx, JSErrorReport* reportp,
                  JSErrorCallback callback, void* userRef);
 
 extern JSErrorReport*
 ErrorFromException(JSContext* cx, HandleObject obj);
 
 /*
  * Make a copy of errobj parented to cx's compartment's global.
  *
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1447,17 +1447,17 @@ struct MOZ_STACK_CLASS JS_FRIEND_API(Err
     bool init(JSContext* cx, JS::HandleValue exn,
               SniffingBehavior sniffingBehavior);
 
     JSErrorReport* report()
     {
         return reportp;
     }
 
-    const char* message()
+    const JS::ConstUTF8CharsZ message()
     {
         return message_;
     }
 
   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.
@@ -1469,43 +1469,36 @@ struct MOZ_STACK_CLASS JS_FRIEND_API(Err
 
     // 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.
-    const char* message_;
+    JS::ConstUTF8CharsZ message_;
 
     // Or we may need to synthesize a JSErrorReport one of our own.
     JSErrorReport ownedReport;
 
-    // Or a message of our own.  If this is non-null, we need to clean up both
-    // it and ownedReport.
-    char* ownedMessage;
-
     // 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;
-
-    // True if we need to free message_ and the stuff in ownedReport
-    bool ownsMessageAndReport;
 };
 
 /* 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
@@ -6389,25 +6389,26 @@ js::shell::AutoReportException::~AutoRep
     RootedValue exn(cx);
     (void) JS_GetPendingException(cx, &exn);
 
     JS_ClearPendingException(cx);
 
     ShellContext* sc = GetShellContext(cx);
     js::ErrorReport report(cx);
     if (!report.init(cx, exn, js::ErrorReport::WithSideEffects)) {
-        PrintError(cx, stderr, "out of memory initializing ErrorReport", nullptr, reportWarnings);
+        fprintf(stderr, "out of memory initializing ErrorReport\n");
+        fflush(stderr);
         JS_ClearPendingException(cx);
         return;
     }
 
     MOZ_ASSERT(!JSREPORT_IS_WARNING(report.report()->flags));
 
     FILE* fp = ErrorFilePointer();
-    PrintError(cx, fp, report.message(), report.report(), reportWarnings);
+    PrintError(cx, fp, report.message().c_str(), report.report(), reportWarnings);
 
     {
         JS::AutoSaveExceptionState savedExc(cx);
         if (!PrintStackTrace(cx, exn))
             fputs("(Unable to print stack trace)\n", fp);
         savedExc.restore();
     }
 
@@ -6415,17 +6416,17 @@ js::shell::AutoReportException::~AutoRep
         sc->exitCode = EXITCODE_OUT_OF_MEMORY;
     else
         sc->exitCode = EXITCODE_RUNTIME_ERROR;
 
     JS_ClearPendingException(cx);
 }
 
 void
-js::shell::WarningReporter(JSContext* cx, const char* message, JSErrorReport* report)
+js::shell::WarningReporter(JSContext* cx, JSErrorReport* report)
 {
     ShellContext* sc = GetShellContext(cx);
     FILE* fp = ErrorFilePointer();
 
     MOZ_ASSERT(report);
     MOZ_ASSERT(JSREPORT_IS_WARNING(report->flags));
 
     if (sc->lastWarningEnabled) {
@@ -6433,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, message, report, reportWarnings);
+    PrintError(cx, fp, nullptr, report, reportWarnings);
 }
 
 static bool
 global_enumerate(JSContext* cx, HandleObject obj)
 {
 #ifdef LAZY_STANDARD_CLASSES
     return JS_EnumerateStandardClasses(cx, obj);
 #else
--- a/js/src/shell/jsshell.h
+++ b/js/src/shell/jsshell.h
@@ -19,17 +19,17 @@ enum JSShellErrNum {
 #undef MSG_DEF
     JSShellErr_Limit
 };
 
 const JSErrorFormatString*
 my_GetErrorMessage(void* userRef, const unsigned errorNumber);
 
 void
-WarningReporter(JSContext* cx, const char* message, JSErrorReport* report);
+WarningReporter(JSContext* cx, JSErrorReport* report);
 
 class MOZ_STACK_CLASS AutoReportException
 {
     JSContext* cx;
   public:
     explicit AutoReportException(JSContext* cx)
       : cx(cx)
     {}
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -61,22 +61,22 @@ using namespace js::selfhosted;
 
 using JS::AutoCheckCannotGC;
 using mozilla::IsInRange;
 using mozilla::Maybe;
 using mozilla::PodMove;
 using mozilla::Maybe;
 
 static void
-selfHosting_WarningReporter(JSContext* cx, const char* message, JSErrorReport* report)
+selfHosting_WarningReporter(JSContext* cx, JSErrorReport* report)
 {
     MOZ_ASSERT(report);
     MOZ_ASSERT(JSREPORT_IS_WARNING(report->flags));
 
-    PrintError(cx, stderr, message, report, true);
+    PrintError(cx, stderr, nullptr, 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 +2677,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(), report.report(), true);
+    PrintError(cx, file, report.message().c_str(), report.report(), true);
 }
 
 class MOZ_STACK_CLASS AutoSelfHostingErrorReporter
 {
     JSContext* cx_;
     JS::WarningReporter oldReporter_;
 
   public:
--- a/netwerk/base/ProxyAutoConfig.cpp
+++ b/netwerk/base/ProxyAutoConfig.cpp
@@ -331,17 +331,17 @@ PACLogErrorOrWarning(const nsAString& aK
     formattedMessage.Append(NS_ConvertUTF8toUTF16(aReport->message().c_str()));
   formattedMessage += NS_LITERAL_STRING(" [");
   formattedMessage.Append(aReport->linebuf(), aReport->linebufLength());
   formattedMessage += NS_LITERAL_STRING("]");
   PACLogToConsole(formattedMessage);
 }
 
 static void
-PACWarningReporter(JSContext* aCx, const char* aMessage, JSErrorReport* aReport)
+PACWarningReporter(JSContext* aCx, JSErrorReport* aReport)
 {
   MOZ_ASSERT(aReport);
   MOZ_ASSERT(JSREPORT_IS_WARNING(aReport->flags));
 
   PACLogErrorOrWarning(NS_LITERAL_STRING("Warning"), aReport);
 }
 
 class MOZ_STACK_CLASS AutoPACErrorReporter
--- a/xpcom/base/CycleCollectedJSContext.cpp
+++ b/xpcom/base/CycleCollectedJSContext.cpp
@@ -481,17 +481,17 @@ CycleCollectedJSContext::~CycleCollected
 
   mozilla::dom::DestroyScriptSettings();
 
   mOwningThread->SetScriptObserver(nullptr);
   NS_RELEASE(mOwningThread);
 }
 
 static void
-MozCrashWarningReporter(JSContext*, const char*, JSErrorReport*)
+MozCrashWarningReporter(JSContext*, JSErrorReport*)
 {
   MOZ_CRASH("Why is someone touching JSAPI without an AutoJSAPI?");
 }
 
 nsresult
 CycleCollectedJSContext::Initialize(JSContext* aParentContext,
                                     uint32_t aMaxBytes,
                                     uint32_t aMaxNurseryBytes)