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