Bug 1290422 - Part 1: Remove JSErrorReport.messageArgs. r=jwalden
authorTooru Fujisawa <arai_a@mac.com>
Wed, 03 Aug 2016 18:09:42 +0900
changeset 307844 b4cb1f015845f5b5e3aad0154add1929fad0a883
parent 307843 50c2b9e5f491bc89186235e52c6c95a1d495df11
child 307845 c39c24ce6b51f17740cb827ca3df77b66e43a7a7
push id80199
push userarai_a@mac.com
push dateWed, 03 Aug 2016 09:10:03 +0000
treeherdermozilla-inbound@c39c24ce6b51 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs1290422
milestone51.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 1290422 - Part 1: Remove JSErrorReport.messageArgs. r=jwalden
js/src/frontend/TokenStream.cpp
js/src/jsapi-tests/testParseJSON.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsexn.cpp
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -588,23 +588,16 @@ CompileError::throwError(JSContext* cx)
 
 CompileError::~CompileError()
 {
     js_free((void*)report.linebuf());
     js_free((void*)report.ucmessage);
     js_free(message);
     message = nullptr;
 
-    if (report.messageArgs) {
-        unsigned i = 0;
-        while (report.messageArgs[i])
-            js_free((void*)report.messageArgs[i++]);
-        js_free(report.messageArgs);
-    }
-
     PodZero(&report);
 }
 
 bool
 TokenStream::reportCompileErrorNumberVA(uint32_t offset, unsigned flags, unsigned errorNumber,
                                         va_list args)
 {
     bool warning = JSREPORT_IS_WARNING(flags);
@@ -643,17 +636,17 @@ TokenStream::reportCompileErrorNumberVA(
         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,
-                                ArgumentsAreASCII, &err.report, args))
+                                nullptr, ArgumentsAreASCII, &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
     // T's (starting) line for context.
