1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 * Error-reporting APIs.
9 * Despite the types and structures defined here existing in js/public,
10 * significant parts of their heritage date back to distant SpiderMonkey past,
11 * and they are not all universally well-thought-out as ideal,
12 * intended-to-be-permanent API. We may eventually replace this with something
13 * more consistent with ECMAScript the language and less consistent with
14 * '90s-era JSAPI inventions, but it's doubtful this will happen any time soon.
17 #ifndef js_ErrorReport_h
18 #define js_ErrorReport_h
20 #include "mozilla/Assertions.h" // MOZ_ASSERT
21 #include "mozilla/Maybe.h" // mozilla::Maybe
24 #include <iterator> // std::input_iterator_tag, std::iterator
26 #include <stddef.h> // size_t
27 #include <stdint.h> // int16_t, uint16_t
28 #include <string.h> // strlen
30 #include "jstypes.h" // JS_PUBLIC_API
32 #include "js/AllocPolicy.h"
33 #include "js/CharacterEncoding.h" // JS::ConstUTF8CharsZ
34 #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
35 #include "js/RootingAPI.h" // JS::HandleObject, JS::RootedObject
36 #include "js/UniquePtr.h" // js::UniquePtr
37 #include "js/Value.h" // JS::Value
38 #include "js/Vector.h" // js::Vector
40 struct JS_PUBLIC_API JSContext
;
41 class JS_PUBLIC_API JSString
;
47 class SystemAllocPolicy
;
49 enum ErrorArgumentsType
{
58 * Possible exception types. These types are part of a JSErrorFormatString
59 * structure. They define which error to throw in case of a runtime error.
61 * JSEXN_WARN is used for warnings, that are not strictly errors but are handled
62 * using the generalized error reporting mechanism. (One side effect of this
63 * type is to not prepend 'Error:' to warning messages.) This value can go away
64 * if we ever decide to use an entirely separate mechanism for warnings.
66 * The errors and warnings are arranged in alphabetically within their
67 * respective categories as defined in the comments below.
72 JSEXN_FIRST
= JSEXN_ERR
,
80 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
87 JSEXN_DEBUGGEEWOULDRUN
,
89 JSEXN_WASMCOMPILEERROR
,
91 JSEXN_WASMRUNTIMEERROR
,
94 JSEXN_WARN
= JSEXN_ERROR_LIMIT
,
100 struct JSErrorFormatString
{
101 /** The error message name in ASCII. */
104 /** The error format string in ASCII. */
107 /** The number of arguments to expand in the formatted error message. */
110 /** One of the JSExnType constants above. */
114 using JSErrorCallback
=
115 const JSErrorFormatString
* (*)(void* userRef
, const unsigned errorNumber
);
118 * Base class that implements parts shared by JSErrorReport and
119 * JSErrorNotes::Note.
123 // The (default) error message.
124 // If ownsMessage_ is true, the it is freed in destructor.
125 JS::ConstUTF8CharsZ message_
;
128 // Source file name, URL, etc., or null.
129 JS::ConstUTF8CharsZ filename
;
131 // Unique identifier for the script source.
134 // Source line number (1-origin).
137 // Column number in line in UTF-16 code units.
138 JS::ColumnNumberOneOrigin column
;
140 // the error number, e.g. see js/public/friend/ErrorNumbers.msg.
141 unsigned errorNumber
;
143 // Points to JSErrorFormatString::name.
144 // This string must never be freed.
145 const char* errorMessageName
;
148 bool ownsMessage_
: 1;
156 errorMessageName(nullptr),
157 ownsMessage_(false) {}
158 JSErrorBase(JSErrorBase
&& other
) noexcept
159 : message_(other
.message_
),
160 filename(other
.filename
),
161 sourceId(other
.sourceId
),
162 lineno(other
.lineno
),
163 column(other
.column
),
164 errorNumber(other
.errorNumber
),
165 errorMessageName(other
.errorMessageName
),
166 ownsMessage_(other
.ownsMessage_
) {
168 other
.ownsMessage_
= false;
172 ~JSErrorBase() { freeMessage(); }
175 const JS::ConstUTF8CharsZ
message() const { return message_
; }
177 void initOwnedMessage(const char* messageArg
) {
178 initBorrowedMessage(messageArg
);
181 void initBorrowedMessage(const char* messageArg
) {
182 MOZ_ASSERT(!message_
);
183 message_
= JS::ConstUTF8CharsZ(messageArg
, strlen(messageArg
));
186 JSString
* newMessageString(JSContext
* cx
);
193 * Notes associated with JSErrorReport.
197 class Note final
: public JSErrorBase
{};
200 // Stores pointers to each note.
201 js::Vector
<js::UniquePtr
<Note
>, 1, js::SystemAllocPolicy
> notes_
;
203 bool addNoteVA(js::FrontendContext
* fc
, const char* filename
,
204 unsigned sourceId
, uint32_t lineno
,
205 JS::ColumnNumberOneOrigin column
,
206 JSErrorCallback errorCallback
, void* userRef
,
207 const unsigned errorNumber
,
208 js::ErrorArgumentsType argumentsType
, va_list ap
);
214 // Add a note to the given position.
215 bool addNoteASCII(JSContext
* cx
, const char* filename
, unsigned sourceId
,
216 uint32_t lineno
, JS::ColumnNumberOneOrigin column
,
217 JSErrorCallback errorCallback
, void* userRef
,
218 const unsigned errorNumber
, ...);
219 bool addNoteASCII(js::FrontendContext
* fc
, const char* filename
,
220 unsigned sourceId
, uint32_t lineno
,
221 JS::ColumnNumberOneOrigin column
,
222 JSErrorCallback errorCallback
, void* userRef
,
223 const unsigned errorNumber
, ...);
224 bool addNoteLatin1(JSContext
* cx
, const char* filename
, unsigned sourceId
,
225 uint32_t lineno
, JS::ColumnNumberOneOrigin column
,
226 JSErrorCallback errorCallback
, void* userRef
,
227 const unsigned errorNumber
, ...);
228 bool addNoteLatin1(js::FrontendContext
* fc
, const char* filename
,
229 unsigned sourceId
, uint32_t lineno
,
230 JS::ColumnNumberOneOrigin column
,
231 JSErrorCallback errorCallback
, void* userRef
,
232 const unsigned errorNumber
, ...);
233 bool addNoteUTF8(JSContext
* cx
, const char* filename
, unsigned sourceId
,
234 uint32_t lineno
, JS::ColumnNumberOneOrigin column
,
235 JSErrorCallback errorCallback
, void* userRef
,
236 const unsigned errorNumber
, ...);
237 bool addNoteUTF8(js::FrontendContext
* fc
, const char* filename
,
238 unsigned sourceId
, uint32_t lineno
,
239 JS::ColumnNumberOneOrigin column
,
240 JSErrorCallback errorCallback
, void* userRef
,
241 const unsigned errorNumber
, ...);
243 JS_PUBLIC_API
size_t length();
245 // Create a deep copy of notes.
246 js::UniquePtr
<JSErrorNotes
> copy(JSContext
* cx
);
248 class iterator final
{
250 js::UniquePtr
<Note
>* note_
;
253 using iterator_category
= std::input_iterator_tag
;
254 using value_type
= js::UniquePtr
<Note
>;
255 using difference_type
= ptrdiff_t;
256 using pointer
= value_type
*;
257 using reference
= value_type
&;
259 explicit iterator(js::UniquePtr
<Note
>* note
= nullptr) : note_(note
) {}
261 bool operator==(iterator other
) const { return note_
== other
.note_
; }
262 bool operator!=(iterator other
) const { return !(*this == other
); }
263 iterator
& operator++() {
267 reference
operator*() { return *note_
; }
270 JS_PUBLIC_API iterator
begin();
271 JS_PUBLIC_API iterator
end();
275 * Describes a single error or warning that occurs in the execution of script.
277 class JSErrorReport
: public JSErrorBase
{
279 // Offending source line without final '\n'.
280 // If ownsLinebuf_ is true, the buffer is freed in destructor.
281 const char16_t
* linebuf_
;
283 // Number of chars in linebuf_. Does not include trailing '\0'.
284 size_t linebufLength_
;
286 // The 0-based offset of error token in linebuf_.
290 // Associated notes, or nullptr if there's no note.
291 js::UniquePtr
<JSErrorNotes
> notes
;
293 // One of the JSExnType constants.
296 // See the comment in TransitiveCompileOptions.
299 // This error report is actually a warning.
303 bool ownsLinebuf_
: 1;
314 ownsLinebuf_(false) {}
315 JSErrorReport(JSErrorReport
&& other
) noexcept
316 : JSErrorBase(std::move(other
)),
317 linebuf_(other
.linebuf_
),
318 linebufLength_(other
.linebufLength_
),
319 tokenOffset_(other
.tokenOffset_
),
320 notes(std::move(other
.notes
)),
321 exnType(other
.exnType
),
322 isMuted(other
.isMuted
),
323 isWarning_(other
.isWarning_
),
324 ownsLinebuf_(other
.ownsLinebuf_
) {
326 other
.ownsLinebuf_
= false;
330 ~JSErrorReport() { freeLinebuf(); }
333 const char16_t
* linebuf() const { return linebuf_
; }
334 size_t linebufLength() const { return linebufLength_
; }
335 size_t tokenOffset() const { return tokenOffset_
; }
336 void initOwnedLinebuf(const char16_t
* linebufArg
, size_t linebufLengthArg
,
337 size_t tokenOffsetArg
) {
338 initBorrowedLinebuf(linebufArg
, linebufLengthArg
, tokenOffsetArg
);
341 void initBorrowedLinebuf(const char16_t
* linebufArg
, size_t linebufLengthArg
,
342 size_t tokenOffsetArg
);
344 bool isWarning() const { return isWarning_
; }
352 struct MOZ_STACK_CLASS JS_PUBLIC_API ErrorReportBuilder
{
353 explicit ErrorReportBuilder(JSContext
* cx
);
354 ~ErrorReportBuilder();
356 enum SniffingBehavior
{ WithSideEffects
, NoSideEffects
};
359 * Generate a JSErrorReport from the provided thrown value.
361 * If the value is a (possibly wrapped) Error object, the JSErrorReport will
362 * be exactly initialized from the Error object's information, without
363 * observable side effects. (The Error object's JSErrorReport is reused, if
366 * Otherwise various attempts are made to derive JSErrorReport information
367 * from |exnStack| and from the current execution state. This process is
368 * *definitely* inconsistent with any standard, and particulars of the
369 * behavior implemented here generally shouldn't be relied upon.
371 * If the value of |sniffingBehavior| is |WithSideEffects|, some of these
372 * attempts *may* invoke user-configurable behavior when the exception is an
373 * object: converting it to a string, detecting and getting its properties,
374 * accessing its prototype chain, and others are possible. Users *must*
375 * tolerate |ErrorReportBuilder::init| potentially having arbitrary effects.
376 * Any exceptions thrown by these operations will be caught and silently
377 * ignored, and "default" values will be substituted into the JSErrorReport.
379 * But if the value of |sniffingBehavior| is |NoSideEffects|, these attempts
380 * *will not* invoke any observable side effects. The JSErrorReport will
381 * simply contain fewer, less precise details.
383 * Unlike some functions involved in error handling, this function adheres
384 * to the usual JSAPI return value error behavior.
386 bool init(JSContext
* cx
, const JS::ExceptionStack
& exnStack
,
387 SniffingBehavior sniffingBehavior
);
389 JSErrorReport
* report() const { return reportp
; }
391 const JS::ConstUTF8CharsZ
toStringResult() const { return toStringResult_
; }
394 // More or less an equivalent of JS_ReportErrorNumber/js::ReportErrorNumberVA
395 // but fills in an ErrorReport instead of reporting it. Uses varargs to
396 // make it simpler to call js::ExpandErrorArgumentsVA.
398 // Returns false if we fail to actually populate the ErrorReport
399 // for some reason (probably out of memory).
400 bool populateUncaughtExceptionReportUTF8(JSContext
* cx
,
401 JS::HandleObject stack
, ...);
402 bool populateUncaughtExceptionReportUTF8VA(JSContext
* cx
,
403 JS::HandleObject stack
,
406 // Reports exceptions from add-on scopes to telemetry.
407 void ReportAddonExceptionToTelemetry(JSContext
* cx
);
409 // We may have a provided JSErrorReport, so need a way to represent that.
410 JSErrorReport
* reportp
;
412 // Or we may need to synthesize a JSErrorReport one of our own.
413 JSErrorReport ownedReport
;
415 // Root our exception value to keep a possibly borrowed |reportp| alive.
416 JS::RootedObject exnObject
;
418 // And for our filename.
419 JS::UniqueChars filename
;
421 // We may have a result of error.toString().
422 // FIXME: We should not call error.toString(), since it could have side
423 // effect (see bug 633623).
424 JS::ConstUTF8CharsZ toStringResult_
;
425 JS::UniqueChars toStringResultBytesStorage
;
428 // Writes a full report to a file descriptor. Does nothing for JSErrorReports
429 // which are warnings, unless reportWarnings is set.
430 extern JS_PUBLIC_API
void PrintError(FILE* file
, JSErrorReport
* report
,
431 bool reportWarnings
);
433 extern JS_PUBLIC_API
void PrintError(FILE* file
,
434 const JS::ErrorReportBuilder
& builder
,
435 bool reportWarnings
);
440 * There are four encoding variants for the error reporting API:
442 * JSAPI's default encoding for error handling. Use this when the encoding
443 * of the error message, format string, and arguments is UTF-8.
445 * Equivalent to UTF-8, but also asserts that the error message, format
446 * string, and arguments are all ASCII. Because ASCII is a subset of UTF-8,
447 * any use of this encoding variant *could* be replaced with use of the
448 * UTF-8 variant. This variant exists solely to double-check the
449 * developer's assumption that all these strings truly are ASCII, given that
450 * UTF-8 and ASCII strings regrettably have the same C++ type.
452 * Use this when arguments are UTF-16. The format string must be UTF-8.
453 * Latin1 (planned to be removed)
454 * In this variant, all strings are interpreted byte-for-byte as the
455 * corresponding Unicode codepoint. This encoding may *safely* be used on
456 * any null-terminated string, regardless of its encoding. (You shouldn't
457 * *actually* be uncertain, but in the real world, a string's encoding -- if
458 * promised at all -- may be more...aspirational...than reality.) This
459 * encoding variant will eventually be removed -- work to convert your uses
460 * to UTF-8 as you're able.
464 const uint16_t MaxNumErrorArguments
= 10;
468 * Report an exception represented by the sprintf-like conversion of format
471 extern JS_PUBLIC_API
void JS_ReportErrorASCII(JSContext
* cx
, const char* format
,
472 ...) MOZ_FORMAT_PRINTF(2, 3);
474 extern JS_PUBLIC_API
void JS_ReportErrorLatin1(JSContext
* cx
,
475 const char* format
, ...)
476 MOZ_FORMAT_PRINTF(2, 3);
478 extern JS_PUBLIC_API
void JS_ReportErrorUTF8(JSContext
* cx
, const char* format
,
479 ...) MOZ_FORMAT_PRINTF(2, 3);
482 * Use an errorNumber to retrieve the format string, args are char*
484 extern JS_PUBLIC_API
void JS_ReportErrorNumberASCII(
485 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
486 const unsigned errorNumber
, ...);
488 extern JS_PUBLIC_API
void JS_ReportErrorNumberASCIIVA(
489 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
490 const unsigned errorNumber
, va_list ap
);
492 extern JS_PUBLIC_API
void JS_ReportErrorNumberLatin1(
493 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
494 const unsigned errorNumber
, ...);
497 extern JS_PUBLIC_API
void JS_ReportErrorNumberLatin1VA(
498 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
499 const unsigned errorNumber
, va_list ap
);
502 extern JS_PUBLIC_API
void JS_ReportErrorNumberUTF8(
503 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
504 const unsigned errorNumber
, ...);
507 extern JS_PUBLIC_API
void JS_ReportErrorNumberUTF8VA(
508 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
509 const unsigned errorNumber
, va_list ap
);
513 * args is null-terminated. That is, a null char* means there are no
514 * more args. The number of args must match the number expected for
515 * errorNumber for the given JSErrorCallback.
517 extern JS_PUBLIC_API
void JS_ReportErrorNumberUTF8Array(
518 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
519 const unsigned errorNumber
, const char** args
);
522 * Use an errorNumber to retrieve the format string, args are char16_t*
524 extern JS_PUBLIC_API
void JS_ReportErrorNumberUC(JSContext
* cx
,
525 JSErrorCallback errorCallback
,
527 const unsigned errorNumber
,
530 extern JS_PUBLIC_API
void JS_ReportErrorNumberUCArray(
531 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
532 const unsigned errorNumber
, const char16_t
** args
);
535 * Complain when out of memory.
537 extern MOZ_COLD JS_PUBLIC_API
void JS_ReportOutOfMemory(JSContext
* cx
);
539 extern JS_PUBLIC_API
bool JS_ExpandErrorArgumentsASCII(
540 JSContext
* cx
, JSErrorCallback errorCallback
, const unsigned errorNumber
,
541 JSErrorReport
* reportp
, ...);
544 * Complain when an allocation size overflows the maximum supported limit.
546 extern JS_PUBLIC_API
void JS_ReportAllocationOverflow(JSContext
* cx
);
550 extern JS_PUBLIC_API
bool CreateError(
551 JSContext
* cx
, JSExnType type
, HandleObject stack
, HandleString fileName
,
552 uint32_t lineNumber
, JS::ColumnNumberOneOrigin column
,
553 JSErrorReport
* report
, HandleString message
,
554 Handle
<mozilla::Maybe
<Value
>> cause
, MutableHandleValue rval
);
557 * An uncatchable exception is used to terminate execution by returning false
558 * or nullptr without reporting a pending exception on the context. These
559 * exceptions are called "uncatchable" because try-catch can't be used to catch
562 * This is mainly used to terminate JS execution from the interrupt handler.
564 * If the context has a pending exception, this function will clear it. Also, in
565 * debug builds, it sets a flag on the context to improve exception handling
566 * assertions in the engine.
568 extern JS_PUBLIC_API
void ReportUncatchableException(JSContext
* cx
);
572 #endif /* js_ErrorReport_h */