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.
68 JSEXN_FIRST
= JSEXN_ERR
,
77 JSEXN_DEBUGGEEWOULDRUN
,
78 JSEXN_WASMCOMPILEERROR
,
80 JSEXN_WASMRUNTIMEERROR
,
82 JSEXN_WARN
= JSEXN_ERROR_LIMIT
,
87 struct JSErrorFormatString
{
88 /** The error message name in ASCII. */
91 /** The error format string in ASCII. */
94 /** The number of arguments to expand in the formatted error message. */
97 /** One of the JSExnType constants above. */
101 using JSErrorCallback
=
102 const JSErrorFormatString
* (*)(void* userRef
, const unsigned errorNumber
);
105 * Base class that implements parts shared by JSErrorReport and
106 * JSErrorNotes::Note.
110 // The (default) error message.
111 // If ownsMessage_ is true, the it is freed in destructor.
112 JS::ConstUTF8CharsZ message_
;
115 // Source file name, URL, etc., or null.
116 JS::ConstUTF8CharsZ filename
;
118 // Unique identifier for the script source.
121 // Source line number (1-origin).
124 // Column number in line in UTF-16 code units.
125 JS::ColumnNumberOneOrigin column
;
127 // the error number, e.g. see js/public/friend/ErrorNumbers.msg.
128 unsigned errorNumber
;
130 // Points to JSErrorFormatString::name.
131 // This string must never be freed.
132 const char* errorMessageName
;
135 bool ownsMessage_
: 1;
143 errorMessageName(nullptr),
144 ownsMessage_(false) {}
145 JSErrorBase(JSErrorBase
&& other
) noexcept
146 : message_(other
.message_
),
147 filename(other
.filename
),
148 sourceId(other
.sourceId
),
149 lineno(other
.lineno
),
150 column(other
.column
),
151 errorNumber(other
.errorNumber
),
152 errorMessageName(other
.errorMessageName
),
153 ownsMessage_(other
.ownsMessage_
) {
155 other
.ownsMessage_
= false;
159 ~JSErrorBase() { freeMessage(); }
162 const JS::ConstUTF8CharsZ
message() const { return message_
; }
164 void initOwnedMessage(const char* messageArg
) {
165 initBorrowedMessage(messageArg
);
168 void initBorrowedMessage(const char* messageArg
) {
169 MOZ_ASSERT(!message_
);
170 message_
= JS::ConstUTF8CharsZ(messageArg
, strlen(messageArg
));
173 JSString
* newMessageString(JSContext
* cx
);
180 * Notes associated with JSErrorReport.
184 class Note final
: public JSErrorBase
{};
187 // Stores pointers to each note.
188 js::Vector
<js::UniquePtr
<Note
>, 1, js::SystemAllocPolicy
> notes_
;
190 bool addNoteVA(js::FrontendContext
* fc
, const char* filename
,
191 unsigned sourceId
, uint32_t lineno
,
192 JS::ColumnNumberOneOrigin column
,
193 JSErrorCallback errorCallback
, void* userRef
,
194 const unsigned errorNumber
,
195 js::ErrorArgumentsType argumentsType
, va_list ap
);
201 // Add a note to the given position.
202 bool addNoteASCII(JSContext
* cx
, const char* filename
, unsigned sourceId
,
203 uint32_t lineno
, JS::ColumnNumberOneOrigin column
,
204 JSErrorCallback errorCallback
, void* userRef
,
205 const unsigned errorNumber
, ...);
206 bool addNoteASCII(js::FrontendContext
* fc
, const char* filename
,
207 unsigned sourceId
, uint32_t lineno
,
208 JS::ColumnNumberOneOrigin column
,
209 JSErrorCallback errorCallback
, void* userRef
,
210 const unsigned errorNumber
, ...);
211 bool addNoteLatin1(JSContext
* cx
, const char* filename
, unsigned sourceId
,
212 uint32_t lineno
, JS::ColumnNumberOneOrigin column
,
213 JSErrorCallback errorCallback
, void* userRef
,
214 const unsigned errorNumber
, ...);
215 bool addNoteLatin1(js::FrontendContext
* fc
, const char* filename
,
216 unsigned sourceId
, uint32_t lineno
,
217 JS::ColumnNumberOneOrigin column
,
218 JSErrorCallback errorCallback
, void* userRef
,
219 const unsigned errorNumber
, ...);
220 bool addNoteUTF8(JSContext
* cx
, const char* filename
, unsigned sourceId
,
221 uint32_t lineno
, JS::ColumnNumberOneOrigin column
,
222 JSErrorCallback errorCallback
, void* userRef
,
223 const unsigned errorNumber
, ...);
224 bool addNoteUTF8(js::FrontendContext
* fc
, const char* filename
,
225 unsigned sourceId
, uint32_t lineno
,
226 JS::ColumnNumberOneOrigin column
,
227 JSErrorCallback errorCallback
, void* userRef
,
228 const unsigned errorNumber
, ...);
230 JS_PUBLIC_API
size_t length();
232 // Create a deep copy of notes.
233 js::UniquePtr
<JSErrorNotes
> copy(JSContext
* cx
);
235 class iterator final
{
237 js::UniquePtr
<Note
>* note_
;
240 using iterator_category
= std::input_iterator_tag
;
241 using value_type
= js::UniquePtr
<Note
>;
242 using difference_type
= ptrdiff_t;
243 using pointer
= value_type
*;
244 using reference
= value_type
&;
246 explicit iterator(js::UniquePtr
<Note
>* note
= nullptr) : note_(note
) {}
248 bool operator==(iterator other
) const { return note_
== other
.note_
; }
249 bool operator!=(iterator other
) const { return !(*this == other
); }
250 iterator
& operator++() {
254 reference
operator*() { return *note_
; }
257 JS_PUBLIC_API iterator
begin();
258 JS_PUBLIC_API iterator
end();
262 * Describes a single error or warning that occurs in the execution of script.
264 class JSErrorReport
: public JSErrorBase
{
266 // Offending source line without final '\n'.
267 // If ownsLinebuf_ is true, the buffer is freed in destructor.
268 const char16_t
* linebuf_
;
270 // Number of chars in linebuf_. Does not include trailing '\0'.
271 size_t linebufLength_
;
273 // The 0-based offset of error token in linebuf_.
277 // Associated notes, or nullptr if there's no note.
278 js::UniquePtr
<JSErrorNotes
> notes
;
280 // One of the JSExnType constants.
283 // See the comment in TransitiveCompileOptions.
286 // This error report is actually a warning.
290 bool ownsLinebuf_
: 1;
301 ownsLinebuf_(false) {}
302 JSErrorReport(JSErrorReport
&& other
) noexcept
303 : JSErrorBase(std::move(other
)),
304 linebuf_(other
.linebuf_
),
305 linebufLength_(other
.linebufLength_
),
306 tokenOffset_(other
.tokenOffset_
),
307 notes(std::move(other
.notes
)),
308 exnType(other
.exnType
),
309 isMuted(other
.isMuted
),
310 isWarning_(other
.isWarning_
),
311 ownsLinebuf_(other
.ownsLinebuf_
) {
313 other
.ownsLinebuf_
= false;
317 ~JSErrorReport() { freeLinebuf(); }
320 const char16_t
* linebuf() const { return linebuf_
; }
321 size_t linebufLength() const { return linebufLength_
; }
322 size_t tokenOffset() const { return tokenOffset_
; }
323 void initOwnedLinebuf(const char16_t
* linebufArg
, size_t linebufLengthArg
,
324 size_t tokenOffsetArg
) {
325 initBorrowedLinebuf(linebufArg
, linebufLengthArg
, tokenOffsetArg
);
328 void initBorrowedLinebuf(const char16_t
* linebufArg
, size_t linebufLengthArg
,
329 size_t tokenOffsetArg
);
331 bool isWarning() const { return isWarning_
; }
339 struct MOZ_STACK_CLASS JS_PUBLIC_API ErrorReportBuilder
{
340 explicit ErrorReportBuilder(JSContext
* cx
);
341 ~ErrorReportBuilder();
343 enum SniffingBehavior
{ WithSideEffects
, NoSideEffects
};
346 * Generate a JSErrorReport from the provided thrown value.
348 * If the value is a (possibly wrapped) Error object, the JSErrorReport will
349 * be exactly initialized from the Error object's information, without
350 * observable side effects. (The Error object's JSErrorReport is reused, if
353 * Otherwise various attempts are made to derive JSErrorReport information
354 * from |exnStack| and from the current execution state. This process is
355 * *definitely* inconsistent with any standard, and particulars of the
356 * behavior implemented here generally shouldn't be relied upon.
358 * If the value of |sniffingBehavior| is |WithSideEffects|, some of these
359 * attempts *may* invoke user-configurable behavior when the exception is an
360 * object: converting it to a string, detecting and getting its properties,
361 * accessing its prototype chain, and others are possible. Users *must*
362 * tolerate |ErrorReportBuilder::init| potentially having arbitrary effects.
363 * Any exceptions thrown by these operations will be caught and silently
364 * ignored, and "default" values will be substituted into the JSErrorReport.
366 * But if the value of |sniffingBehavior| is |NoSideEffects|, these attempts
367 * *will not* invoke any observable side effects. The JSErrorReport will
368 * simply contain fewer, less precise details.
370 * Unlike some functions involved in error handling, this function adheres
371 * to the usual JSAPI return value error behavior.
373 bool init(JSContext
* cx
, const JS::ExceptionStack
& exnStack
,
374 SniffingBehavior sniffingBehavior
);
376 JSErrorReport
* report() const { return reportp
; }
378 const JS::ConstUTF8CharsZ
toStringResult() const { return toStringResult_
; }
381 // More or less an equivalent of JS_ReportErrorNumber/js::ReportErrorNumberVA
382 // but fills in an ErrorReport instead of reporting it. Uses varargs to
383 // make it simpler to call js::ExpandErrorArgumentsVA.
385 // Returns false if we fail to actually populate the ErrorReport
386 // for some reason (probably out of memory).
387 bool populateUncaughtExceptionReportUTF8(JSContext
* cx
,
388 JS::HandleObject stack
, ...);
389 bool populateUncaughtExceptionReportUTF8VA(JSContext
* cx
,
390 JS::HandleObject stack
,
393 // Reports exceptions from add-on scopes to telemetry.
394 void ReportAddonExceptionToTelemetry(JSContext
* cx
);
396 // We may have a provided JSErrorReport, so need a way to represent that.
397 JSErrorReport
* reportp
;
399 // Or we may need to synthesize a JSErrorReport one of our own.
400 JSErrorReport ownedReport
;
402 // Root our exception value to keep a possibly borrowed |reportp| alive.
403 JS::RootedObject exnObject
;
405 // And for our filename.
406 JS::UniqueChars filename
;
408 // We may have a result of error.toString().
409 // FIXME: We should not call error.toString(), since it could have side
410 // effect (see bug 633623).
411 JS::ConstUTF8CharsZ toStringResult_
;
412 JS::UniqueChars toStringResultBytesStorage
;
415 // Writes a full report to a file descriptor. Does nothing for JSErrorReports
416 // which are warnings, unless reportWarnings is set.
417 extern JS_PUBLIC_API
void PrintError(FILE* file
, JSErrorReport
* report
,
418 bool reportWarnings
);
420 extern JS_PUBLIC_API
void PrintError(FILE* file
,
421 const JS::ErrorReportBuilder
& builder
,
422 bool reportWarnings
);
427 * There are four encoding variants for the error reporting API:
429 * JSAPI's default encoding for error handling. Use this when the encoding
430 * of the error message, format string, and arguments is UTF-8.
432 * Equivalent to UTF-8, but also asserts that the error message, format
433 * string, and arguments are all ASCII. Because ASCII is a subset of UTF-8,
434 * any use of this encoding variant *could* be replaced with use of the
435 * UTF-8 variant. This variant exists solely to double-check the
436 * developer's assumption that all these strings truly are ASCII, given that
437 * UTF-8 and ASCII strings regrettably have the same C++ type.
439 * Use this when arguments are UTF-16. The format string must be UTF-8.
440 * Latin1 (planned to be removed)
441 * In this variant, all strings are interpreted byte-for-byte as the
442 * corresponding Unicode codepoint. This encoding may *safely* be used on
443 * any null-terminated string, regardless of its encoding. (You shouldn't
444 * *actually* be uncertain, but in the real world, a string's encoding -- if
445 * promised at all -- may be more...aspirational...than reality.) This
446 * encoding variant will eventually be removed -- work to convert your uses
447 * to UTF-8 as you're able.
451 const uint16_t MaxNumErrorArguments
= 10;
455 * Report an exception represented by the sprintf-like conversion of format
458 extern JS_PUBLIC_API
void JS_ReportErrorASCII(JSContext
* cx
, const char* format
,
459 ...) MOZ_FORMAT_PRINTF(2, 3);
461 extern JS_PUBLIC_API
void JS_ReportErrorLatin1(JSContext
* cx
,
462 const char* format
, ...)
463 MOZ_FORMAT_PRINTF(2, 3);
465 extern JS_PUBLIC_API
void JS_ReportErrorUTF8(JSContext
* cx
, const char* format
,
466 ...) MOZ_FORMAT_PRINTF(2, 3);
469 * Use an errorNumber to retrieve the format string, args are char*
471 extern JS_PUBLIC_API
void JS_ReportErrorNumberASCII(
472 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
473 const unsigned errorNumber
, ...);
475 extern JS_PUBLIC_API
void JS_ReportErrorNumberASCIIVA(
476 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
477 const unsigned errorNumber
, va_list ap
);
479 extern JS_PUBLIC_API
void JS_ReportErrorNumberLatin1(
480 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
481 const unsigned errorNumber
, ...);
484 extern JS_PUBLIC_API
void JS_ReportErrorNumberLatin1VA(
485 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
486 const unsigned errorNumber
, va_list ap
);
489 extern JS_PUBLIC_API
void JS_ReportErrorNumberUTF8(
490 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
491 const unsigned errorNumber
, ...);
494 extern JS_PUBLIC_API
void JS_ReportErrorNumberUTF8VA(
495 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
496 const unsigned errorNumber
, va_list ap
);
500 * args is null-terminated. That is, a null char* means there are no
501 * more args. The number of args must match the number expected for
502 * errorNumber for the given JSErrorCallback.
504 extern JS_PUBLIC_API
void JS_ReportErrorNumberUTF8Array(
505 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
506 const unsigned errorNumber
, const char** args
);
509 * Use an errorNumber to retrieve the format string, args are char16_t*
511 extern JS_PUBLIC_API
void JS_ReportErrorNumberUC(JSContext
* cx
,
512 JSErrorCallback errorCallback
,
514 const unsigned errorNumber
,
517 extern JS_PUBLIC_API
void JS_ReportErrorNumberUCArray(
518 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
519 const unsigned errorNumber
, const char16_t
** args
);
522 * Complain when out of memory.
524 extern MOZ_COLD JS_PUBLIC_API
void JS_ReportOutOfMemory(JSContext
* cx
);
526 extern JS_PUBLIC_API
bool JS_ExpandErrorArgumentsASCII(
527 JSContext
* cx
, JSErrorCallback errorCallback
, const unsigned errorNumber
,
528 JSErrorReport
* reportp
, ...);
531 * Complain when an allocation size overflows the maximum supported limit.
533 extern JS_PUBLIC_API
void JS_ReportAllocationOverflow(JSContext
* cx
);
537 extern JS_PUBLIC_API
bool CreateError(
538 JSContext
* cx
, JSExnType type
, HandleObject stack
, HandleString fileName
,
539 uint32_t lineNumber
, JS::ColumnNumberOneOrigin column
,
540 JSErrorReport
* report
, HandleString message
,
541 Handle
<mozilla::Maybe
<Value
>> cause
, MutableHandleValue rval
);
545 #endif /* js_ErrorReport_h */