--- a/js/src/jsapi-tests/testParseJSON.cpp
+++ b/js/src/jsapi-tests/testParseJSON.cpp
@@ -1,17 +1,19 @@
 /* -*- 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 <limits>
+#include <string.h>
 
+#include "jsprf.h"
 #include "jsstr.h"
 
 #include "jsapi-tests/tests.h"
 
 using namespace js;
 
 class AutoInflatedString {
     JSContext * const cx;
@@ -26,16 +28,23 @@ class AutoInflatedString {
 
     template<size_t N> void operator=(const char (&str)[N]) {
         length_ = N - 1;
         chars_ = InflateString(cx, str, &length_);
         if (!chars_)
             abort();
     }
 
+    void operator=(const char* str) {
+        length_ = strlen(str);
+        chars_ = InflateString(cx, str, &length_);
+        if (!chars_)
+            abort();
+    }
+
     const char16_t* chars() const { return chars_; }
     size_t length() const { return length_; }
 };
 
 BEGIN_TEST(testParseJSON_success)
 {
     // Primitives
     JS::RootedValue expected(cx);
@@ -165,147 +174,146 @@ TryParse(JSContext* cx, const char (&inp
     CHECK(JS_ParseJSON(cx, str.chars(), str.length(), &v));
     CHECK_SAME(v, expected);
     return true;
 }
 END_TEST(testParseJSON_success)
 
 BEGIN_TEST(testParseJSON_error)
 {
-    CHECK(Error(cx, ""                                  , "1", "1"));
-    CHECK(Error(cx, "\n"                                , "2", "1"));
-    CHECK(Error(cx, "\r"                                , "2", "1"));
-    CHECK(Error(cx, "\r\n"                              , "2", "1"));
+    CHECK(Error(cx, ""                                  , 1, 1));
+    CHECK(Error(cx, "\n"                                , 2, 1));
+    CHECK(Error(cx, "\r"                                , 2, 1));
+    CHECK(Error(cx, "\r\n"                              , 2, 1));
 
-    CHECK(Error(cx, "["                                 , "1", "2"));
-    CHECK(Error(cx, "[,]"                               , "1", "2"));
-    CHECK(Error(cx, "[1,]"                              , "1", "4"));
-    CHECK(Error(cx, "{a:2}"                             , "1", "2"));
-    CHECK(Error(cx, "{\"a\":2,}"                        , "1", "8"));
-    CHECK(Error(cx, "]"                                 , "1", "1"));
-    CHECK(Error(cx, "\""                                , "1", "2"));
-    CHECK(Error(cx, "{]"                                , "1", "2"));
-    CHECK(Error(cx, "[}"                                , "1", "2"));
-    CHECK(Error(cx, "'wrongly-quoted string'"           , "1", "1"));
+    CHECK(Error(cx, "["                                 , 1, 2));
+    CHECK(Error(cx, "[,]"                               , 1, 2));
+    CHECK(Error(cx, "[1,]"                              , 1, 4));
+    CHECK(Error(cx, "{a:2}"                             , 1, 2));
+    CHECK(Error(cx, "{\"a\":2,}"                        , 1, 8));
+    CHECK(Error(cx, "]"                                 , 1, 1));
+    CHECK(Error(cx, "\""                                , 1, 2));
+    CHECK(Error(cx, "{]"                                , 1, 2));
+    CHECK(Error(cx, "[}"                                , 1, 2));
+    CHECK(Error(cx, "'wrongly-quoted string'"           , 1, 1));
 
-    CHECK(Error(cx, "{\"a\":2 \n b:3}"                  , "2", "2"));
-    CHECK(Error(cx, "\n["                               , "2", "2"));
-    CHECK(Error(cx, "\n[,]"                             , "2", "2"));
-    CHECK(Error(cx, "\n[1,]"                            , "2", "4"));
-    CHECK(Error(cx, "\n{a:2}"                           , "2", "2"));
-    CHECK(Error(cx, "\n{\"a\":2,}"                      , "2", "8"));
-    CHECK(Error(cx, "\n]"                               , "2", "1"));
-    CHECK(Error(cx, "\"bad string\n\""                  , "1", "12"));
-    CHECK(Error(cx, "\r'wrongly-quoted string'"         , "2", "1"));
-    CHECK(Error(cx, "\n\""                              , "2", "2"));
-    CHECK(Error(cx, "\n{]"                              , "2", "2"));
-    CHECK(Error(cx, "\n[}"                              , "2", "2"));
-    CHECK(Error(cx, "{\"a\":[2,3],\n\"b\":,5,6}"        , "2", "5"));
+    CHECK(Error(cx, "{\"a\":2 \n b:3}"                  , 2, 2));
+    CHECK(Error(cx, "\n["                               , 2, 2));
+    CHECK(Error(cx, "\n[,]"                             , 2, 2));
+    CHECK(Error(cx, "\n[1,]"                            , 2, 4));
+    CHECK(Error(cx, "\n{a:2}"                           , 2, 2));
+    CHECK(Error(cx, "\n{\"a\":2,}"                      , 2, 8));
+    CHECK(Error(cx, "\n]"                               , 2, 1));
+    CHECK(Error(cx, "\"bad string\n\""                  , 1, 12));
+    CHECK(Error(cx, "\r'wrongly-quoted string'"         , 2, 1));
+    CHECK(Error(cx, "\n\""                              , 2, 2));
+    CHECK(Error(cx, "\n{]"                              , 2, 2));
+    CHECK(Error(cx, "\n[}"                              , 2, 2));
+    CHECK(Error(cx, "{\"a\":[2,3],\n\"b\":,5,6}"        , 2, 5));
 
-    CHECK(Error(cx, "{\"a\":2 \r b:3}"                  , "2", "2"));
-    CHECK(Error(cx, "\r["                               , "2", "2"));
-    CHECK(Error(cx, "\r[,]"                             , "2", "2"));
-    CHECK(Error(cx, "\r[1,]"                            , "2", "4"));
-    CHECK(Error(cx, "\r{a:2}"                           , "2", "2"));
-    CHECK(Error(cx, "\r{\"a\":2,}"                      , "2", "8"));
-    CHECK(Error(cx, "\r]"                               , "2", "1"));
-    CHECK(Error(cx, "\"bad string\r\""                  , "1", "12"));
-    CHECK(Error(cx, "\r'wrongly-quoted string'"       , "2", "1"));
-    CHECK(Error(cx, "\r\""                              , "2", "2"));
-    CHECK(Error(cx, "\r{]"                              , "2", "2"));
-    CHECK(Error(cx, "\r[}"                              , "2", "2"));
-    CHECK(Error(cx, "{\"a\":[2,3],\r\"b\":,5,6}"        , "2", "5"));
+    CHECK(Error(cx, "{\"a\":2 \r b:3}"                  , 2, 2));
+    CHECK(Error(cx, "\r["                               , 2, 2));
+    CHECK(Error(cx, "\r[,]"                             , 2, 2));
+    CHECK(Error(cx, "\r[1,]"                            , 2, 4));
+    CHECK(Error(cx, "\r{a:2}"                           , 2, 2));
+    CHECK(Error(cx, "\r{\"a\":2,}"                      , 2, 8));
+    CHECK(Error(cx, "\r]"                               , 2, 1));
+    CHECK(Error(cx, "\"bad string\r\""                  , 1, 12));
+    CHECK(Error(cx, "\r'wrongly-quoted string'"         , 2, 1));
+    CHECK(Error(cx, "\r\""                              , 2, 2));
+    CHECK(Error(cx, "\r{]"                              , 2, 2));
+    CHECK(Error(cx, "\r[}"                              , 2, 2));
+    CHECK(Error(cx, "{\"a\":[2,3],\r\"b\":,5,6}"        , 2, 5));
 
-    CHECK(Error(cx, "{\"a\":2 \r\n b:3}"                , "2", "2"));
-    CHECK(Error(cx, "\r\n["                             , "2", "2"));
-    CHECK(Error(cx, "\r\n[,]"                           , "2", "2"));
-    CHECK(Error(cx, "\r\n[1,]"                          , "2", "4"));
-    CHECK(Error(cx, "\r\n{a:2}"                         , "2", "2"));
-    CHECK(Error(cx, "\r\n{\"a\":2,}"                    , "2", "8"));
-    CHECK(Error(cx, "\r\n]"                             , "2", "1"));
-    CHECK(Error(cx, "\"bad string\r\n\""                , "1", "12"));
-    CHECK(Error(cx, "\r\n'wrongly-quoted string'"       , "2", "1"));
-    CHECK(Error(cx, "\r\n\""                            , "2", "2"));
-    CHECK(Error(cx, "\r\n{]"                            , "2", "2"));
-    CHECK(Error(cx, "\r\n[}"                            , "2", "2"));
-    CHECK(Error(cx, "{\"a\":[2,3],\r\n\"b\":,5,6}"      , "2", "5"));
+    CHECK(Error(cx, "{\"a\":2 \r\n b:3}"                , 2, 2));
+    CHECK(Error(cx, "\r\n["                             , 2, 2));
+    CHECK(Error(cx, "\r\n[,]"                           , 2, 2));
+    CHECK(Error(cx, "\r\n[1,]"                          , 2, 4));
+    CHECK(Error(cx, "\r\n{a:2}"                         , 2, 2));
+    CHECK(Error(cx, "\r\n{\"a\":2,}"                    , 2, 8));
+    CHECK(Error(cx, "\r\n]"                             , 2, 1));
+    CHECK(Error(cx, "\"bad string\r\n\""                , 1, 12));
+    CHECK(Error(cx, "\r\n'wrongly-quoted string'"       , 2, 1));
+    CHECK(Error(cx, "\r\n\""                            , 2, 2));
+    CHECK(Error(cx, "\r\n{]"                            , 2, 2));
+    CHECK(Error(cx, "\r\n[}"                            , 2, 2));
+    CHECK(Error(cx, "{\"a\":[2,3],\r\n\"b\":,5,6}"      , 2, 5));
 
-    CHECK(Error(cx, "\n\"bad string\n\""                , "2", "12"));
-    CHECK(Error(cx, "\r\"bad string\r\""                , "2", "12"));
-    CHECK(Error(cx, "\r\n\"bad string\r\n\""            , "2", "12"));
+    CHECK(Error(cx, "\n\"bad string\n\""                , 2, 12));
+    CHECK(Error(cx, "\r\"bad string\r\""                , 2, 12));
+    CHECK(Error(cx, "\r\n\"bad string\r\n\""            , 2, 12));
 
-    CHECK(Error(cx, "{\n\"a\":[2,3],\r\"b\":,5,6}"      , "3", "5"));
-    CHECK(Error(cx, "{\r\"a\":[2,3],\n\"b\":,5,6}"      , "3", "5"));
-    CHECK(Error(cx, "[\"\\t\\q"                         , "1", "6"));
-    CHECK(Error(cx, "[\"\\t\x00"                        , "1", "5"));
-    CHECK(Error(cx, "[\"\\t\x01"                        , "1", "5"));
-    CHECK(Error(cx, "[\"\\t\\\x00"                      , "1", "6"));
-    CHECK(Error(cx, "[\"\\t\\\x01"                      , "1", "6"));
+    CHECK(Error(cx, "{\n\"a\":[2,3],\r\"b\":,5,6}"      , 3, 5));
+    CHECK(Error(cx, "{\r\"a\":[2,3],\n\"b\":,5,6}"      , 3, 5));
+    CHECK(Error(cx, "[\"\\t\\q"                         , 1, 6));
+    CHECK(Error(cx, "[\"\\t\x00"                        , 1, 5));
+    CHECK(Error(cx, "[\"\\t\x01"                        , 1, 5));
+    CHECK(Error(cx, "[\"\\t\\\x00"                      , 1, 6));
+    CHECK(Error(cx, "[\"\\t\\\x01"                      , 1, 6));
 
     // Unicode escape errors are messy.  The first bad character could be
     // non-hexadecimal, or it could be absent entirely.  Include tests where
     // there's a bad character, followed by zero to as many characters as are
     // needed to form a complete Unicode escape sequence, plus one.  (The extra
     // characters beyond are valuable because our implementation checks for
     // too-few subsequent characters first, before checking for subsequent
     // non-hexadecimal characters.  So \u<END>, \u0<END>, \u00<END>, and
     // \u000<END> are all *detected* as invalid by the same code path, but the
     // process of computing the first invalid character follows a different
     // code path for each.  And \uQQQQ, \u0QQQ, \u00QQ, and \u000Q are detected
     // as invalid by the same code path [ignoring which precise subexpression
     // triggers failure of a single condition], but the computation of the
     // first invalid character follows a different code path for each.)
-    CHECK(Error(cx, "[\"\\t\\u"                         , "1", "7"));
-    CHECK(Error(cx, "[\"\\t\\uZ"                        , "1", "7"));
-    CHECK(Error(cx, "[\"\\t\\uZZ"                       , "1", "7"));
-    CHECK(Error(cx, "[\"\\t\\uZZZ"                      , "1", "7"));
-    CHECK(Error(cx, "[\"\\t\\uZZZZ"                     , "1", "7"));
-    CHECK(Error(cx, "[\"\\t\\uZZZZZ"                    , "1", "7"));
+    CHECK(Error(cx, "[\"\\t\\u"                         , 1, 7));
+    CHECK(Error(cx, "[\"\\t\\uZ"                        , 1, 7));
+    CHECK(Error(cx, "[\"\\t\\uZZ"                       , 1, 7));
+    CHECK(Error(cx, "[\"\\t\\uZZZ"                      , 1, 7));
+    CHECK(Error(cx, "[\"\\t\\uZZZZ"                     , 1, 7));
+    CHECK(Error(cx, "[\"\\t\\uZZZZZ"                    , 1, 7));
 
-    CHECK(Error(cx, "[\"\\t\\u0"                        , "1", "8"));
-    CHECK(Error(cx, "[\"\\t\\u0Z"                       , "1", "8"));
-    CHECK(Error(cx, "[\"\\t\\u0ZZ"                      , "1", "8"));
-    CHECK(Error(cx, "[\"\\t\\u0ZZZ"                     , "1", "8"));
-    CHECK(Error(cx, "[\"\\t\\u0ZZZZ"                    , "1", "8"));
+    CHECK(Error(cx, "[\"\\t\\u0"                        , 1, 8));
+    CHECK(Error(cx, "[\"\\t\\u0Z"                       , 1, 8));
+    CHECK(Error(cx, "[\"\\t\\u0ZZ"                      , 1, 8));
+    CHECK(Error(cx, "[\"\\t\\u0ZZZ"                     , 1, 8));
+    CHECK(Error(cx, "[\"\\t\\u0ZZZZ"                    , 1, 8));
 
-    CHECK(Error(cx, "[\"\\t\\u00"                       , "1", "9"));
-    CHECK(Error(cx, "[\"\\t\\u00Z"                      , "1", "9"));
-    CHECK(Error(cx, "[\"\\t\\u00ZZ"                     , "1", "9"));
-    CHECK(Error(cx, "[\"\\t\\u00ZZZ"                    , "1", "9"));
+    CHECK(Error(cx, "[\"\\t\\u00"                       , 1, 9));
+    CHECK(Error(cx, "[\"\\t\\u00Z"                      , 1, 9));
+    CHECK(Error(cx, "[\"\\t\\u00ZZ"                     , 1, 9));
+    CHECK(Error(cx, "[\"\\t\\u00ZZZ"                    , 1, 9));
 
-    CHECK(Error(cx, "[\"\\t\\u000"                      , "1", "10"));
-    CHECK(Error(cx, "[\"\\t\\u000Z"                     , "1", "10"));
-    CHECK(Error(cx, "[\"\\t\\u000ZZ"                    , "1", "10"));
+    CHECK(Error(cx, "[\"\\t\\u000"                      , 1, 10));
+    CHECK(Error(cx, "[\"\\t\\u000Z"                     , 1, 10));
+    CHECK(Error(cx, "[\"\\t\\u000ZZ"                    , 1, 10));
 
     return true;
 }
 
-template<size_t N, size_t M, size_t L> inline bool
-Error(JSContext* cx, const char (&input)[N], const char (&expectedLine)[M],
-      const char (&expectedColumn)[L])
+template<size_t N> inline bool
+Error(JSContext* cx, const char (&input)[N], uint32_t expectedLine,
+      uint32_t expectedColumn)
 {
-    AutoInflatedString str(cx), line(cx), column(cx);
+    AutoInflatedString str(cx);
     RootedValue dummy(cx);
     str = input;
 
     bool ok = JS_ParseJSON(cx, str.chars(), str.length(), &dummy);
     CHECK(!ok);
 
     RootedValue exn(cx);
     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);
 
-    column = expectedColumn;
-    CHECK(js_strcmp(column.chars(), report.report()->messageArgs[2]) == 0);
-    line = expectedLine;
-    CHECK(js_strcmp(line.chars(), report.report()->messageArgs[1]) == 0);
+    const char* lineAndColumnASCII = JS_smprintf("line %d column %d", expectedLine, expectedColumn);
+    CHECK(strstr(report.message(), 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.h
+++ b/js/src/jsapi.h
@@ -5163,27 +5163,26 @@ class JSErrorReport
     // The 0-based offset of error token in linebuf_.
     size_t tokenOffset_;
 
   public:
     JSErrorReport()
       : linebuf_(nullptr), linebufLength_(0), tokenOffset_(0),
         filename(nullptr), lineno(0), column(0), isMuted(false),
         flags(0), errorNumber(0), ucmessage(nullptr),
-        messageArgs(nullptr), exnType(0)
+        exnType(0)
     {}
 
     const char*     filename;      /* source file name, URL, etc., or null */
     unsigned        lineno;         /* source line number */
     unsigned        column;         /* zero-based column index in line */
     bool            isMuted;        /* See the comment in ReadOnlyCompileOptions. */
     unsigned        flags;          /* error/warning, etc. */
     unsigned        errorNumber;    /* the error number, e.g. see js.msg */
     const char16_t* ucmessage;     /* the (default) error message */
