1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * JS standard exception implementation.
13 #include "mozilla/Assertions.h"
14 #include "mozilla/Maybe.h"
15 #include "mozilla/ScopeExit.h"
25 #include "jsfriendapi.h"
28 #include "frontend/FrontendContext.h" // AutoReportFrontendContext
29 #include "js/CharacterEncoding.h" // JS::UTF8Chars, JS::ConstUTF8CharsZ
31 #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin, JS::TaggedColumnNumberZeroOrigin
32 #include "js/Conversions.h"
33 #include "js/ErrorReport.h" // JS::PrintError
34 #include "js/Exception.h" // JS::ExceptionStack
35 #include "js/experimental/TypedData.h" // JS_IsArrayBufferViewObject
36 #include "js/friend/ErrorMessages.h" // JSErrNum, js::GetErrorMessage, JSMSG_*
37 #include "js/Object.h" // JS::GetBuiltinClass
38 #include "js/PropertyAndElement.h" // JS_GetProperty, JS_HasProperty
39 #include "js/SavedFrameAPI.h"
41 #include "js/UniquePtr.h"
43 #include "js/Warnings.h" // JS::{,Set}WarningReporter
44 #include "js/Wrapper.h"
45 #include "util/Memory.h"
46 #include "util/StringBuffer.h"
47 #include "vm/Compartment.h"
48 #include "vm/ErrorObject.h"
49 #include "vm/FrameIter.h" // js::NonBuiltinFrameIter
50 #include "vm/JSAtomUtils.h" // ClassName
51 #include "vm/JSContext.h"
52 #include "vm/JSObject.h"
53 #include "vm/JSScript.h"
55 #include "vm/SavedFrame.h"
56 #include "vm/SavedStacks.h"
57 #include "vm/SelfHosting.h"
59 #include "vm/StringType.h"
60 #include "vm/SymbolType.h"
61 #include "wasm/WasmJS.h" // WasmExceptionObject
63 #include "vm/Compartment-inl.h"
64 #include "vm/ErrorObject-inl.h"
65 #include "vm/JSContext-inl.h"
66 #include "vm/JSObject-inl.h"
67 #include "vm/ObjectOperations-inl.h" // js::GetProperty
68 #include "vm/SavedStacks-inl.h"
72 using JS::SavedFrameSelfHosted
;
74 size_t ExtraMallocSize(JSErrorReport
* report
) {
75 if (report
->linebuf()) {
77 * Count with null terminator and alignment.
78 * See CopyExtraData for the details about alignment.
80 return (report
->linebufLength() + 1) * sizeof(char16_t
) + 1;
86 size_t ExtraMallocSize(JSErrorNotes::Note
* note
) { return 0; }
88 bool CopyExtraData(JSContext
* cx
, uint8_t** cursor
, JSErrorReport
* copy
,
89 JSErrorReport
* report
) {
90 if (report
->linebuf()) {
92 * Make sure cursor is properly aligned for char16_t for platforms
93 * which need it and it's at the end of the buffer on exit.
95 size_t alignment_backlog
= 0;
96 if (size_t(*cursor
) % 2) {
99 alignment_backlog
= 1;
102 size_t linebufSize
= (report
->linebufLength() + 1) * sizeof(char16_t
);
103 const char16_t
* linebufCopy
= (const char16_t
*)(*cursor
);
104 js_memcpy(*cursor
, report
->linebuf(), linebufSize
);
105 *cursor
+= linebufSize
+ alignment_backlog
;
106 copy
->initBorrowedLinebuf(linebufCopy
, report
->linebufLength(),
107 report
->tokenOffset());
110 /* Copy non-pointer members. */
111 copy
->isMuted
= report
->isMuted
;
112 copy
->exnType
= report
->exnType
;
113 copy
->isWarning_
= report
->isWarning_
;
115 /* Deep copy notes. */
117 auto copiedNotes
= report
->notes
->copy(cx
);
121 copy
->notes
= std::move(copiedNotes
);
123 copy
->notes
.reset(nullptr);
129 bool CopyExtraData(JSContext
* cx
, uint8_t** cursor
, JSErrorNotes::Note
* copy
,
130 JSErrorNotes::Note
* report
) {
134 template <typename T
>
135 static UniquePtr
<T
> CopyErrorHelper(JSContext
* cx
, T
* report
) {
137 * We use a single malloc block to make a deep copy of JSErrorReport or
138 * JSErrorNotes::Note, except JSErrorNotes linked from JSErrorReport with
139 * the following layout:
140 * JSErrorReport or JSErrorNotes::Note
141 * char array with characters for message_
142 * char array with characters for filename
143 * char16_t array with characters for linebuf (only for JSErrorReport)
144 * Such layout together with the properties enforced by the following
145 * asserts does not need any extra alignment padding.
147 static_assert(sizeof(T
) % sizeof(const char*) == 0);
148 static_assert(sizeof(const char*) % sizeof(char16_t
) == 0);
150 size_t filenameSize
=
151 report
->filename
? strlen(report
->filename
.c_str()) + 1 : 0;
152 size_t messageSize
= 0;
153 if (report
->message()) {
154 messageSize
= strlen(report
->message().c_str()) + 1;
158 * The mallocSize can not overflow since it represents the sum of the
159 * sizes of already allocated objects.
162 sizeof(T
) + messageSize
+ filenameSize
+ ExtraMallocSize(report
);
163 uint8_t* cursor
= cx
->pod_calloc
<uint8_t>(mallocSize
);
168 UniquePtr
<T
> copy(new (cursor
) T());
171 if (report
->message()) {
172 copy
->initBorrowedMessage((const char*)cursor
);
173 js_memcpy(cursor
, report
->message().c_str(), messageSize
);
174 cursor
+= messageSize
;
177 if (report
->filename
) {
178 copy
->filename
= JS::ConstUTF8CharsZ((const char*)cursor
);
179 js_memcpy(cursor
, report
->filename
.c_str(), filenameSize
);
180 cursor
+= filenameSize
;
183 if (!CopyExtraData(cx
, &cursor
, copy
.get(), report
)) {
187 MOZ_ASSERT(cursor
== (uint8_t*)copy
.get() + mallocSize
);
189 // errorMessageName should be static.
190 copy
->errorMessageName
= report
->errorMessageName
;
192 /* Copy non-pointer members. */
193 copy
->sourceId
= report
->sourceId
;
194 copy
->lineno
= report
->lineno
;
195 copy
->column
= report
->column
;
196 copy
->errorNumber
= report
->errorNumber
;
201 UniquePtr
<JSErrorNotes::Note
> js::CopyErrorNote(JSContext
* cx
,
202 JSErrorNotes::Note
* note
) {
203 return CopyErrorHelper(cx
, note
);
206 UniquePtr
<JSErrorReport
> js::CopyErrorReport(JSContext
* cx
,
207 JSErrorReport
* report
) {
208 return CopyErrorHelper(cx
, report
);
211 struct SuppressErrorsGuard
{
213 JS::WarningReporter prevReporter
;
214 JS::AutoSaveExceptionState prevState
;
216 explicit SuppressErrorsGuard(JSContext
* cx
)
218 prevReporter(JS::SetWarningReporter(cx
, nullptr)),
221 ~SuppressErrorsGuard() { JS::SetWarningReporter(cx
, prevReporter
); }
224 // Cut off the stack if it gets too deep (most commonly for infinite recursion
226 static const size_t MAX_REPORTED_STACK_DEPTH
= 1u << 7;
228 bool js::CaptureStack(JSContext
* cx
, MutableHandleObject stack
) {
229 return CaptureCurrentStack(
230 cx
, stack
, JS::StackCapture(JS::MaxFrames(MAX_REPORTED_STACK_DEPTH
)));
233 JSString
* js::ComputeStackString(JSContext
* cx
) {
234 SuppressErrorsGuard
seg(cx
);
236 RootedObject
stack(cx
);
237 if (!CaptureStack(cx
, &stack
)) {
241 RootedString
str(cx
);
242 if (!BuildStackString(cx
, cx
->realm()->principals(), stack
, &str
)) {
249 JSErrorReport
* js::ErrorFromException(JSContext
* cx
, HandleObject objArg
) {
250 // It's ok to UncheckedUnwrap here, since all we do is get the
251 // JSErrorReport, and consumers are careful with the information they get
252 // from that anyway. Anyone doing things that would expose anything in the
253 // JSErrorReport to page script either does a security check on the
254 // JSErrorReport's principal or also tries to do toString on our object and
255 // will fail if they can't unwrap it.
256 RootedObject
obj(cx
, UncheckedUnwrap(objArg
));
257 if (!obj
->is
<ErrorObject
>()) {
261 JSErrorReport
* report
= obj
->as
<ErrorObject
>().getOrCreateErrorReport(cx
);
263 MOZ_ASSERT(cx
->isThrowingOutOfMemory());
264 cx
->recoverFromOutOfMemory();
270 JS_PUBLIC_API JSObject
* JS::ExceptionStackOrNull(HandleObject objArg
) {
271 ErrorObject
* errorObject
= objArg
->maybeUnwrapIf
<ErrorObject
>();
273 return errorObject
->stack();
276 WasmExceptionObject
* wasmObject
=
277 objArg
->maybeUnwrapIf
<WasmExceptionObject
>();
279 return wasmObject
->stack();
285 JS_PUBLIC_API JSLinearString
* js::GetErrorTypeName(JSContext
* cx
,
288 * JSEXN_INTERNALERR returns null to prevent that "InternalError: "
289 * is prepended before "uncaught exception: "
291 if (exnType
< 0 || exnType
>= JSEXN_LIMIT
|| exnType
== JSEXN_INTERNALERR
||
292 exnType
== JSEXN_WARN
|| exnType
== JSEXN_NOTE
) {
295 JSProtoKey key
= GetExceptionProtoKey(JSExnType(exnType
));
296 return ClassName(key
, cx
);
299 bool js::ErrorToException(JSContext
* cx
, JSErrorReport
* reportp
,
300 JSErrorCallback callback
, void* userRef
) {
301 MOZ_ASSERT(!reportp
->isWarning());
303 // Find the exception index associated with this error.
304 JSErrNum errorNumber
= static_cast<JSErrNum
>(reportp
->errorNumber
);
306 callback
= GetErrorMessage
;
308 const JSErrorFormatString
* errorString
= callback(userRef
, errorNumber
);
310 errorString
? static_cast<JSExnType
>(errorString
->exnType
) : JSEXN_ERR
;
311 MOZ_ASSERT(exnType
< JSEXN_ERROR_LIMIT
);
313 // Prevent infinite recursion.
314 if (cx
->generatingError
) {
318 cx
->generatingError
= true;
319 auto restore
= mozilla::MakeScopeExit([cx
] { cx
->generatingError
= false; });
321 // Create an exception object.
322 RootedString
messageStr(cx
, reportp
->newMessageString(cx
));
327 Rooted
<JSString
*> fileName(cx
);
328 if (const char* filename
= reportp
->filename
.c_str()) {
330 JS_NewStringCopyUTF8N(cx
, JS::UTF8Chars(filename
, strlen(filename
)));
335 fileName
= cx
->emptyString();
338 uint32_t sourceId
= reportp
->sourceId
;
339 uint32_t lineNumber
= reportp
->lineno
;
340 JS::ColumnNumberOneOrigin columnNumber
= reportp
->column
;
342 // Error reports don't provide a |cause|, so we default to |Nothing| here.
343 auto cause
= JS::NothingHandleValue
;
345 RootedObject
stack(cx
);
346 if (!CaptureStack(cx
, &stack
)) {
350 UniquePtr
<JSErrorReport
> report
= CopyErrorReport(cx
, reportp
);
355 ErrorObject
* errObject
=
356 ErrorObject::create(cx
, exnType
, stack
, fileName
, sourceId
, lineNumber
,
357 columnNumber
, std::move(report
), messageStr
, cause
);
363 RootedValue
errValue(cx
, ObjectValue(*errObject
));
364 Rooted
<SavedFrame
*> nstack(cx
);
366 nstack
= &stack
->as
<SavedFrame
>();
368 cx
->setPendingException(errValue
, nstack
);
372 using SniffingBehavior
= JS::ErrorReportBuilder::SniffingBehavior
;
374 static bool IsDuckTypedErrorObject(JSContext
* cx
, HandleObject exnObject
,
375 const char** filename_strp
) {
377 * This function is called from ErrorReport::init and so should not generate
378 * any new exceptions.
380 AutoClearPendingException
acpe(cx
);
383 if (!JS_HasProperty(cx
, exnObject
, "message", &found
) || !found
) {
387 // First try "filename".
388 const char* filename_str
= *filename_strp
;
389 if (!JS_HasProperty(cx
, exnObject
, filename_str
, &found
)) {
393 // If that doesn't work, try "fileName".
394 filename_str
= "fileName";
395 if (!JS_HasProperty(cx
, exnObject
, filename_str
, &found
) || !found
) {
400 if (!JS_HasProperty(cx
, exnObject
, "lineNumber", &found
) || !found
) {
404 *filename_strp
= filename_str
;
408 static bool GetPropertyNoException(JSContext
* cx
, HandleObject obj
,
409 SniffingBehavior behavior
,
410 Handle
<PropertyName
*> name
,
411 MutableHandleValue vp
) {
412 // This function has no side-effects so always use it.
413 if (GetPropertyPure(cx
, obj
, NameToId(name
), vp
.address())) {
417 if (behavior
== SniffingBehavior::WithSideEffects
) {
418 AutoClearPendingException
acpe(cx
);
419 return GetProperty(cx
, obj
, obj
, name
, vp
);
425 // Create a new error message similar to what Error.prototype.toString would
426 // produce when called on an object with those property values for name and
428 static JSString
* FormatErrorMessage(JSContext
* cx
, HandleString name
,
429 HandleString message
) {
430 if (name
&& message
) {
431 AutoClearPendingException
acpe(cx
);
432 JSStringBuilder
sb(cx
);
434 // Prefix the message with the error type, if it exists.
435 if (!sb
.append(name
) || !sb
.append(": ") || !sb
.append(message
)) {
439 return sb
.finishString();
442 return name
? name
: message
;
445 static JSString
* ErrorReportToString(JSContext
* cx
, HandleObject exn
,
446 JSErrorReport
* reportp
,
447 SniffingBehavior behavior
) {
448 // The error object might have custom `name` overwriting the exnType in the
449 // error report. Try getting that property and use the exnType as a fallback.
450 RootedString
name(cx
);
451 RootedValue
nameV(cx
);
452 if (GetPropertyNoException(cx
, exn
, behavior
, cx
->names().name
, &nameV
) &&
454 name
= nameV
.toString();
457 // We do NOT want to use GetErrorTypeName() here because it will not do the
458 // "right thing" for JSEXN_INTERNALERR. That is, the caller of this API
459 // expects that "InternalError: " will be prepended but GetErrorTypeName
460 // goes out of its way to avoid this.
462 JSExnType type
= static_cast<JSExnType
>(reportp
->exnType
);
463 if (type
!= JSEXN_WARN
&& type
!= JSEXN_NOTE
) {
464 name
= ClassName(GetExceptionProtoKey(type
), cx
);
468 RootedString
message(cx
);
469 RootedValue
messageV(cx
);
470 if (GetPropertyNoException(cx
, exn
, behavior
, cx
->names().message
,
472 messageV
.isString()) {
473 message
= messageV
.toString();
477 message
= reportp
->newMessageString(cx
);
483 return FormatErrorMessage(cx
, name
, message
);
486 JS::ErrorReportBuilder::ErrorReportBuilder(JSContext
* cx
)
487 : reportp(nullptr), exnObject(cx
) {}
489 JS::ErrorReportBuilder::~ErrorReportBuilder() = default;
491 bool JS::ErrorReportBuilder::init(JSContext
* cx
,
492 const JS::ExceptionStack
& exnStack
,
493 SniffingBehavior sniffingBehavior
) {
494 MOZ_ASSERT(!cx
->isExceptionPending());
495 MOZ_ASSERT(!reportp
);
497 if (exnStack
.exception().isObject()) {
498 // Because ToString below could error and an exception object could become
499 // unrooted, we must root our exception object, if any.
500 exnObject
= &exnStack
.exception().toObject();
501 reportp
= ErrorFromException(cx
, exnObject
);
504 // Be careful not to invoke ToString if we've already successfully extracted
505 // an error report, since the exception might be wrapped in a security
506 // wrapper, and ToString-ing it might throw.
507 RootedString
str(cx
);
509 str
= ErrorReportToString(cx
, exnObject
, reportp
, sniffingBehavior
);
510 } else if (exnStack
.exception().isSymbol()) {
511 RootedValue
strVal(cx
);
512 if (js::SymbolDescriptiveString(cx
, exnStack
.exception().toSymbol(),
514 str
= strVal
.toString();
518 } else if (exnObject
&& sniffingBehavior
== NoSideEffects
) {
519 str
= cx
->names().Object
;
521 str
= js::ToString
<CanGC
>(cx
, exnStack
.exception());
525 cx
->clearPendingException();
528 // If ErrorFromException didn't get us a JSErrorReport, then the object
529 // was not an ErrorObject, security-wrapped or otherwise. However, it might
530 // still quack like one. Give duck-typing a chance. We start by looking for
531 // "filename" (all lowercase), since that's where DOMExceptions store their
532 // filename. Then we check "fileName", which is where Errors store it. We
533 // have to do it in that order, because DOMExceptions have Error.prototype
534 // on their proto chain, and hence also have a "fileName" property, but its
536 const char* filename_str
= "filename";
537 if (!reportp
&& exnObject
&& sniffingBehavior
== WithSideEffects
&&
538 IsDuckTypedErrorObject(cx
, exnObject
, &filename_str
)) {
539 // Temporary value for pulling properties off of duck-typed objects.
542 RootedString
name(cx
);
543 if (JS_GetProperty(cx
, exnObject
, "name", &val
) && val
.isString()) {
544 name
= val
.toString();
546 cx
->clearPendingException();
549 RootedString
msg(cx
);
550 if (JS_GetProperty(cx
, exnObject
, "message", &val
) && val
.isString()) {
551 msg
= val
.toString();
553 cx
->clearPendingException();
556 // If we have the right fields, override the ToString we performed on
557 // the exception object above with something built out of its quacks
558 // (i.e. as much of |NameQuack: MessageQuack| as we can make).
559 str
= FormatErrorMessage(cx
, name
, msg
);
562 AutoClearPendingException
acpe(cx
);
563 if (JS_GetProperty(cx
, exnObject
, filename_str
, &val
)) {
564 RootedString
tmp(cx
, js::ToString
<CanGC
>(cx
, val
));
566 filename
= JS_EncodeStringToUTF8(cx
, tmp
);
571 filename
= DuplicateString("");
573 ReportOutOfMemory(cx
);
579 if (!JS_GetProperty(cx
, exnObject
, "lineNumber", &val
) ||
580 !ToUint32(cx
, val
, &lineno
)) {
581 cx
->clearPendingException();
586 if (!JS_GetProperty(cx
, exnObject
, "columnNumber", &val
) ||
587 !ToUint32(cx
, val
, &column
)) {
588 cx
->clearPendingException();
592 reportp
= &ownedReport
;
593 new (reportp
) JSErrorReport();
594 ownedReport
.filename
= JS::ConstUTF8CharsZ(filename
.get());
595 ownedReport
.lineno
= lineno
;
596 ownedReport
.exnType
= JSEXN_INTERNALERR
;
597 ownedReport
.column
= JS::ColumnNumberOneOrigin(column
);
600 // Note that using |str| for |message_| here is kind of wrong,
601 // because |str| is supposed to be of the format
602 // |ErrorName: ErrorMessage|, and |message_| is supposed to
603 // correspond to |ErrorMessage|. But this is what we've
604 // historically done for duck-typed error objects.
606 // If only this stuff could get specced one day...
607 if (auto utf8
= JS_EncodeStringToUTF8(cx
, str
)) {
608 ownedReport
.initOwnedMessage(utf8
.release());
610 cx
->clearPendingException();
616 const char* utf8Message
= nullptr;
618 toStringResultBytesStorage
= JS_EncodeStringToUTF8(cx
, str
);
619 utf8Message
= toStringResultBytesStorage
.get();
621 cx
->clearPendingException();
625 utf8Message
= "unknown (can't convert to string)";
629 // This is basically an inlined version of
631 // JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
632 // JSMSG_UNCAUGHT_EXCEPTION, utf8Message);
634 // but without the reporting bits. Instead it just puts all
635 // the stuff we care about in our ownedReport and message_.
636 if (!populateUncaughtExceptionReportUTF8(cx
, exnStack
.stack(),
638 // Just give up. We're out of memory or something; not much we can
643 toStringResult_
= JS::ConstUTF8CharsZ(utf8Message
, strlen(utf8Message
));
649 bool JS::ErrorReportBuilder::populateUncaughtExceptionReportUTF8(
650 JSContext
* cx
, HandleObject stack
, ...) {
653 bool ok
= populateUncaughtExceptionReportUTF8VA(cx
, stack
, ap
);
658 bool JS::ErrorReportBuilder::populateUncaughtExceptionReportUTF8VA(
659 JSContext
* cx
, HandleObject stack
, va_list ap
) {
660 new (&ownedReport
) JSErrorReport();
661 ownedReport
.isWarning_
= false;
662 ownedReport
.errorNumber
= JSMSG_UNCAUGHT_EXCEPTION
;
665 Rooted
<SavedFrame
*> frame(
666 cx
, UnwrapSavedFrame(cx
, cx
->realm()->principals(), stack
,
667 SavedFrameSelfHosted::Exclude
, skippedAsync
));
669 filename
= StringToNewUTF8CharsZ(cx
, *frame
->getSource());
674 // |ownedReport.filename| inherits the lifetime of |ErrorReport::filename|.
675 ownedReport
.filename
= JS::ConstUTF8CharsZ(filename
.get());
676 ownedReport
.sourceId
= frame
->getSourceId();
677 ownedReport
.lineno
= frame
->getLine();
679 JS::ColumnNumberOneOrigin(frame
->getColumn().oneOriginValue());
680 ownedReport
.isMuted
= frame
->getMutedErrors();
682 // XXXbz this assumes the stack we have right now is still
683 // related to our exception object.
684 NonBuiltinFrameIter
iter(cx
, cx
->realm()->principals());
686 ownedReport
.filename
= JS::ConstUTF8CharsZ(iter
.filename());
687 JS::TaggedColumnNumberZeroOrigin column
;
688 ownedReport
.sourceId
=
689 iter
.hasScript() ? iter
.script()->scriptSource()->id() : 0;
690 ownedReport
.lineno
= iter
.computeLine(&column
);
691 ownedReport
.column
= JS::ColumnNumberOneOrigin(column
.oneOriginValue());
692 ownedReport
.isMuted
= iter
.mutedErrors();
696 AutoReportFrontendContext
fc(cx
);
697 if (!ExpandErrorArgumentsVA(&fc
, GetErrorMessage
, nullptr,
698 JSMSG_UNCAUGHT_EXCEPTION
, ArgumentsAreUTF8
,
703 toStringResult_
= ownedReport
.message();
704 reportp
= &ownedReport
;
708 JSObject
* js::CopyErrorObject(JSContext
* cx
, Handle
<ErrorObject
*> err
) {
709 UniquePtr
<JSErrorReport
> copyReport
;
710 if (JSErrorReport
* errorReport
= err
->getErrorReport()) {
711 copyReport
= CopyErrorReport(cx
, errorReport
);
717 RootedString
message(cx
, err
->getMessage());
718 if (message
&& !cx
->compartment()->wrap(cx
, &message
)) {
721 RootedString
fileName(cx
, err
->fileName(cx
));
722 if (!cx
->compartment()->wrap(cx
, &fileName
)) {
725 RootedObject
stack(cx
, err
->stack());
726 if (!cx
->compartment()->wrap(cx
, &stack
)) {
729 if (stack
&& JS_IsDeadWrapper(stack
)) {
730 // ErrorObject::create expects |stack| to be either nullptr or a (possibly
731 // wrapped) SavedFrame instance.
734 Rooted
<mozilla::Maybe
<Value
>> cause(cx
, mozilla::Nothing());
735 if (auto maybeCause
= err
->getCause()) {
736 RootedValue
errorCause(cx
, maybeCause
.value());
737 if (!cx
->compartment()->wrap(cx
, &errorCause
)) {
740 cause
= mozilla::Some(errorCause
.get());
742 uint32_t sourceId
= err
->sourceId();
743 uint32_t lineNumber
= err
->lineNumber();
744 JS::ColumnNumberOneOrigin columnNumber
= err
->columnNumber();
745 JSExnType errorType
= err
->type();
747 // Create the Error object.
748 return ErrorObject::create(cx
, errorType
, stack
, fileName
, sourceId
,
749 lineNumber
, columnNumber
, std::move(copyReport
),
753 JS_PUBLIC_API
bool JS::CreateError(JSContext
* cx
, JSExnType type
,
754 HandleObject stack
, HandleString fileName
,
756 JS::ColumnNumberOneOrigin columnNumber
,
757 JSErrorReport
* report
, HandleString message
,
758 Handle
<mozilla::Maybe
<Value
>> cause
,
759 MutableHandleValue rval
) {
760 cx
->check(stack
, fileName
, message
);
761 AssertObjectIsSavedFrameOrWrapper(cx
, stack
);
763 js::UniquePtr
<JSErrorReport
> rep
;
765 rep
= CopyErrorReport(cx
, report
);
772 js::ErrorObject::create(cx
, type
, stack
, fileName
, 0, lineNumber
,
773 columnNumber
, std::move(rep
), message
, cause
);
778 rval
.setObject(*obj
);
782 const char* js::ValueToSourceForError(JSContext
* cx
, HandleValue val
,
783 UniqueChars
& bytes
) {
784 if (val
.isUndefined()) {
792 AutoClearPendingException
acpe(cx
);
794 RootedString
str(cx
, JS_ValueToSource(cx
, val
));
796 return "<<error converting value to string>>";
799 JSStringBuilder
sb(cx
);
800 if (val
.hasObjectPayload()) {
801 RootedObject
valObj(cx
, &val
.getObjectPayload());
803 if (!JS::GetBuiltinClass(cx
, valObj
, &cls
)) {
804 return "<<error determining class of value>>";
807 if (cls
== ESClass::Array
) {
809 } else if (cls
== ESClass::ArrayBuffer
) {
810 s
= "the array buffer ";
811 } else if (JS_IsArrayBufferViewObject(valObj
)) {
812 s
= "the typed array ";
813 #ifdef ENABLE_RECORD_TUPLE
814 } else if (cls
== ESClass::Record
) {
816 } else if (cls
== ESClass::Tuple
) {
822 if (!sb
.append(s
, strlen(s
))) {
823 return "<<error converting value to string>>";
825 } else if (val
.isNumber()) {
826 if (!sb
.append("the number ")) {
827 return "<<error converting value to string>>";
829 } else if (val
.isString()) {
830 if (!sb
.append("the string ")) {
831 return "<<error converting value to string>>";
833 } else if (val
.isBigInt()) {
834 if (!sb
.append("the BigInt ")) {
835 return "<<error converting value to string>>";
838 MOZ_ASSERT(val
.isBoolean() || val
.isSymbol());
839 bytes
= StringToNewUTF8CharsZ(cx
, *str
);
842 if (!sb
.append(str
)) {
843 return "<<error converting value to string>>";
845 str
= sb
.finishString();
847 return "<<error converting value to string>>";
849 bytes
= StringToNewUTF8CharsZ(cx
, *str
);
853 bool js::GetInternalError(JSContext
* cx
, unsigned errorNumber
,
854 MutableHandleValue error
) {
855 FixedInvokeArgs
<1> args(cx
);
856 args
[0].set(Int32Value(errorNumber
));
857 return CallSelfHostedFunction(cx
, cx
->names().GetInternalError
,
858 NullHandleValue
, args
, error
);
861 bool js::GetTypeError(JSContext
* cx
, unsigned errorNumber
,
862 MutableHandleValue error
) {
863 FixedInvokeArgs
<1> args(cx
);
864 args
[0].set(Int32Value(errorNumber
));
865 return CallSelfHostedFunction(cx
, cx
->names().GetTypeError
, NullHandleValue
,
869 bool js::GetAggregateError(JSContext
* cx
, unsigned errorNumber
,
870 MutableHandleValue error
) {
871 FixedInvokeArgs
<1> args(cx
);
872 args
[0].set(Int32Value(errorNumber
));
873 return CallSelfHostedFunction(cx
, cx
->names().GetAggregateError
,
874 NullHandleValue
, args
, error
);
877 JS_PUBLIC_API
mozilla::Maybe
<Value
> JS::GetExceptionCause(JSObject
* exc
) {
878 if (!exc
->is
<ErrorObject
>()) {
879 return mozilla::Nothing();
881 auto& error
= exc
->as
<ErrorObject
>();
882 return error
.getCause();