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