-    const char16_t** messageArgs;  /* arguments for the error message */
     int16_t         exnType;        /* One of the JSExnType constants */
 
     const char16_t* linebuf() const {
         return linebuf_;
     }
     size_t linebufLength() const {
         return linebufLength_;
     }
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -454,36 +454,52 @@ js::PrintError(JSContext* cx, FILE* file
         fputc('^', file);
     }
     fputc('\n', file);
     fflush(file);
     JS_free(cx, prefix);
     return true;
 }
 
+static void
+FreeMessageArgs(const char16_t** messageArgs, ErrorArgumentsType argumentsType)
+{
+    if (!messageArgs)
+        return;
+
+    /* free the arguments only if we allocated them */
+    if (argumentsType == ArgumentsAreASCII) {
+        uint16_t i = 0;
+        while (messageArgs[i])
+            js_free((void*)messageArgs[i++]);
+    }
+    js_free(messageArgs);
+}
+
 /*
  * The arguments from ap need to be packaged up into an array and stored
  * into the report struct.
  *
  * The format string addressed by the error number may contain operands
  * identified by the format {N}, where N is a decimal digit. Each of these
  * is to be replaced by the Nth argument from the va_list. The complete
  * message is placed into reportp->ucmessage converted to a JSString.
  *
  * 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, ErrorArgumentsType argumentsType,
+                           char** messagep, const char16_t** messageArgs,
+                           ErrorArgumentsType argumentsType,
                            JSErrorReport* reportp, va_list ap)
 {
     const JSErrorFormatString* efs;
     uint16_t argCount;
-    bool messageArgsPassed = !!reportp->messageArgs;
+    bool messageArgsPassed = !!messageArgs;
 
     *messagep = nullptr;
 
     if (!callback)
         callback = GetErrorMessage;
 
     {
         AutoSuppressGC suppressGC(cx);
@@ -500,37 +516,36 @@ js::ExpandErrorArgumentsVA(ExclusiveCont
         if (argCount > 0) {
             /*
              * Gather the arguments into an array, and accumulate
              * their sizes. We allocate 1 more than necessary and
              * null it out to act as the caboose when we free the
              * pointers later.
              */
             if (messageArgsPassed) {
-                MOZ_ASSERT(!reportp->messageArgs[argCount]);
+                MOZ_ASSERT(!messageArgs[argCount]);
             } else {
-                reportp->messageArgs = cx->pod_malloc<const char16_t*>(argCount + 1);
-                if (!reportp->messageArgs)
+                messageArgs = cx->pod_malloc<const char16_t*>(argCount + 1);
+                if (!messageArgs)
                     return false;
-                /* nullptr-terminate for easy copying. */
-                reportp->messageArgs[argCount] = nullptr;
+                messageArgs[argCount] = nullptr;
             }
             for (uint16_t i = 0; i < argCount; i++) {
                 if (messageArgsPassed) {
                     /* Do nothing. */
                 } else if (argumentsType == ArgumentsAreASCII) {
                     char* charArg = va_arg(ap, char*);
                     size_t charArgLength = strlen(charArg);
-                    reportp->messageArgs[i] = InflateString(cx, charArg, &charArgLength);
-                    if (!reportp->messageArgs[i])
+                    messageArgs[i] = InflateString(cx, charArg, &charArgLength);
+                    if (!messageArgs[i])
                         goto error;
                 } else {
-                    reportp->messageArgs[i] = va_arg(ap, char16_t*);
+                    messageArgs[i] = va_arg(ap, char16_t*);
                 }
-                argLengths[i] = js_strlen(reportp->messageArgs[i]);
+                argLengths[i] = js_strlen(messageArgs[i]);
                 totalArgsLength += argLengths[i];
             }
         }
         /*
          * Parse the error format, substituting the argument X
          * for {X} in the format.
          */
         if (argCount > 0) {
@@ -558,18 +573,17 @@ js::ExpandErrorArgumentsVA(ExclusiveCont
                     js_free(buffer);
                     goto error;
                 }
                 while (*fmt) {
                     if (*fmt == '{') {
                         if (isdigit(fmt[1])) {
                             int d = JS7_UNDEC(fmt[1]);
                             MOZ_RELEASE_ASSERT(d < argCount);
-                            js_strncpy(out, reportp->messageArgs[d],
-                                       argLengths[d]);
+                            js_strncpy(out, messageArgs[d], argLengths[d]);
                             out += argLengths[d];
                             fmt += 3;
                             expandedArgs++;
                             continue;
                         }
                     }
                     *out++ = *fmt++;
                 }
@@ -580,17 +594,17 @@ js::ExpandErrorArgumentsVA(ExclusiveCont
                                                  static_cast<const char16_t*>(out));
                 mozilla::Range<const char16_t> ucmsg(reportp->ucmessage, msgLen);
                 *messagep = JS::LossyTwoByteCharsToNewLatin1CharsZ(cx, ucmsg).c_str();
                 if (!*messagep)
                     goto error;
             }
         } else {
             /* Non-null messageArgs should have at least one non-null arg. */
-            MOZ_ASSERT(!reportp->messageArgs);
+            MOZ_ASSERT(!messageArgsPassed);
             /*
              * Zero arguments: the format string (if it exists) is the
              * entire message.
              */
             if (efs->format) {
                 size_t len;
                 *messagep = DuplicateString(cx, efs->format).release();
                 if (!*messagep)
@@ -607,29 +621,23 @@ js::ExpandErrorArgumentsVA(ExclusiveCont
         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);
     }
+    if (!messageArgsPassed)
+        FreeMessageArgs(messageArgs, argumentsType);
     return true;
 
 error:
-    if (!messageArgsPassed && reportp->messageArgs) {
-        /* free the arguments only if we allocated them */
-        if (argumentsType == ArgumentsAreASCII) {
-            uint16_t i = 0;
-            while (reportp->messageArgs[i])
-                js_free((void*)reportp->messageArgs[i++]);
-        }
-        js_free((void*)reportp->messageArgs);
-        reportp->messageArgs = nullptr;
-    }
+    if (!messageArgsPassed)
+        FreeMessageArgs(messageArgs, argumentsType);
     if (reportp->ucmessage) {
         js_free((void*)reportp->ucmessage);
         reportp->ucmessage = nullptr;
     }
     if (*messagep) {
         js_free((void*)*messagep);
         *messagep = nullptr;
     }
@@ -649,50 +657,39 @@ js::ReportErrorNumberVA(JSContext* cx, u
         return true;
     warning = JSREPORT_IS_WARNING(flags);
 
     report.flags = flags;
     report.errorNumber = errorNumber;
     PopulateReportBlame(cx, &report);
 
     if (!ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
-                                &message, argumentsType, &report, ap)) {
+                                &message, nullptr, argumentsType, &report, ap)) {
         return false;
     }
 
     ReportError(cx, message, &report, callback, userRef);
 
     js_free(message);
