Backed out changeset 9d8b4c0b99ed (bug 1945683) for causing btime failures. CLOSED...
[gecko.git] / js / public / ErrorReport.h
blobb57950446e60912257515fe67d16641cfe5fe640
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/. */
6 /*
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
23 #include <cstdarg>
24 #include <iterator> // std::input_iterator_tag, std::iterator
25 #include <stdarg.h>
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;
43 namespace JS {
44 class ExceptionStack;
46 namespace js {
47 class SystemAllocPolicy;
49 enum ErrorArgumentsType {
50 ArgumentsAreUnicode,
51 ArgumentsAreASCII,
52 ArgumentsAreLatin1,
53 ArgumentsAreUTF8
55 } // namespace js
57 /**
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.
69 enum JSExnType {
70 // Generic Errors
71 JSEXN_ERR,
72 JSEXN_FIRST = JSEXN_ERR,
73 // Internal Errors
74 JSEXN_INTERNALERR,
75 // ECMAScript Errors
76 JSEXN_AGGREGATEERR,
77 JSEXN_EVALERR,
78 JSEXN_RANGEERR,
79 JSEXN_REFERENCEERR,
80 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
81 JSEXN_SUPPRESSEDERR,
82 #endif
83 JSEXN_SYNTAXERR,
84 JSEXN_TYPEERR,
85 JSEXN_URIERR,
86 // Debugger Errors
87 JSEXN_DEBUGGEEWOULDRUN,
88 // WASM Errors
89 JSEXN_WASMCOMPILEERROR,
90 JSEXN_WASMLINKERROR,
91 JSEXN_WASMRUNTIMEERROR,
92 JSEXN_ERROR_LIMIT,
93 // Warnings
94 JSEXN_WARN = JSEXN_ERROR_LIMIT,
95 // Error Notes
96 JSEXN_NOTE,
97 JSEXN_LIMIT
100 struct JSErrorFormatString {
101 /** The error message name in ASCII. */
102 const char* name;
104 /** The error format string in ASCII. */
105 const char* format;
107 /** The number of arguments to expand in the formatted error message. */
108 uint16_t argCount;
110 /** One of the JSExnType constants above. */
111 int16_t exnType;
114 using JSErrorCallback =
115 const JSErrorFormatString* (*)(void* userRef, const unsigned errorNumber);
118 * Base class that implements parts shared by JSErrorReport and
119 * JSErrorNotes::Note.
121 class JSErrorBase {
122 private:
123 // The (default) error message.
124 // If ownsMessage_ is true, the it is freed in destructor.
125 JS::ConstUTF8CharsZ message_;
127 public:
128 // Source file name, URL, etc., or null.
129 JS::ConstUTF8CharsZ filename;
131 // Unique identifier for the script source.
132 unsigned sourceId;
134 // Source line number (1-origin).
135 uint32_t lineno;
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;
147 private:
148 bool ownsMessage_ : 1;
150 public:
151 JSErrorBase()
152 : filename(nullptr),
153 sourceId(0),
154 lineno(0),
155 errorNumber(0),
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_) {
167 if (ownsMessage_) {
168 other.ownsMessage_ = false;
172 ~JSErrorBase() { freeMessage(); }
174 public:
175 const JS::ConstUTF8CharsZ message() const { return message_; }
177 void initOwnedMessage(const char* messageArg) {
178 initBorrowedMessage(messageArg);
179 ownsMessage_ = true;
181 void initBorrowedMessage(const char* messageArg) {
182 MOZ_ASSERT(!message_);
183 message_ = JS::ConstUTF8CharsZ(messageArg, strlen(messageArg));
186 JSString* newMessageString(JSContext* cx);
188 private:
189 void freeMessage();
193 * Notes associated with JSErrorReport.
195 class JSErrorNotes {
196 public:
197 class Note final : public JSErrorBase {};
199 private:
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);
210 public:
211 JSErrorNotes();
212 ~JSErrorNotes();
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 {
249 private:
250 js::UniquePtr<Note>* note_;
252 public:
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++() {
264 note_++;
265 return *this;
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 {
278 private:
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_.
287 size_t tokenOffset_;
289 public:
290 // Associated notes, or nullptr if there's no note.
291 js::UniquePtr<JSErrorNotes> notes;
293 // One of the JSExnType constants.
294 int16_t exnType;
296 // See the comment in TransitiveCompileOptions.
297 bool isMuted : 1;
299 // This error report is actually a warning.
300 bool isWarning_ : 1;
302 private:
303 bool ownsLinebuf_ : 1;
305 public:
306 JSErrorReport()
307 : linebuf_(nullptr),
308 linebufLength_(0),
309 tokenOffset_(0),
310 notes(nullptr),
311 exnType(0),
312 isMuted(false),
313 isWarning_(false),
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_) {
325 if (ownsLinebuf_) {
326 other.ownsLinebuf_ = false;
330 ~JSErrorReport() { freeLinebuf(); }
332 public:
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);
339 ownsLinebuf_ = true;
341 void initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg,
342 size_t tokenOffsetArg);
344 bool isWarning() const { return isWarning_; }
346 private:
347 void freeLinebuf();
350 namespace JS {
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
364 * it has one.)
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_; }
393 private:
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,
404 va_list ap);
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);
437 } // namespace JS
440 * There are four encoding variants for the error reporting API:
441 * UTF-8
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.
444 * ASCII
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.
451 * UC = UTF-16
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.
463 namespace JS {
464 const uint16_t MaxNumErrorArguments = 10;
468 * Report an exception represented by the sprintf-like conversion of format
469 * and its arguments.
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, ...);
496 #ifdef va_start
497 extern JS_PUBLIC_API void JS_ReportErrorNumberLatin1VA(
498 JSContext* cx, JSErrorCallback errorCallback, void* userRef,
499 const unsigned errorNumber, va_list ap);
500 #endif
502 extern JS_PUBLIC_API void JS_ReportErrorNumberUTF8(
503 JSContext* cx, JSErrorCallback errorCallback, void* userRef,
504 const unsigned errorNumber, ...);
506 #ifdef va_start
507 extern JS_PUBLIC_API void JS_ReportErrorNumberUTF8VA(
508 JSContext* cx, JSErrorCallback errorCallback, void* userRef,
509 const unsigned errorNumber, va_list ap);
510 #endif
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,
526 void* userRef,
527 const unsigned errorNumber,
528 ...);
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);
548 namespace JS {
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
560 * them.
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);
570 } /* namespace JS */
572 #endif /* js_ErrorReport_h */