Bug 1690340 - Part 4: Insert the "Page Source" before the "Extensions for Developers...
[gecko.git] / js / public / ErrorReport.h
bloba53e348c04f278fb8c58a7ad0f00c27424a42f29
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 types and structures.
9 * Despite these types and structures existing in js/public, significant parts
10 * of their heritage date back to distant SpiderMonkey past, and they are not
11 * all universally well-thought-out as ideal, intended-to-be-permanent API.
12 * We may eventually replace this with something more consistent with
13 * ECMAScript the language and less consistent with '90s-era JSAPI inventions,
14 * 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
22 #include <iterator> // std::input_iterator_tag, std::iterator
23 #include <stddef.h> // size_t
24 #include <stdint.h> // int16_t, uint16_t
25 #include <string.h> // strlen
27 #include "jstypes.h" // JS_PUBLIC_API
29 #include "js/AllocPolicy.h" // js::SystemAllocPolicy
30 #include "js/CharacterEncoding.h" // JS::ConstUTF8CharsZ
31 #include "js/Exception.h" // JS::ExceptionStack
32 #include "js/RootingAPI.h" // JS::HandleObject, JS::RootedObject
33 #include "js/UniquePtr.h" // js::UniquePtr
34 #include "js/Vector.h" // js::Vector
36 struct JS_PUBLIC_API JSContext;
37 class JS_PUBLIC_API JSString;
39 /**
40 * Possible exception types. These types are part of a JSErrorFormatString
41 * structure. They define which error to throw in case of a runtime error.
43 * JSEXN_WARN is used for warnings, that are not strictly errors but are handled
44 * using the generalized error reporting mechanism. (One side effect of this
45 * type is to not prepend 'Error:' to warning messages.) This value can go away
46 * if we ever decide to use an entirely separate mechanism for warnings.
48 enum JSExnType {
49 JSEXN_ERR,
50 JSEXN_FIRST = JSEXN_ERR,
51 JSEXN_INTERNALERR,
52 JSEXN_AGGREGATEERR,
53 JSEXN_EVALERR,
54 JSEXN_RANGEERR,
55 JSEXN_REFERENCEERR,
56 JSEXN_SYNTAXERR,
57 JSEXN_TYPEERR,
58 JSEXN_URIERR,
59 JSEXN_DEBUGGEEWOULDRUN,
60 JSEXN_WASMCOMPILEERROR,
61 JSEXN_WASMLINKERROR,
62 JSEXN_WASMRUNTIMEERROR,
63 JSEXN_ERROR_LIMIT,
64 JSEXN_WARN = JSEXN_ERROR_LIMIT,
65 JSEXN_NOTE,
66 JSEXN_LIMIT
69 struct JSErrorFormatString {
70 /** The error message name in ASCII. */
71 const char* name;
73 /** The error format string in ASCII. */
74 const char* format;
76 /** The number of arguments to expand in the formatted error message. */
77 uint16_t argCount;
79 /** One of the JSExnType constants above. */
80 int16_t exnType;
83 using JSErrorCallback =
84 const JSErrorFormatString* (*)(void* userRef, const unsigned errorNumber);
86 /**
87 * Base class that implements parts shared by JSErrorReport and
88 * JSErrorNotes::Note.
90 class JSErrorBase {
91 private:
92 // The (default) error message.
93 // If ownsMessage_ is true, the it is freed in destructor.
94 JS::ConstUTF8CharsZ message_;
96 public:
97 // Source file name, URL, etc., or null.
98 const char* filename;
100 // Unique identifier for the script source.
101 unsigned sourceId;
103 // Source line number.
104 unsigned lineno;
106 // Zero-based column index in line.
107 unsigned column;
109 // the error number, e.g. see js/public/friend/ErrorNumbers.msg.
110 unsigned errorNumber;
112 // Points to JSErrorFormatString::name.
113 // This string must never be freed.
114 const char* errorMessageName;
116 private:
117 bool ownsMessage_ : 1;
119 public:
120 JSErrorBase()
121 : filename(nullptr),
122 sourceId(0),
123 lineno(0),
124 column(0),
125 errorNumber(0),
126 errorMessageName(nullptr),
127 ownsMessage_(false) {}
129 ~JSErrorBase() { freeMessage(); }
131 public:
132 const JS::ConstUTF8CharsZ message() const { return message_; }
134 void initOwnedMessage(const char* messageArg) {
135 initBorrowedMessage(messageArg);
136 ownsMessage_ = true;
138 void initBorrowedMessage(const char* messageArg) {
139 MOZ_ASSERT(!message_);
140 message_ = JS::ConstUTF8CharsZ(messageArg, strlen(messageArg));
143 JSString* newMessageString(JSContext* cx);
145 private:
146 void freeMessage();
150 * Notes associated with JSErrorReport.
152 class JSErrorNotes {
153 public:
154 class Note final : public JSErrorBase {};
156 private:
157 // Stores pointers to each note.
158 js::Vector<js::UniquePtr<Note>, 1, js::SystemAllocPolicy> notes_;
160 public:
161 JSErrorNotes();
162 ~JSErrorNotes();
164 // Add an note to the given position.
165 bool addNoteASCII(JSContext* cx, const char* filename, unsigned sourceId,
166 unsigned lineno, unsigned column,
167 JSErrorCallback errorCallback, void* userRef,
168 const unsigned errorNumber, ...);
169 bool addNoteLatin1(JSContext* cx, const char* filename, unsigned sourceId,
170 unsigned lineno, unsigned column,
171 JSErrorCallback errorCallback, void* userRef,
172 const unsigned errorNumber, ...);
173 bool addNoteUTF8(JSContext* cx, const char* filename, unsigned sourceId,
174 unsigned lineno, unsigned column,
175 JSErrorCallback errorCallback, void* userRef,
176 const unsigned errorNumber, ...);
178 JS_PUBLIC_API size_t length();
180 // Create a deep copy of notes.
181 js::UniquePtr<JSErrorNotes> copy(JSContext* cx);
183 class iterator final {
184 private:
185 js::UniquePtr<Note>* note_;
187 public:
188 using iterator_category = std::input_iterator_tag;
189 using value_type = js::UniquePtr<Note>;
190 using difference_type = ptrdiff_t;
191 using pointer = value_type*;
192 using reference = value_type&;
194 explicit iterator(js::UniquePtr<Note>* note = nullptr) : note_(note) {}
196 bool operator==(iterator other) const { return note_ == other.note_; }
197 bool operator!=(iterator other) const { return !(*this == other); }
198 iterator& operator++() {
199 note_++;
200 return *this;
202 reference operator*() { return *note_; }
205 JS_PUBLIC_API iterator begin();
206 JS_PUBLIC_API iterator end();
210 * Describes a single error or warning that occurs in the execution of script.
212 class JSErrorReport : public JSErrorBase {
213 private:
214 // Offending source line without final '\n'.
215 // If ownsLinebuf_ is true, the buffer is freed in destructor.
216 const char16_t* linebuf_;
218 // Number of chars in linebuf_. Does not include trailing '\0'.
219 size_t linebufLength_;
221 // The 0-based offset of error token in linebuf_.
222 size_t tokenOffset_;
224 public:
225 // Associated notes, or nullptr if there's no note.
226 js::UniquePtr<JSErrorNotes> notes;
228 // One of the JSExnType constants.
229 int16_t exnType;
231 // See the comment in TransitiveCompileOptions.
232 bool isMuted : 1;
234 // This error report is actually a warning.
235 bool isWarning_ : 1;
237 private:
238 bool ownsLinebuf_ : 1;
240 public:
241 JSErrorReport()
242 : linebuf_(nullptr),
243 linebufLength_(0),
244 tokenOffset_(0),
245 notes(nullptr),
246 exnType(0),
247 isMuted(false),
248 isWarning_(false),
249 ownsLinebuf_(false) {}
251 ~JSErrorReport() { freeLinebuf(); }
253 public:
254 const char16_t* linebuf() const { return linebuf_; }
255 size_t linebufLength() const { return linebufLength_; }
256 size_t tokenOffset() const { return tokenOffset_; }
257 void initOwnedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg,
258 size_t tokenOffsetArg) {
259 initBorrowedLinebuf(linebufArg, linebufLengthArg, tokenOffsetArg);
260 ownsLinebuf_ = true;
262 void initBorrowedLinebuf(const char16_t* linebufArg, size_t linebufLengthArg,
263 size_t tokenOffsetArg);
265 bool isWarning() const { return isWarning_; }
267 private:
268 void freeLinebuf();
271 namespace JS {
273 struct MOZ_STACK_CLASS JS_PUBLIC_API ErrorReportBuilder {
274 explicit ErrorReportBuilder(JSContext* cx);
275 ~ErrorReportBuilder();
277 enum SniffingBehavior { WithSideEffects, NoSideEffects };
280 * Generate a JSErrorReport from the provided thrown value.
282 * If the value is a (possibly wrapped) Error object, the JSErrorReport will
283 * be exactly initialized from the Error object's information, without
284 * observable side effects. (The Error object's JSErrorReport is reused, if
285 * it has one.)
287 * Otherwise various attempts are made to derive JSErrorReport information
288 * from |exnStack| and from the current execution state. This process is
289 * *definitely* inconsistent with any standard, and particulars of the
290 * behavior implemented here generally shouldn't be relied upon.
292 * If the value of |sniffingBehavior| is |WithSideEffects|, some of these
293 * attempts *may* invoke user-configurable behavior when the exception is an
294 * object: converting it to a string, detecting and getting its properties,
295 * accessing its prototype chain, and others are possible. Users *must*
296 * tolerate |ErrorReportBuilder::init| potentially having arbitrary effects.
297 * Any exceptions thrown by these operations will be caught and silently
298 * ignored, and "default" values will be substituted into the JSErrorReport.
300 * But if the value of |sniffingBehavior| is |NoSideEffects|, these attempts
301 * *will not* invoke any observable side effects. The JSErrorReport will
302 * simply contain fewer, less precise details.
304 * Unlike some functions involved in error handling, this function adheres
305 * to the usual JSAPI return value error behavior.
307 bool init(JSContext* cx, const JS::ExceptionStack& exnStack,
308 SniffingBehavior sniffingBehavior);
310 JSErrorReport* report() const { return reportp; }
312 const JS::ConstUTF8CharsZ toStringResult() const { return toStringResult_; }
314 private:
315 // More or less an equivalent of JS_ReportErrorNumber/js::ReportErrorNumberVA
316 // but fills in an ErrorReport instead of reporting it. Uses varargs to
317 // make it simpler to call js::ExpandErrorArgumentsVA.
319 // Returns false if we fail to actually populate the ErrorReport
320 // for some reason (probably out of memory).
321 bool populateUncaughtExceptionReportUTF8(JSContext* cx,
322 JS::HandleObject stack, ...);
323 bool populateUncaughtExceptionReportUTF8VA(JSContext* cx,
324 JS::HandleObject stack,
325 va_list ap);
327 // Reports exceptions from add-on scopes to telemetry.
328 void ReportAddonExceptionToTelemetry(JSContext* cx);
330 // We may have a provided JSErrorReport, so need a way to represent that.
331 JSErrorReport* reportp;
333 // Or we may need to synthesize a JSErrorReport one of our own.
334 JSErrorReport ownedReport;
336 // Root our exception value to keep a possibly borrowed |reportp| alive.
337 JS::RootedObject exnObject;
339 // And for our filename.
340 JS::UniqueChars filename;
342 // We may have a result of error.toString().
343 // FIXME: We should not call error.toString(), since it could have side
344 // effect (see bug 633623).
345 JS::ConstUTF8CharsZ toStringResult_;
346 JS::UniqueChars toStringResultBytesStorage;
349 // Writes a full report to a file descriptor. Does nothing for JSErrorReports
350 // which are warnings, unless reportWarnings is set.
351 extern JS_PUBLIC_API void PrintError(JSContext* cx, FILE* file,
352 JSErrorReport* report,
353 bool reportWarnings);
355 extern JS_PUBLIC_API void PrintError(JSContext* cx, FILE* file,
356 const JS::ErrorReportBuilder& builder,
357 bool reportWarnings);
359 } // namespace JS
361 #endif /* js_ErrorReport_h */