-    if (report.messageArgs) {
-        /*
-         * ExpandErrorArgumentsVA owns its messageArgs only if it had to
-         * inflate the arguments (from regular |char*|s).
-         */
-        if (argumentsType == ArgumentsAreASCII) {
-            int i = 0;
-            while (report.messageArgs[i])
-                js_free((void*)report.messageArgs[i++]);
-        }
-        js_free((void*)report.messageArgs);
-    }
     js_free((void*)report.ucmessage);
 
     return warning;
 }
 
 static bool
 ExpandErrorArguments(ExclusiveContext* cx, JSErrorCallback callback,
                      void* userRef, const unsigned errorNumber,
-                     char** messagep, ErrorArgumentsType argumentsType,
+                     char** messagep, const char16_t** messageArgs,
+                     ErrorArgumentsType argumentsType,
                      JSErrorReport* reportp, ...)
 {
     va_list ap;
     va_start(ap, reportp);
     bool expanded = js::ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
-                                               messagep, argumentsType, reportp, ap);
+                                               messagep, 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)
@@ -700,21 +697,21 @@ js::ReportErrorNumberUCArray(JSContext* 
     if (checkReportFlags(cx, &flags))
         return true;
     bool warning = JSREPORT_IS_WARNING(flags);
 
     JSErrorReport report;
     report.flags = flags;
     report.errorNumber = errorNumber;
     PopulateReportBlame(cx, &report);
-    report.messageArgs = args;
 
     char* message;
     if (!ExpandErrorArguments(cx, callback, userRef, errorNumber,
-                              &message, ArgumentsAreUnicode, &report)) {
+                              &message, args, ArgumentsAreUnicode, &report))
+    {
         return false;
     }
 
     ReportError(cx, message, &report, callback, userRef);
 
     js_free(message);
     js_free((void*)report.ucmessage);
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -573,17 +573,18 @@ 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, ErrorArgumentsType argumentsType,
+                       char** message, const char16_t** messageArgs,
+                       ErrorArgumentsType argumentsType,
                        JSErrorReport* reportp, va_list ap);
 
 /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
 extern void
 ReportUsageError(JSContext* cx, HandleObject callee, const char* msg);
 
 /*
  * Prints a full report and returns true if the given report is non-nullptr
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -143,76 +143,47 @@ ErrorObject::classes[JSEXN_LIMIT] = {
 
 JSErrorReport*
 js::CopyErrorReport(JSContext* cx, JSErrorReport* report)
 {
     /*
      * We use a single malloc block to make a deep copy of JSErrorReport with
      * the following layout:
      *   JSErrorReport
-     *   array of copies of report->messageArgs
-     *   char16_t array with characters for all messageArgs
      *   char16_t array with characters for ucmessage
      *   char16_t array with characters for linebuf
      *   char array with characters for filename
      * Such layout together with the properties enforced by the following
      * asserts does not need any extra alignment padding.
      */
     JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char*) == 0);
     JS_STATIC_ASSERT(sizeof(const char*) % sizeof(char16_t) == 0);
 
 #define JS_CHARS_SIZE(chars) ((js_strlen(chars) + 1) * sizeof(char16_t))
 
     size_t filenameSize = report->filename ? strlen(report->filename) + 1 : 0;
     size_t linebufSize = 0;
     if (report->linebuf())
         linebufSize = (report->linebufLength() + 1) * sizeof(char16_t);
     size_t ucmessageSize = 0;
