tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ErrorReporting.cpp (21318B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "vm/ErrorReporting.h"
      8 
      9 #include "mozilla/PodOperations.h"
     10 
     11 #include <stdarg.h>
     12 #include <utility>
     13 
     14 #include "jsexn.h"
     15 #include "jsfriendapi.h"
     16 
     17 #include "frontend/FrontendContext.h"  // AutoReportFrontendContext
     18 #include "js/CharacterEncoding.h"      // JS::ConstUTF8CharsZ
     19 #include "js/ColumnNumber.h"  // JS::ColumnNumberOneOrigin, JS::TaggedColumnNumberOneOrigin
     20 #include "js/ErrorReport.h"           // JSErrorBase
     21 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     22 #include "js/Printf.h"                // JS_vsmprintf
     23 #include "js/Warnings.h"              // JS::WarningReporter
     24 #include "vm/FrameIter.h"
     25 #include "vm/GlobalObject.h"
     26 #include "vm/JSContext.h"
     27 
     28 using namespace js;
     29 
     30 using JS::HandleObject;
     31 using JS::HandleValue;
     32 using JS::UniqueTwoByteChars;
     33 
     34 void js::CallWarningReporter(JSContext* cx, JSErrorReport* reportp) {
     35  MOZ_ASSERT(reportp->isWarning());
     36 
     37  if (JS::WarningReporter warningReporter = cx->runtime()->warningReporter) {
     38    warningReporter(cx, reportp);
     39  }
     40 }
     41 
     42 bool js::CompileError::throwError(JSContext* cx) {
     43  if (isWarning()) {
     44    CallWarningReporter(cx, this);
     45    return true;
     46  }
     47 
     48  // If there's a runtime exception type associated with this error
     49  // number, set that as the pending exception.  For errors occurring at
     50  // compile time, this is very likely to be a JSEXN_SYNTAXERR.
     51  return ErrorToException(cx, this, nullptr, nullptr);
     52 }
     53 
     54 bool js::ReportExceptionClosure::operator()(JSContext* cx) {
     55  cx->setPendingException(exn_, ShouldCaptureStack::Always);
     56  return false;
     57 }
     58 
     59 bool js::ReportCompileWarning(FrontendContext* fc, ErrorMetadata&& metadata,
     60                              UniquePtr<JSErrorNotes> notes,
     61                              unsigned errorNumber, va_list* args) {
     62  // On the main thread, report the error immediately. When compiling off
     63  // thread, save the error so that the thread finishing the parse can report
     64  // it later.
     65  CompileError err;
     66 
     67  err.notes = std::move(notes);
     68  err.isWarning_ = true;
     69  err.errorNumber = errorNumber;
     70 
     71  err.filename = JS::ConstUTF8CharsZ(metadata.filename);
     72  err.lineno = metadata.lineNumber;
     73  err.column = metadata.columnNumber;
     74  err.isMuted = metadata.isMuted;
     75 
     76  if (UniqueTwoByteChars lineOfContext = std::move(metadata.lineOfContext)) {
     77    err.initOwnedLinebuf(lineOfContext.release(), metadata.lineLength,
     78                         metadata.tokenOffset);
     79  }
     80 
     81  if (!ExpandErrorArgumentsVA(fc, GetErrorMessage, nullptr, errorNumber,
     82                              ArgumentsAreLatin1, &err, *args)) {
     83    return false;
     84  }
     85 
     86  return fc->reportWarning(std::move(err));
     87 }
     88 
     89 static void ReportCompileErrorImpl(FrontendContext* fc,
     90                                   js::ErrorMetadata&& metadata,
     91                                   js::UniquePtr<JSErrorNotes> notes,
     92                                   unsigned errorNumber, va_list* args,
     93                                   ErrorArgumentsType argumentsType) {
     94  js::CompileError err;
     95 
     96  err.notes = std::move(notes);
     97  err.isWarning_ = false;
     98  err.errorNumber = errorNumber;
     99 
    100  err.filename = JS::ConstUTF8CharsZ(metadata.filename);
    101  err.lineno = metadata.lineNumber;
    102  err.column = metadata.columnNumber;
    103  err.isMuted = metadata.isMuted;
    104 
    105  if (UniqueTwoByteChars lineOfContext = std::move(metadata.lineOfContext)) {
    106    err.initOwnedLinebuf(lineOfContext.release(), metadata.lineLength,
    107                         metadata.tokenOffset);
    108  }
    109 
    110  if (!js::ExpandErrorArgumentsVA(fc, js::GetErrorMessage, nullptr, errorNumber,
    111                                  argumentsType, &err, *args)) {
    112    return;
    113  }
    114 
    115  fc->reportError(std::move(err));
    116 }
    117 
    118 void js::ReportCompileErrorLatin1(FrontendContext* fc, ErrorMetadata&& metadata,
    119                                  UniquePtr<JSErrorNotes> notes,
    120                                  unsigned errorNumber, ...) {
    121  va_list args;
    122  va_start(args, errorNumber);
    123  ReportCompileErrorLatin1VA(fc, std::move(metadata), std::move(notes),
    124                             errorNumber, &args);
    125  va_end(args);
    126 }
    127 
    128 void js::ReportCompileErrorUTF8(FrontendContext* fc, ErrorMetadata&& metadata,
    129                                UniquePtr<JSErrorNotes> notes,
    130                                unsigned errorNumber, ...) {
    131  va_list args;
    132  va_start(args, errorNumber);
    133  ReportCompileErrorUTF8VA(fc, std::move(metadata), std::move(notes),
    134                           errorNumber, &args);
    135  va_end(args);
    136 }
    137 
    138 void js::ReportCompileErrorLatin1VA(FrontendContext* fc,
    139                                    ErrorMetadata&& metadata,
    140                                    UniquePtr<JSErrorNotes> notes,
    141                                    unsigned errorNumber, va_list* args) {
    142  ReportCompileErrorImpl(fc, std::move(metadata), std::move(notes), errorNumber,
    143                         args, ArgumentsAreLatin1);
    144 }
    145 
    146 void js::ReportCompileErrorUTF8VA(FrontendContext* fc, ErrorMetadata&& metadata,
    147                                  UniquePtr<JSErrorNotes> notes,
    148                                  unsigned errorNumber, va_list* args) {
    149  ReportCompileErrorImpl(fc, std::move(metadata), std::move(notes), errorNumber,
    150                         args, ArgumentsAreUTF8);
    151 }
    152 
    153 void js::ReportErrorToGlobal(JSContext* cx, Handle<GlobalObject*> global,
    154                             HandleValue error) {
    155  MOZ_ASSERT(!cx->isExceptionPending());
    156 #ifdef DEBUG
    157  // No assertSameCompartment version that doesn't take JSContext...
    158  if (error.isObject()) {
    159    AssertSameCompartment(global, &error.toObject());
    160  }
    161 #endif  // DEBUG
    162  js::ReportExceptionClosure report(error);
    163  PrepareScriptEnvironmentAndInvoke(cx, global, report);
    164 }
    165 
    166 static bool ReportError(JSContext* cx, JSErrorReport* reportp,
    167                        JSErrorCallback callback, void* userRef) {
    168  if (reportp->isWarning()) {
    169    CallWarningReporter(cx, reportp);
    170    return true;
    171  }
    172 
    173  // Check the error report, and set a JavaScript-catchable exception
    174  // if the error is defined to have an associated exception.
    175  return ErrorToException(cx, reportp, callback, userRef);
    176 }
    177 
    178 /*
    179 * The given JSErrorReport object have been zeroed and must not outlive
    180 * cx->fp() (otherwise owned fields may become invalid).
    181 */
    182 static void PopulateReportBlame(JSContext* cx, JSErrorReport* report) {
    183  JS::Realm* realm = cx->realm();
    184  if (!realm) {
    185    return;
    186  }
    187 
    188  /*
    189   * Walk stack until we find a frame that is associated with a non-builtin
    190   * rather than a builtin frame and which we're allowed to know about.
    191   */
    192  NonBuiltinFrameIter iter(cx, realm->principals());
    193  if (iter.done()) {
    194    return;
    195  }
    196 
    197  report->filename = JS::ConstUTF8CharsZ(iter.filename());
    198  if (iter.hasScript()) {
    199    report->sourceId = iter.script()->scriptSource()->id();
    200  }
    201  JS::TaggedColumnNumberOneOrigin column;
    202  report->lineno = iter.computeLine(&column);
    203  report->column = JS::ColumnNumberOneOrigin(column.oneOriginValue());
    204  report->isMuted = iter.mutedErrors();
    205 }
    206 
    207 class MOZ_RAII AutoMessageArgs {
    208  size_t totalLength_;
    209  /* only {0} thru {9} supported */
    210  mozilla::Array<const char*, JS::MaxNumErrorArguments> args_;
    211  mozilla::Array<size_t, JS::MaxNumErrorArguments> lengths_;
    212  uint16_t count_;
    213  bool allocatedElements_ : 1;
    214 
    215 public:
    216  AutoMessageArgs() : totalLength_(0), count_(0), allocatedElements_(false) {
    217    PodArrayZero(args_);
    218  }
    219 
    220  ~AutoMessageArgs() {
    221    /* free the arguments only if we allocated them */
    222    if (allocatedElements_) {
    223      uint16_t i = 0;
    224      while (i < count_) {
    225        if (args_[i]) {
    226          js_free((void*)args_[i]);
    227        }
    228        i++;
    229      }
    230    }
    231  }
    232 
    233  const char* args(size_t i) const {
    234    MOZ_ASSERT(i < count_);
    235    return args_[i];
    236  }
    237 
    238  size_t totalLength() const { return totalLength_; }
    239 
    240  size_t lengths(size_t i) const {
    241    MOZ_ASSERT(i < count_);
    242    return lengths_[i];
    243  }
    244 
    245  uint16_t count() const { return count_; }
    246 
    247  /* Gather the arguments into an array, and accumulate their sizes.
    248   *
    249   * We could template on the type of argsArg, but we're already trusting people
    250   * to do the right thing with varargs, so might as well trust them on this
    251   * part too.  Upstream consumers do assert that it's the right thing.  Also,
    252   * if argsArg were strongly typed we'd still need casting below for this to
    253   * compile, because typeArg is not known at compile-time here.
    254   */
    255  template <typename Allocator>
    256  bool init(Allocator* alloc, void* argsArg, uint16_t countArg,
    257            ErrorArgumentsType typeArg, va_list ap) {
    258    MOZ_ASSERT(countArg > 0);
    259 
    260    count_ = countArg;
    261 
    262    for (uint16_t i = 0; i < count_; i++) {
    263      switch (typeArg) {
    264        case ArgumentsAreASCII:
    265        case ArgumentsAreUTF8: {
    266          const char* c = argsArg ? static_cast<const char**>(argsArg)[i]
    267                                  : va_arg(ap, const char*);
    268          args_[i] = c;
    269          MOZ_ASSERT_IF(typeArg == ArgumentsAreASCII,
    270                        JS::StringIsASCII(args_[i]));
    271          lengths_[i] = strlen(args_[i]);
    272          break;
    273        }
    274        case ArgumentsAreLatin1: {
    275          MOZ_ASSERT(!argsArg);
    276          const Latin1Char* latin1 = va_arg(ap, Latin1Char*);
    277          size_t len = strlen(reinterpret_cast<const char*>(latin1));
    278          mozilla::Range<const Latin1Char> range(latin1, len);
    279          char* utf8 = JS::CharsToNewUTF8CharsZ(alloc, range).c_str();
    280          if (!utf8) {
    281            return false;
    282          }
    283 
    284          args_[i] = utf8;
    285          lengths_[i] = strlen(utf8);
    286          allocatedElements_ = true;
    287          break;
    288        }
    289        case ArgumentsAreUnicode: {
    290          const char16_t* uc = argsArg
    291                                   ? static_cast<const char16_t**>(argsArg)[i]
    292                                   : va_arg(ap, const char16_t*);
    293          size_t len = js_strlen(uc);
    294          mozilla::Range<const char16_t> range(uc, len);
    295          char* utf8 = JS::CharsToNewUTF8CharsZ(alloc, range).c_str();
    296          if (!utf8) {
    297            return false;
    298          }
    299 
    300          args_[i] = utf8;
    301          lengths_[i] = strlen(utf8);
    302          allocatedElements_ = true;
    303          break;
    304        }
    305      }
    306      totalLength_ += lengths_[i];
    307    }
    308    return true;
    309  }
    310 };
    311 
    312 /*
    313 * The arguments from ap need to be packaged up into an array and stored
    314 * into the report struct.
    315 *
    316 * The format string addressed by the error number may contain operands
    317 * identified by the format {N}, where N is a decimal digit. Each of these
    318 * is to be replaced by the Nth argument from the va_list. The complete
    319 * message is placed into reportp->message_.
    320 *
    321 * Returns true if the expansion succeeds (can fail if out of memory).
    322 *
    323 * messageArgs is a `const char**` or a `const char16_t**` but templating on
    324 * that is not worth it here because AutoMessageArgs takes a void* anyway, and
    325 * using void* here simplifies our callers a bit.
    326 */
    327 template <typename T>
    328 static bool ExpandErrorArgumentsHelper(FrontendContext* fc,
    329                                       JSErrorCallback callback, void* userRef,
    330                                       const unsigned errorNumber,
    331                                       void* messageArgs,
    332                                       ErrorArgumentsType argumentsType,
    333                                       T* reportp, va_list ap) {
    334  const JSErrorFormatString* efs;
    335 
    336  if (!callback) {
    337    callback = GetErrorMessage;
    338  }
    339 
    340  efs = fc->gcSafeCallback(callback, userRef, errorNumber);
    341 
    342  if (efs) {
    343    if constexpr (std::is_same_v<T, JSErrorReport>) {
    344      reportp->exnType = efs->exnType;
    345    }
    346 
    347    MOZ_ASSERT(reportp->errorNumber == errorNumber);
    348    reportp->errorMessageName = efs->name;
    349 
    350    MOZ_ASSERT_IF(argumentsType == ArgumentsAreASCII,
    351                  JS::StringIsASCII(efs->format));
    352 
    353    uint16_t argCount = efs->argCount;
    354    MOZ_RELEASE_ASSERT(argCount <= JS::MaxNumErrorArguments);
    355    if (argCount > 0) {
    356      /*
    357       * Parse the error format, substituting the argument X
    358       * for {X} in the format.
    359       */
    360      if (efs->format) {
    361        const char* fmt;
    362        char* out;
    363 #ifdef DEBUG
    364        int expandedArgs = 0;
    365 #endif
    366        size_t expandedLength;
    367        size_t len = strlen(efs->format);
    368 
    369        AutoMessageArgs args;
    370        if (!args.init(fc->getAllocator(), messageArgs, argCount, argumentsType,
    371                       ap)) {
    372          return false;
    373        }
    374 
    375        expandedLength = len - (3 * args.count()) /* exclude the {n} */
    376                         + args.totalLength();
    377 
    378        /*
    379         * Note - the above calculation assumes that each argument
    380         * is used once and only once in the expansion !!!
    381         */
    382        char* utf8 = out =
    383            fc->getAllocator()->pod_malloc<char>(expandedLength + 1);
    384        if (!out) {
    385          return false;
    386        }
    387 
    388        fmt = efs->format;
    389        while (*fmt) {
    390          if (*fmt == '{') {
    391            if (mozilla::IsAsciiDigit(fmt[1])) {
    392              int d = AsciiDigitToNumber(fmt[1]);
    393              MOZ_RELEASE_ASSERT(d < args.count());
    394              strncpy(out, args.args(d), args.lengths(d));
    395              out += args.lengths(d);
    396              fmt += 3;
    397 #ifdef DEBUG
    398              expandedArgs++;
    399 #endif
    400              continue;
    401            }
    402          }
    403          *out++ = *fmt++;
    404        }
    405        MOZ_ASSERT(expandedArgs == args.count());
    406        *out = 0;
    407 
    408        reportp->initOwnedMessage(utf8);
    409      }
    410    } else {
    411      /* Non-null messageArgs should have at least one non-null arg. */
    412      MOZ_ASSERT(!messageArgs);
    413      /*
    414       * Zero arguments: the format string (if it exists) is the
    415       * entire message.
    416       */
    417      if (efs->format) {
    418        reportp->initBorrowedMessage(efs->format);
    419      }
    420    }
    421  }
    422  if (!reportp->message()) {
    423    /* where's the right place for this ??? */
    424    const char* defaultErrorMessage =
    425        "No error message available for error number %d";
    426    size_t nbytes = strlen(defaultErrorMessage) + 16;
    427    char* message = fc->getAllocator()->pod_malloc<char>(nbytes);
    428    if (!message) {
    429      return false;
    430    }
    431    snprintf(message, nbytes, defaultErrorMessage, errorNumber);
    432    reportp->initOwnedMessage(message);
    433  }
    434  return true;
    435 }
    436 
    437 bool js::ExpandErrorArgumentsVA(FrontendContext* fc, JSErrorCallback callback,
    438                                void* userRef, const unsigned errorNumber,
    439                                const char16_t** messageArgs,
    440                                ErrorArgumentsType argumentsType,
    441                                JSErrorReport* reportp, va_list ap) {
    442  MOZ_ASSERT(argumentsType == ArgumentsAreUnicode);
    443  return ExpandErrorArgumentsHelper(fc, callback, userRef, errorNumber,
    444                                    messageArgs, argumentsType, reportp, ap);
    445 }
    446 
    447 bool js::ExpandErrorArgumentsVA(FrontendContext* fc, JSErrorCallback callback,
    448                                void* userRef, const unsigned errorNumber,
    449                                const char** messageArgs,
    450                                ErrorArgumentsType argumentsType,
    451                                JSErrorReport* reportp, va_list ap) {
    452  MOZ_ASSERT(argumentsType != ArgumentsAreUnicode);
    453  return ExpandErrorArgumentsHelper(fc, callback, userRef, errorNumber,
    454                                    messageArgs, argumentsType, reportp, ap);
    455 }
    456 
    457 bool js::ExpandErrorArgumentsVA(FrontendContext* fc, JSErrorCallback callback,
    458                                void* userRef, const unsigned errorNumber,
    459                                ErrorArgumentsType argumentsType,
    460                                JSErrorReport* reportp, va_list ap) {
    461  return ExpandErrorArgumentsHelper(fc, callback, userRef, errorNumber, nullptr,
    462                                    argumentsType, reportp, ap);
    463 }
    464 
    465 bool js::ExpandErrorArgumentsVA(FrontendContext* fc, JSErrorCallback callback,
    466                                void* userRef, const unsigned errorNumber,
    467                                const char16_t** messageArgs,
    468                                ErrorArgumentsType argumentsType,
    469                                JSErrorNotes::Note* notep, va_list ap) {
    470  return ExpandErrorArgumentsHelper(fc, callback, userRef, errorNumber,
    471                                    messageArgs, argumentsType, notep, ap);
    472 }
    473 
    474 bool js::ReportErrorNumberVA(JSContext* cx, IsWarning isWarning,
    475                             JSErrorCallback callback, void* userRef,
    476                             const unsigned errorNumber,
    477                             ErrorArgumentsType argumentsType, va_list ap) {
    478  JSErrorReport report;
    479  report.isWarning_ = isWarning == IsWarning::Yes;
    480  report.errorNumber = errorNumber;
    481  PopulateReportBlame(cx, &report);
    482 
    483  AutoReportFrontendContext fc(cx);
    484  if (!ExpandErrorArgumentsVA(&fc, callback, userRef, errorNumber,
    485                              argumentsType, &report, ap)) {
    486    return false;
    487  }
    488 
    489  if (!ReportError(cx, &report, callback, userRef)) {
    490    return false;
    491  }
    492 
    493  return report.isWarning();
    494 }
    495 
    496 template <typename CharT>
    497 static bool ExpandErrorArguments(FrontendContext* fc, JSErrorCallback callback,
    498                                 void* userRef, const unsigned errorNumber,
    499                                 const CharT** messageArgs,
    500                                 js::ErrorArgumentsType argumentsType,
    501                                 JSErrorReport* reportp, ...) {
    502  va_list ap;
    503  va_start(ap, reportp);
    504  bool expanded =
    505      js::ExpandErrorArgumentsVA(fc, callback, userRef, errorNumber,
    506                                 messageArgs, argumentsType, reportp, ap);
    507  va_end(ap);
    508  return expanded;
    509 }
    510 
    511 template <js::ErrorArgumentsType argType, typename CharT>
    512 static bool ReportErrorNumberArray(JSContext* cx, IsWarning isWarning,
    513                                   JSErrorCallback callback, void* userRef,
    514                                   const unsigned errorNumber,
    515                                   const CharT** args) {
    516  static_assert(
    517      (argType == ArgumentsAreUnicode && std::is_same_v<CharT, char16_t>) ||
    518          (argType != ArgumentsAreUnicode && std::is_same_v<CharT, char>),
    519      "Mismatch between character type and argument type");
    520 
    521  JSErrorReport report;
    522  report.isWarning_ = isWarning == IsWarning::Yes;
    523  report.errorNumber = errorNumber;
    524  PopulateReportBlame(cx, &report);
    525 
    526  AutoReportFrontendContext fc(cx);
    527  if (!ExpandErrorArguments(&fc, callback, userRef, errorNumber, args, argType,
    528                            &report)) {
    529    return false;
    530  }
    531 
    532  if (!ReportError(cx, &report, callback, userRef)) {
    533    return false;
    534  }
    535 
    536  return report.isWarning();
    537 }
    538 
    539 bool js::ReportErrorNumberUCArray(JSContext* cx, IsWarning isWarning,
    540                                  JSErrorCallback callback, void* userRef,
    541                                  const unsigned errorNumber,
    542                                  const char16_t** args) {
    543  return ReportErrorNumberArray<ArgumentsAreUnicode>(
    544      cx, isWarning, callback, userRef, errorNumber, args);
    545 }
    546 
    547 bool js::ReportErrorNumberUTF8Array(JSContext* cx, IsWarning isWarning,
    548                                    JSErrorCallback callback, void* userRef,
    549                                    const unsigned errorNumber,
    550                                    const char** args) {
    551  return ReportErrorNumberArray<ArgumentsAreUTF8>(cx, isWarning, callback,
    552                                                  userRef, errorNumber, args);
    553 }
    554 
    555 bool js::ReportErrorVA(JSContext* cx, IsWarning isWarning, const char* format,
    556                       js::ErrorArgumentsType argumentsType, va_list ap) {
    557  JSErrorReport report;
    558 
    559  UniqueChars message(JS_vsmprintf(format, ap));
    560  if (!message) {
    561    ReportOutOfMemory(cx);
    562    return false;
    563  }
    564 
    565  MOZ_ASSERT_IF(argumentsType == ArgumentsAreASCII,
    566                JS::StringIsASCII(message.get()));
    567 
    568  report.isWarning_ = isWarning == IsWarning::Yes;
    569  report.errorNumber = JSMSG_USER_DEFINED_ERROR;
    570  if (argumentsType == ArgumentsAreASCII || argumentsType == ArgumentsAreUTF8) {
    571    report.initOwnedMessage(message.release());
    572  } else {
    573    MOZ_ASSERT(argumentsType == ArgumentsAreLatin1);
    574    JS::Latin1Chars latin1(message.get(), strlen(message.get()));
    575    JS::UTF8CharsZ utf8(JS::CharsToNewUTF8CharsZ(cx, latin1));
    576    if (!utf8) {
    577      return false;
    578    }
    579    report.initOwnedMessage(reinterpret_cast<const char*>(utf8.get()));
    580  }
    581  PopulateReportBlame(cx, &report);
    582 
    583  if (!ReportError(cx, &report, nullptr, nullptr)) {
    584    return false;
    585  }
    586 
    587  return report.isWarning();
    588 }
    589 
    590 void js::MaybePrintAndClearPendingException(JSContext* cx) {
    591  if (!cx->isExceptionPending()) {
    592    return;
    593  }
    594 
    595  AutoClearPendingException acpe(cx);
    596 
    597  JS::ExceptionStack exnStack(cx);
    598  if (!JS::StealPendingExceptionStack(cx, &exnStack)) {
    599    fprintf(stderr, "error getting pending exception\n");
    600    return;
    601  }
    602 
    603  JS::ErrorReportBuilder report(cx);
    604  if (!report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) {
    605    fprintf(stderr, "out of memory initializing JS::ErrorReportBuilder\n");
    606    return;
    607  }
    608 
    609  MOZ_ASSERT(!report.report()->isWarning());
    610  JS::PrintError(stderr, report, true);
    611 }