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
22 #include <iterator> // std::input_iterator_tag, std::iterator
24 #include <stddef.h> // size_t
25 #include <stdint.h> // int16_t, uint16_t
26 #include <string.h> // strlen
28 #include "jstypes.h" // JS_PUBLIC_API
30 #include "js/AllocPolicy.h"
31 #include "js/CharacterEncoding.h" // JS::ConstUTF8CharsZ
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
;
43 class SystemAllocPolicy
;
47 * Possible exception types. These types are part of a JSErrorFormatString
48 * structure. They define which error to throw in case of a runtime error.
50 * JSEXN_WARN is used for warnings, that are not strictly errors but are handled
51 * using the generalized error reporting mechanism. (One side effect of this
52 * type is to not prepend 'Error:' to warning messages.) This value can go away
53 * if we ever decide to use an entirely separate mechanism for warnings.
57 JSEXN_FIRST
= JSEXN_ERR
,
66 JSEXN_DEBUGGEEWOULDRUN
,
67 JSEXN_WASMCOMPILEERROR
,
69 JSEXN_WASMRUNTIMEERROR
,
71 JSEXN_WARN
= JSEXN_ERROR_LIMIT
,
76 struct JSErrorFormatString
{
77 /** The error message name in ASCII. */
80 /** The error format string in ASCII. */
83 /** The number of arguments to expand in the formatted error message. */
86 /** One of the JSExnType constants above. */
90 using JSErrorCallback
=
91 const JSErrorFormatString
* (*)(void* userRef
, const unsigned errorNumber
);
94 * Base class that implements parts shared by JSErrorReport and
99 // The (default) error message.
100 // If ownsMessage_ is true, the it is freed in destructor.
101 JS::ConstUTF8CharsZ message_
;
104 // Source file name, URL, etc., or null.
105 const char* filename
;
107 // Unique identifier for the script source.
110 // Source line number.
113 // Zero-based column index in line.
116 // the error number, e.g. see js/public/friend/ErrorNumbers.msg.
117 unsigned errorNumber
;
119 // Points to JSErrorFormatString::name.
120 // This string must never be freed.
121 const char* errorMessageName
;
124 bool ownsMessage_
: 1;
133 errorMessageName(nullptr),
134 ownsMessage_(false) {}
136 ~JSErrorBase() { freeMessage(); }
139 const JS::ConstUTF8CharsZ
message() const { return message_
; }
141 void initOwnedMessage(const char* messageArg
) {
142 initBorrowedMessage(messageArg
);
145 void initBorrowedMessage(const char* messageArg
) {
146 MOZ_ASSERT(!message_
);
147 message_
= JS::ConstUTF8CharsZ(messageArg
, strlen(messageArg
));
150 JSString
* newMessageString(JSContext
* cx
);
157 * Notes associated with JSErrorReport.
161 class Note final
: public JSErrorBase
{};
164 // Stores pointers to each note.
165 js::Vector
<js::UniquePtr
<Note
>, 1, js::SystemAllocPolicy
> notes_
;
171 // Add an note to the given position.
172 bool addNoteASCII(JSContext
* cx
, const char* filename
, unsigned sourceId
,
173 unsigned lineno
, unsigned column
,
174 JSErrorCallback errorCallback
, void* userRef
,
175 const unsigned errorNumber
, ...);
176 bool addNoteLatin1(JSContext
* cx
, const char* filename
, unsigned sourceId
,
177 unsigned lineno
, unsigned column
,
178 JSErrorCallback errorCallback
, void* userRef
,
179 const unsigned errorNumber
, ...);
180 bool addNoteUTF8(JSContext
* cx
, const char* filename
, unsigned sourceId
,
181 unsigned lineno
, unsigned column
,
182 JSErrorCallback errorCallback
, void* userRef
,
183 const unsigned errorNumber
, ...);
185 JS_PUBLIC_API
size_t length();
187 // Create a deep copy of notes.
188 js::UniquePtr
<JSErrorNotes
> copy(JSContext
* cx
);
190 class iterator final
{
192 js::UniquePtr
<Note
>* note_
;
195 using iterator_category
= std::input_iterator_tag
;
196 using value_type
= js::UniquePtr
<Note
>;
197 using difference_type
= ptrdiff_t;
198 using pointer
= value_type
*;
199 using reference
= value_type
&;
201 explicit iterator(js::UniquePtr
<Note
>* note
= nullptr) : note_(note
) {}
203 bool operator==(iterator other
) const { return note_
== other
.note_
; }
204 bool operator!=(iterator other
) const { return !(*this == other
); }
205 iterator
& operator++() {
209 reference
operator*() { return *note_
; }
212 JS_PUBLIC_API iterator
begin();
213 JS_PUBLIC_API iterator
end();
217 * Describes a single error or warning that occurs in the execution of script.
219 class JSErrorReport
: public JSErrorBase
{
221 // Offending source line without final '\n'.
222 // If ownsLinebuf_ is true, the buffer is freed in destructor.
223 const char16_t
* linebuf_
;
225 // Number of chars in linebuf_. Does not include trailing '\0'.
226 size_t linebufLength_
;
228 // The 0-based offset of error token in linebuf_.
232 // Associated notes, or nullptr if there's no note.
233 js::UniquePtr
<JSErrorNotes
> notes
;
235 // One of the JSExnType constants.
238 // See the comment in TransitiveCompileOptions.
241 // This error report is actually a warning.
245 bool ownsLinebuf_
: 1;
256 ownsLinebuf_(false) {}
258 ~JSErrorReport() { freeLinebuf(); }
261 const char16_t
* linebuf() const { return linebuf_
; }
262 size_t linebufLength() const { return linebufLength_
; }
263 size_t tokenOffset() const { return tokenOffset_
; }
264 void initOwnedLinebuf(const char16_t
* linebufArg
, size_t linebufLengthArg
,
265 size_t tokenOffsetArg
) {
266 initBorrowedLinebuf(linebufArg
, linebufLengthArg
, tokenOffsetArg
);
269 void initBorrowedLinebuf(const char16_t
* linebufArg
, size_t linebufLengthArg
,
270 size_t tokenOffsetArg
);
272 bool isWarning() const { return isWarning_
; }
280 struct MOZ_STACK_CLASS JS_PUBLIC_API ErrorReportBuilder
{
281 explicit ErrorReportBuilder(JSContext
* cx
);
282 ~ErrorReportBuilder();
284 enum SniffingBehavior
{ WithSideEffects
, NoSideEffects
};
287 * Generate a JSErrorReport from the provided thrown value.
289 * If the value is a (possibly wrapped) Error object, the JSErrorReport will
290 * be exactly initialized from the Error object's information, without
291 * observable side effects. (The Error object's JSErrorReport is reused, if
294 * Otherwise various attempts are made to derive JSErrorReport information
295 * from |exnStack| and from the current execution state. This process is
296 * *definitely* inconsistent with any standard, and particulars of the
297 * behavior implemented here generally shouldn't be relied upon.
299 * If the value of |sniffingBehavior| is |WithSideEffects|, some of these
300 * attempts *may* invoke user-configurable behavior when the exception is an
301 * object: converting it to a string, detecting and getting its properties,
302 * accessing its prototype chain, and others are possible. Users *must*
303 * tolerate |ErrorReportBuilder::init| potentially having arbitrary effects.
304 * Any exceptions thrown by these operations will be caught and silently
305 * ignored, and "default" values will be substituted into the JSErrorReport.
307 * But if the value of |sniffingBehavior| is |NoSideEffects|, these attempts
308 * *will not* invoke any observable side effects. The JSErrorReport will
309 * simply contain fewer, less precise details.
311 * Unlike some functions involved in error handling, this function adheres
312 * to the usual JSAPI return value error behavior.
314 bool init(JSContext
* cx
, const JS::ExceptionStack
& exnStack
,
315 SniffingBehavior sniffingBehavior
);
317 JSErrorReport
* report() const { return reportp
; }
319 const JS::ConstUTF8CharsZ
toStringResult() const { return toStringResult_
; }
322 // More or less an equivalent of JS_ReportErrorNumber/js::ReportErrorNumberVA
323 // but fills in an ErrorReport instead of reporting it. Uses varargs to
324 // make it simpler to call js::ExpandErrorArgumentsVA.
326 // Returns false if we fail to actually populate the ErrorReport
327 // for some reason (probably out of memory).
328 bool populateUncaughtExceptionReportUTF8(JSContext
* cx
,
329 JS::HandleObject stack
, ...);
330 bool populateUncaughtExceptionReportUTF8VA(JSContext
* cx
,
331 JS::HandleObject stack
,
334 // Reports exceptions from add-on scopes to telemetry.
335 void ReportAddonExceptionToTelemetry(JSContext
* cx
);
337 // We may have a provided JSErrorReport, so need a way to represent that.
338 JSErrorReport
* reportp
;
340 // Or we may need to synthesize a JSErrorReport one of our own.
341 JSErrorReport ownedReport
;
343 // Root our exception value to keep a possibly borrowed |reportp| alive.
344 JS::RootedObject exnObject
;
346 // And for our filename.
347 JS::UniqueChars filename
;
349 // We may have a result of error.toString().
350 // FIXME: We should not call error.toString(), since it could have side
351 // effect (see bug 633623).
352 JS::ConstUTF8CharsZ toStringResult_
;
353 JS::UniqueChars toStringResultBytesStorage
;
356 // Writes a full report to a file descriptor. Does nothing for JSErrorReports
357 // which are warnings, unless reportWarnings is set.
358 extern JS_PUBLIC_API
void PrintError(FILE* file
, JSErrorReport
* report
,
359 bool reportWarnings
);
361 extern JS_PUBLIC_API
void PrintError(FILE* file
,
362 const JS::ErrorReportBuilder
& builder
,
363 bool reportWarnings
);
368 * There are four encoding variants for the error reporting API:
370 * JSAPI's default encoding for error handling. Use this when the encoding
371 * of the error message, format string, and arguments is UTF-8.
373 * Equivalent to UTF-8, but also asserts that the error message, format
374 * string, and arguments are all ASCII. Because ASCII is a subset of UTF-8,
375 * any use of this encoding variant *could* be replaced with use of the
376 * UTF-8 variant. This variant exists solely to double-check the
377 * developer's assumption that all these strings truly are ASCII, given that
378 * UTF-8 and ASCII strings regrettably have the same C++ type.
380 * Use this when arguments are UTF-16. The format string must be UTF-8.
381 * Latin1 (planned to be removed)
382 * In this variant, all strings are interpreted byte-for-byte as the
383 * corresponding Unicode codepoint. This encoding may *safely* be used on
384 * any null-terminated string, regardless of its encoding. (You shouldn't
385 * *actually* be uncertain, but in the real world, a string's encoding -- if
386 * promised at all -- may be more...aspirational...than reality.) This
387 * encoding variant will eventually be removed -- work to convert your uses
388 * to UTF-8 as you're able.
392 const uint16_t MaxNumErrorArguments
= 10;
396 * Report an exception represented by the sprintf-like conversion of format
399 extern JS_PUBLIC_API
void JS_ReportErrorASCII(JSContext
* cx
, const char* format
,
400 ...) MOZ_FORMAT_PRINTF(2, 3);
402 extern JS_PUBLIC_API
void JS_ReportErrorLatin1(JSContext
* cx
,
403 const char* format
, ...)
404 MOZ_FORMAT_PRINTF(2, 3);
406 extern JS_PUBLIC_API
void JS_ReportErrorUTF8(JSContext
* cx
, const char* format
,
407 ...) MOZ_FORMAT_PRINTF(2, 3);
410 * Use an errorNumber to retrieve the format string, args are char*
412 extern JS_PUBLIC_API
void JS_ReportErrorNumberASCII(
413 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
414 const unsigned errorNumber
, ...);
416 extern JS_PUBLIC_API
void JS_ReportErrorNumberASCIIVA(
417 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
418 const unsigned errorNumber
, va_list ap
);
420 extern JS_PUBLIC_API
void JS_ReportErrorNumberLatin1(
421 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
422 const unsigned errorNumber
, ...);
425 extern JS_PUBLIC_API
void JS_ReportErrorNumberLatin1VA(
426 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
427 const unsigned errorNumber
, va_list ap
);
430 extern JS_PUBLIC_API
void JS_ReportErrorNumberUTF8(
431 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
432 const unsigned errorNumber
, ...);
435 extern JS_PUBLIC_API
void JS_ReportErrorNumberUTF8VA(
436 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
437 const unsigned errorNumber
, va_list ap
);
441 * args is null-terminated. That is, a null char* means there are no
442 * more args. The number of args must match the number expected for
443 * errorNumber for the given JSErrorCallback.
445 extern JS_PUBLIC_API
void JS_ReportErrorNumberUTF8Array(
446 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
447 const unsigned errorNumber
, const char** args
);
450 * Use an errorNumber to retrieve the format string, args are char16_t*
452 extern JS_PUBLIC_API
void JS_ReportErrorNumberUC(JSContext
* cx
,
453 JSErrorCallback errorCallback
,
455 const unsigned errorNumber
,
458 extern JS_PUBLIC_API
void JS_ReportErrorNumberUCArray(
459 JSContext
* cx
, JSErrorCallback errorCallback
, void* userRef
,
460 const unsigned errorNumber
, const char16_t
** args
);
463 * Complain when out of memory.
465 extern MOZ_COLD JS_PUBLIC_API
void JS_ReportOutOfMemory(JSContext
* cx
);
467 extern JS_PUBLIC_API
bool JS_ExpandErrorArgumentsASCII(
468 JSContext
* cx
, JSErrorCallback errorCallback
, const unsigned errorNumber
,
469 JSErrorReport
* reportp
, ...);
472 * Complain when an allocation size overflows the maximum supported limit.
474 extern JS_PUBLIC_API
void JS_ReportAllocationOverflow(JSContext
* cx
);
478 extern JS_PUBLIC_API
bool CreateError(
479 JSContext
* cx
, JSExnType type
, HandleObject stack
, HandleString fileName
,
480 uint32_t lineNumber
, uint32_t columnNumber
, JSErrorReport
* report
,
481 HandleString message
, MutableHandleValue rval
);
485 #endif /* js_ErrorReport_h */