-    size_t argsArraySize = 0;
-    size_t argsCopySize = 0;
-    if (report->ucmessage) {
+    if (report->ucmessage)
         ucmessageSize = JS_CHARS_SIZE(report->ucmessage);
-        if (report->messageArgs) {
-            size_t i = 0;
-            for (; report->messageArgs[i]; ++i)
-                argsCopySize += JS_CHARS_SIZE(report->messageArgs[i]);
-
-            /* Non-null messageArgs should have at least one non-null arg. */
-            MOZ_ASSERT(i != 0);
-            argsArraySize = (i + 1) * sizeof(const char16_t*);
-        }
-    }
 
     /*
      * The mallocSize can not overflow since it represents the sum of the
      * sizes of already allocated objects.
      */
-    size_t mallocSize = sizeof(JSErrorReport) + argsArraySize + argsCopySize +
-                         ucmessageSize + linebufSize + filenameSize;
+    size_t mallocSize = sizeof(JSErrorReport) + ucmessageSize + linebufSize + filenameSize;
     uint8_t* cursor = cx->pod_calloc<uint8_t>(mallocSize);
     if (!cursor)
         return nullptr;
 
     JSErrorReport* copy = (JSErrorReport*)cursor;
     cursor += sizeof(JSErrorReport);
 
-    if (argsArraySize != 0) {
-        copy->messageArgs = (const char16_t**)cursor;
-        cursor += argsArraySize;
-        size_t i = 0;
-        for (; report->messageArgs[i]; ++i) {
-            copy->messageArgs[i] = (const char16_t*)cursor;
-            size_t argSize = JS_CHARS_SIZE(report->messageArgs[i]);
-            js_memcpy(cursor, report->messageArgs[i], argSize);
-            cursor += argSize;
-        }
-        copy->messageArgs[i] = nullptr;
-        MOZ_ASSERT(cursor == (uint8_t*)copy->messageArgs[0] + argsCopySize);
-    }
-
     if (report->ucmessage) {
         copy->ucmessage = (const char16_t*)cursor;
         js_memcpy(cursor, report->ucmessage, ucmessageSize);
         cursor += ucmessageSize;
     }
 
     if (report->linebuf()) {
         const char16_t* linebufCopy = (const char16_t*)cursor;
@@ -669,27 +640,16 @@ ErrorReport::ErrorReport(JSContext* cx)
 }
 
 ErrorReport::~ErrorReport()
 {
     if (!ownedMessage)
         return;
 
     js_free(ownedMessage);
-    if (ownedReport.messageArgs) {
-        /*
-         * ExpandErrorArgumentsVA owns its messageArgs only if it had to
-         * inflate the arguments (from regular |char*|s), which is always in
-         * our case.
-         */
-        size_t i = 0;
-        while (ownedReport.messageArgs[i])
-            js_free(const_cast<char16_t*>(ownedReport.messageArgs[i++]));
-        js_free(ownedReport.messageArgs);
-    }
     js_free(const_cast<char16_t*>(ownedReport.ucmessage));
 }
 
 void
 ErrorReport::ReportAddonExceptionToTelementry(JSContext* cx)
 {
     MOZ_ASSERT(exnObject);
     RootedObject unwrapped(cx, UncheckedUnwrap(exnObject));
@@ -940,17 +900,17 @@ ErrorReport::populateUncaughtExceptionRe
         // 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,
-                                ArgumentsAreASCII, &ownedReport, ap)) {
+                                nullptr, ArgumentsAreASCII, &ownedReport, ap)) {
         return false;
     }
 
     reportp = &ownedReport;
     message_ = ownedMessage;
     ownsMessageAndReport = true;
     return true;
 }