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"
31 #include "js/Conversions.h"
32 #include "js/ErrorReport.h" // JS::PrintError
33 #include "js/Exception.h" // JS::ExceptionStack
34 #include "js/experimental/TypedData.h" // JS_IsArrayBufferViewObject
35 #include "js/friend/ErrorMessages.h" // JSErrNum, js::GetErrorMessage, JSMSG_*
36 #include "js/Object.h" // JS::GetBuiltinClass
37 #include "js/PropertyAndElement.h" // JS_GetProperty, JS_HasProperty
38 #include "js/SavedFrameAPI.h"
40 #include "js/UniquePtr.h"
42 #include "js/Warnings.h" // JS::{,Set}WarningReporter
43 #include "js/Wrapper.h"
44 #include "util/Memory.h"
45 #include "util/StringBuffer.h"
46 #include "vm/Compartment.h"
47 #include "vm/ErrorObject.h"
48 #include "vm/FrameIter.h" // js::NonBuiltinFrameIter
49 #include "vm/JSAtom.h"
50 #include "vm/JSContext.h"
51 #include "vm/JSObject.h"
52 #include "vm/JSScript.h"
54 #include "vm/SavedFrame.h"
55 #include "vm/SavedStacks.h"
56 #include "vm/SelfHosting.h"
58 #include "vm/StringType.h"
59 #include "vm/SymbolType.h"
60 #include "vm/WellKnownAtom.h" // js_*_str
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
= report
->filename
? strlen(report
->filename
) + 1 : 0;
151 size_t messageSize
= 0;
152 if (report
->message()) {
153 messageSize
= strlen(report
->message().c_str()) + 1;
157 * The mallocSize can not overflow since it represents the sum of the
158 * sizes of already allocated objects.
161 sizeof(T
) + messageSize
+ filenameSize
+ ExtraMallocSize(report
);
162 uint8_t* cursor
= cx
->pod_calloc
<uint8_t>(mallocSize
);
167 UniquePtr
<T
> copy(new (cursor
) T());
170 if (report
->message()) {
171 copy
->initBorrowedMessage((const char*)cursor
);
172 js_memcpy(cursor
, report
->message().c_str(), messageSize
);
173 cursor
+= messageSize
;
176 if (report
->filename
) {
177 copy
->filename
= (const char*)cursor
;
178 js_memcpy(cursor
, report
->filename
, filenameSize
);
179 cursor
+= filenameSize
;
182 if (!CopyExtraData(cx
, &cursor
, copy
.get(), report
)) {
186 MOZ_ASSERT(cursor
== (uint8_t*)copy
.get() + mallocSize
);
188 // errorMessageName should be static.
189 copy
->errorMessageName
= report
->errorMessageName
;
191 /* Copy non-pointer members. */
192 copy
->sourceId
= report
->sourceId
;
193 copy
->lineno
= report
->lineno
;
194 copy
->column
= report
->column
;
195 copy
->errorNumber
= report
->errorNumber
;
200 UniquePtr
<JSErrorNotes::Note
> js::CopyErrorNote(JSContext
* cx
,
201 JSErrorNotes::Note
* note
) {
202 return CopyErrorHelper(cx
, note
);
205 UniquePtr
<JSErrorReport
> js::CopyErrorReport(JSContext
* cx
,
206 JSErrorReport
* report
) {
207 return CopyErrorHelper(cx
, report
);
210 struct SuppressErrorsGuard
{
212 JS::WarningReporter prevReporter
;
213 JS::AutoSaveExceptionState prevState
;
215 explicit SuppressErrorsGuard(JSContext
* cx
)
217 prevReporter(JS::SetWarningReporter(cx
, nullptr)),
220 ~SuppressErrorsGuard() { JS::SetWarningReporter(cx
, prevReporter
); }
223 // Cut off the stack if it gets too deep (most commonly for infinite recursion
225 static const size_t MAX_REPORTED_STACK_DEPTH
= 1u << 7;
227 bool js::CaptureStack(JSContext
* cx
, MutableHandleObject stack
) {
228 return CaptureCurrentStack(
229 cx
, stack
, JS::StackCapture(JS::MaxFrames(MAX_REPORTED_STACK_DEPTH
)));
232 JSString
* js::ComputeStackString(JSContext
* cx
) {
233 SuppressErrorsGuard
seg(cx
);
235 RootedObject
stack(cx
);
236 if (!CaptureStack(cx
, &stack
)) {
240 RootedString
str(cx
);
241 if (!BuildStackString(cx
, cx
->realm()->principals(), stack
, &str
)) {
248 JSErrorReport
* js::ErrorFromException(JSContext
* cx
, HandleObject objArg
) {
249 // It's ok to UncheckedUnwrap here, since all we do is get the
250 // JSErrorReport, and consumers are careful with the information they get
251 // from that anyway. Anyone doing things that would expose anything in the
252 // JSErrorReport to page script either does a security check on the
253 // JSErrorReport's principal or also tries to do toString on our object and
254 // will fail if they can't unwrap it.
255 RootedObject
obj(cx
, UncheckedUnwrap(objArg
));
256 if (!obj
->is
<ErrorObject
>()) {
260 JSErrorReport
* report
= obj
->as
<ErrorObject
>().getOrCreateErrorReport(cx
);
262 MOZ_ASSERT(cx
->isThrowingOutOfMemory());
263 cx
->recoverFromOutOfMemory();
269 JS_PUBLIC_API JSObject
* JS::ExceptionStackOrNull(HandleObject objArg
) {
270 ErrorObject
* errorObject
= objArg
->maybeUnwrapIf
<ErrorObject
>();
272 return errorObject
->stack();
275 WasmExceptionObject
* wasmObject
=
276 objArg
->maybeUnwrapIf
<WasmExceptionObject
>();
278 return wasmObject
->stack();
284 JS_PUBLIC_API JSLinearString
* js::GetErrorTypeName(JSContext
* cx
,
287 * JSEXN_INTERNALERR returns null to prevent that "InternalError: "
288 * is prepended before "uncaught exception: "
290 if (exnType
< 0 || exnType
>= JSEXN_LIMIT
|| exnType
== JSEXN_INTERNALERR
||
291 exnType
== JSEXN_WARN
|| exnType
== JSEXN_NOTE
) {
294 JSProtoKey key
= GetExceptionProtoKey(JSExnType(exnType
));
295 return ClassName(key
, cx
);
298 void js::ErrorToException(JSContext
* cx
, JSErrorReport
* reportp
,
299 JSErrorCallback callback
, void* userRef
) {
300 MOZ_ASSERT(!reportp
->isWarning());
302 // Find the exception index associated with this error.
303 JSErrNum errorNumber
= static_cast<JSErrNum
>(reportp
->errorNumber
);
305 callback
= GetErrorMessage
;
307 const JSErrorFormatString
* errorString
= callback(userRef
, errorNumber
);
309 errorString
? static_cast<JSExnType
>(errorString
->exnType
) : JSEXN_ERR
;
310 MOZ_ASSERT(exnType
< JSEXN_ERROR_LIMIT
);
312 // Prevent infinite recursion.
313 if (cx
->generatingError
) {
317 cx
->generatingError
= true;
318 auto restore
= mozilla::MakeScopeExit([cx
] { cx
->generatingError
= false; });
320 // Create an exception object.
321 RootedString
messageStr(cx
, reportp
->newMessageString(cx
));
326 RootedString
fileName(cx
, JS_NewStringCopyZ(cx
, reportp
->filename
));
331 uint32_t sourceId
= reportp
->sourceId
;
332 uint32_t lineNumber
= reportp
->lineno
;
333 uint32_t columnNumber
= reportp
->column
;
335 // Error reports don't provide a |cause|, so we default to |Nothing| here.
336 auto cause
= JS::NothingHandleValue
;
338 RootedObject
stack(cx
);
339 if (!CaptureStack(cx
, &stack
)) {
343 UniquePtr
<JSErrorReport
> report
= CopyErrorReport(cx
, reportp
);
348 ErrorObject
* errObject
=
349 ErrorObject::create(cx
, exnType
, stack
, fileName
, sourceId
, lineNumber
,
350 columnNumber
, std::move(report
), messageStr
, cause
);
356 RootedValue
errValue(cx
, ObjectValue(*errObject
));
357 Rooted
<SavedFrame
*> nstack(cx
);
359 nstack
= &stack
->as
<SavedFrame
>();
361 cx
->setPendingException(errValue
, nstack
);
364 using SniffingBehavior
= JS::ErrorReportBuilder::SniffingBehavior
;
366 static bool IsDuckTypedErrorObject(JSContext
* cx
, HandleObject exnObject
,
367 const char** filename_strp
) {
369 * This function is called from ErrorReport::init and so should not generate
370 * any new exceptions.
372 AutoClearPendingException
acpe(cx
);
375 if (!JS_HasProperty(cx
, exnObject
, js_message_str
, &found
) || !found
) {
379 // First try "filename".
380 const char* filename_str
= *filename_strp
;
381 if (!JS_HasProperty(cx
, exnObject
, filename_str
, &found
)) {
385 // If that doesn't work, try "fileName".
386 filename_str
= js_fileName_str
;
387 if (!JS_HasProperty(cx
, exnObject
, filename_str
, &found
) || !found
) {
392 if (!JS_HasProperty(cx
, exnObject
, js_lineNumber_str
, &found
) || !found
) {
396 *filename_strp
= filename_str
;
400 static bool GetPropertyNoException(JSContext
* cx
, HandleObject obj
,
401 SniffingBehavior behavior
,
402 Handle
<PropertyName
*> name
,
403 MutableHandleValue vp
) {
404 // This function has no side-effects so always use it.
405 if (GetPropertyPure(cx
, obj
, NameToId(name
), vp
.address())) {
409 if (behavior
== SniffingBehavior::WithSideEffects
) {
410 AutoClearPendingException
acpe(cx
);
411 return GetProperty(cx
, obj
, obj
, name
, vp
);
417 // Create a new error message similar to what Error.prototype.toString would
418 // produce when called on an object with those property values for name and
420 static JSString
* FormatErrorMessage(JSContext
* cx
, HandleString name
,
421 HandleString message
) {
422 if (name
&& message
) {
423 AutoClearPendingException
acpe(cx
);
424 JSStringBuilder
sb(cx
);
426 // Prefix the message with the error type, if it exists.
427 if (!sb
.append(name
) || !sb
.append(": ") || !sb
.append(message
)) {
431 return sb
.finishString();
434 return name
? name
: message
;
437 static JSString
* ErrorReportToString(JSContext
* cx
, HandleObject exn
,
438 JSErrorReport
* reportp
,
439 SniffingBehavior behavior
) {
440 // The error object might have custom `name` overwriting the exnType in the
441 // error report. Try getting that property and use the exnType as a fallback.
442 RootedString
name(cx
);
443 RootedValue
nameV(cx
);
444 if (GetPropertyNoException(cx
, exn
, behavior
, cx
->names().name
, &nameV
) &&
446 name
= nameV
.toString();
449 // We do NOT want to use GetErrorTypeName() here because it will not do the
450 // "right thing" for JSEXN_INTERNALERR. That is, the caller of this API
451 // expects that "InternalError: " will be prepended but GetErrorTypeName
452 // goes out of its way to avoid this.
454 JSExnType type
= static_cast<JSExnType
>(reportp
->exnType
);
455 if (type
!= JSEXN_WARN
&& type
!= JSEXN_NOTE
) {
456 name
= ClassName(GetExceptionProtoKey(type
), cx
);
460 RootedString
message(cx
);
461 RootedValue
messageV(cx
);
462 if (GetPropertyNoException(cx
, exn
, behavior
, cx
->names().message
,
464 messageV
.isString()) {
465 message
= messageV
.toString();
469 message
= reportp
->newMessageString(cx
);
475 return FormatErrorMessage(cx
, name
, message
);
478 JS::ErrorReportBuilder::ErrorReportBuilder(JSContext
* cx
)
479 : reportp(nullptr), exnObject(cx
) {}
481 JS::ErrorReportBuilder::~ErrorReportBuilder() = default;
483 bool JS::ErrorReportBuilder::init(JSContext
* cx
,
484 const JS::ExceptionStack
& exnStack
,
485 SniffingBehavior sniffingBehavior
) {
486 MOZ_ASSERT(!cx
->isExceptionPending());
487 MOZ_ASSERT(!reportp
);
489 if (exnStack
.exception().isObject()) {
490 // Because ToString below could error and an exception object could become
491 // unrooted, we must root our exception object, if any.
492 exnObject
= &exnStack
.exception().toObject();
493 reportp
= ErrorFromException(cx
, exnObject
);
496 // Be careful not to invoke ToString if we've already successfully extracted
497 // an error report, since the exception might be wrapped in a security
498 // wrapper, and ToString-ing it might throw.
499 RootedString
str(cx
);
501 str
= ErrorReportToString(cx
, exnObject
, reportp
, sniffingBehavior
);
502 } else if (exnStack
.exception().isSymbol()) {
503 RootedValue
strVal(cx
);
504 if (js::SymbolDescriptiveString(cx
, exnStack
.exception().toSymbol(),
506 str
= strVal
.toString();
510 } else if (exnObject
&& sniffingBehavior
== NoSideEffects
) {
511 str
= cx
->names().Object
;
513 str
= js::ToString
<CanGC
>(cx
, exnStack
.exception());
517 cx
->clearPendingException();
520 // If ErrorFromException didn't get us a JSErrorReport, then the object
521 // was not an ErrorObject, security-wrapped or otherwise. However, it might
522 // still quack like one. Give duck-typing a chance. We start by looking for
523 // "filename" (all lowercase), since that's where DOMExceptions store their
524 // filename. Then we check "fileName", which is where Errors store it. We
525 // have to do it in that order, because DOMExceptions have Error.prototype
526 // on their proto chain, and hence also have a "fileName" property, but its
528 const char* filename_str
= "filename";
529 if (!reportp
&& exnObject
&& sniffingBehavior
== WithSideEffects
&&
530 IsDuckTypedErrorObject(cx
, exnObject
, &filename_str
)) {
531 // Temporary value for pulling properties off of duck-typed objects.
534 RootedString
name(cx
);
535 if (JS_GetProperty(cx
, exnObject
, js_name_str
, &val
) && val
.isString()) {
536 name
= val
.toString();
538 cx
->clearPendingException();
541 RootedString
msg(cx
);
542 if (JS_GetProperty(cx
, exnObject
, js_message_str
, &val
) && val
.isString()) {
543 msg
= val
.toString();
545 cx
->clearPendingException();
548 // If we have the right fields, override the ToString we performed on
549 // the exception object above with something built out of its quacks
550 // (i.e. as much of |NameQuack: MessageQuack| as we can make).
551 str
= FormatErrorMessage(cx
, name
, msg
);
554 AutoClearPendingException
acpe(cx
);
555 if (JS_GetProperty(cx
, exnObject
, filename_str
, &val
)) {
556 RootedString
tmp(cx
, js::ToString
<CanGC
>(cx
, val
));
558 filename
= JS_EncodeStringToUTF8(cx
, tmp
);
563 filename
= DuplicateString("");
565 ReportOutOfMemory(cx
);
571 if (!JS_GetProperty(cx
, exnObject
, js_lineNumber_str
, &val
) ||
572 !ToUint32(cx
, val
, &lineno
)) {
573 cx
->clearPendingException();
578 if (!JS_GetProperty(cx
, exnObject
, js_columnNumber_str
, &val
) ||
579 !ToUint32(cx
, val
, &column
)) {
580 cx
->clearPendingException();
584 reportp
= &ownedReport
;
585 new (reportp
) JSErrorReport();
586 ownedReport
.filename
= filename
.get();
587 ownedReport
.lineno
= lineno
;
588 ownedReport
.exnType
= JSEXN_INTERNALERR
;
589 ownedReport
.column
= column
;
592 // Note that using |str| for |message_| here is kind of wrong,
593 // because |str| is supposed to be of the format
594 // |ErrorName: ErrorMessage|, and |message_| is supposed to
595 // correspond to |ErrorMessage|. But this is what we've
596 // historically done for duck-typed error objects.
598 // If only this stuff could get specced one day...
599 if (auto utf8
= JS_EncodeStringToUTF8(cx
, str
)) {
600 ownedReport
.initOwnedMessage(utf8
.release());
602 cx
->clearPendingException();
608 const char* utf8Message
= nullptr;
610 toStringResultBytesStorage
= JS_EncodeStringToUTF8(cx
, str
);
611 utf8Message
= toStringResultBytesStorage
.get();
613 cx
->clearPendingException();
617 utf8Message
= "unknown (can't convert to string)";
621 // This is basically an inlined version of
623 // JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
624 // JSMSG_UNCAUGHT_EXCEPTION, utf8Message);
626 // but without the reporting bits. Instead it just puts all
627 // the stuff we care about in our ownedReport and message_.
628 if (!populateUncaughtExceptionReportUTF8(cx
, exnStack
.stack(),
630 // Just give up. We're out of memory or something; not much we can
635 toStringResult_
= JS::ConstUTF8CharsZ(utf8Message
, strlen(utf8Message
));
641 bool JS::ErrorReportBuilder::populateUncaughtExceptionReportUTF8(
642 JSContext
* cx
, HandleObject stack
, ...) {
645 bool ok
= populateUncaughtExceptionReportUTF8VA(cx
, stack
, ap
);
650 bool JS::ErrorReportBuilder::populateUncaughtExceptionReportUTF8VA(
651 JSContext
* cx
, HandleObject stack
, va_list ap
) {
652 new (&ownedReport
) JSErrorReport();
653 ownedReport
.isWarning_
= false;
654 ownedReport
.errorNumber
= JSMSG_UNCAUGHT_EXCEPTION
;
657 Rooted
<SavedFrame
*> frame(
658 cx
, UnwrapSavedFrame(cx
, cx
->realm()->principals(), stack
,
659 SavedFrameSelfHosted::Exclude
, skippedAsync
));
661 filename
= StringToNewUTF8CharsZ(cx
, *frame
->getSource());
666 // |ownedReport.filename| inherits the lifetime of |ErrorReport::filename|.
667 ownedReport
.filename
= filename
.get();
668 ownedReport
.sourceId
= frame
->getSourceId();
669 ownedReport
.lineno
= frame
->getLine();
670 // Follow FixupColumnForDisplay and set column to 1 for WASM.
671 ownedReport
.column
= frame
->isWasm() ? 1 : frame
->getColumn();
672 ownedReport
.isMuted
= frame
->getMutedErrors();
674 // XXXbz this assumes the stack we have right now is still
675 // related to our exception object.
676 NonBuiltinFrameIter
iter(cx
, cx
->realm()->principals());
678 ownedReport
.filename
= iter
.filename();
680 ownedReport
.sourceId
=
681 iter
.hasScript() ? iter
.script()->scriptSource()->id() : 0;
682 ownedReport
.lineno
= iter
.computeLine(&column
);
683 ownedReport
.column
= FixupColumnForDisplay(column
);
684 ownedReport
.isMuted
= iter
.mutedErrors();
688 AutoReportFrontendContext
fc(cx
);
689 if (!ExpandErrorArgumentsVA(&fc
, GetErrorMessage
, nullptr,
690 JSMSG_UNCAUGHT_EXCEPTION
, ArgumentsAreUTF8
,
695 toStringResult_
= ownedReport
.message();
696 reportp
= &ownedReport
;
700 JSObject
* js::CopyErrorObject(JSContext
* cx
, Handle
<ErrorObject
*> err
) {
701 UniquePtr
<JSErrorReport
> copyReport
;
702 if (JSErrorReport
* errorReport
= err
->getErrorReport()) {
703 copyReport
= CopyErrorReport(cx
, errorReport
);
709 RootedString
message(cx
, err
->getMessage());
710 if (message
&& !cx
->compartment()->wrap(cx
, &message
)) {
713 RootedString
fileName(cx
, err
->fileName(cx
));
714 if (!cx
->compartment()->wrap(cx
, &fileName
)) {
717 RootedObject
stack(cx
, err
->stack());
718 if (!cx
->compartment()->wrap(cx
, &stack
)) {
721 if (stack
&& JS_IsDeadWrapper(stack
)) {
722 // ErrorObject::create expects |stack| to be either nullptr or a (possibly
723 // wrapped) SavedFrame instance.
726 Rooted
<mozilla::Maybe
<Value
>> cause(cx
, mozilla::Nothing());
727 if (auto maybeCause
= err
->getCause()) {
728 RootedValue
errorCause(cx
, maybeCause
.value());
729 if (!cx
->compartment()->wrap(cx
, &errorCause
)) {
732 cause
= mozilla::Some(errorCause
.get());
734 uint32_t sourceId
= err
->sourceId();
735 uint32_t lineNumber
= err
->lineNumber();
736 uint32_t columnNumber
= err
->columnNumber();
737 JSExnType errorType
= err
->type();
739 // Create the Error object.
740 return ErrorObject::create(cx
, errorType
, stack
, fileName
, sourceId
,
741 lineNumber
, columnNumber
, std::move(copyReport
),
745 JS_PUBLIC_API
bool JS::CreateError(JSContext
* cx
, JSExnType type
,
746 HandleObject stack
, HandleString fileName
,
747 uint32_t lineNumber
, uint32_t columnNumber
,
748 JSErrorReport
* report
, HandleString message
,
749 Handle
<mozilla::Maybe
<Value
>> cause
,
750 MutableHandleValue rval
) {
751 cx
->check(stack
, fileName
, message
);
752 AssertObjectIsSavedFrameOrWrapper(cx
, stack
);
754 js::UniquePtr
<JSErrorReport
> rep
;
756 rep
= CopyErrorReport(cx
, report
);
763 js::ErrorObject::create(cx
, type
, stack
, fileName
, 0, lineNumber
,
764 columnNumber
, std::move(rep
), message
, cause
);
769 rval
.setObject(*obj
);
773 const char* js::ValueToSourceForError(JSContext
* cx
, HandleValue val
,
774 UniqueChars
& bytes
) {
775 if (val
.isUndefined()) {
783 AutoClearPendingException
acpe(cx
);
785 RootedString
str(cx
, JS_ValueToSource(cx
, val
));
787 return "<<error converting value to string>>";
790 JSStringBuilder
sb(cx
);
791 if (val
.hasObjectPayload()) {
792 RootedObject
valObj(cx
, &val
.getObjectPayload());
794 if (!JS::GetBuiltinClass(cx
, valObj
, &cls
)) {
795 return "<<error determining class of value>>";
798 if (cls
== ESClass::Array
) {
800 } else if (cls
== ESClass::ArrayBuffer
) {
801 s
= "the array buffer ";
802 } else if (JS_IsArrayBufferViewObject(valObj
)) {
803 s
= "the typed array ";
804 #ifdef ENABLE_RECORD_TUPLE
805 } else if (cls
== ESClass::Record
) {
807 } else if (cls
== ESClass::Tuple
) {
813 if (!sb
.append(s
, strlen(s
))) {
814 return "<<error converting value to string>>";
816 } else if (val
.isNumber()) {
817 if (!sb
.append("the number ")) {
818 return "<<error converting value to string>>";
820 } else if (val
.isString()) {
821 if (!sb
.append("the string ")) {
822 return "<<error converting value to string>>";
824 } else if (val
.isBigInt()) {
825 if (!sb
.append("the BigInt ")) {
826 return "<<error converting value to string>>";
829 MOZ_ASSERT(val
.isBoolean() || val
.isSymbol());
830 bytes
= StringToNewUTF8CharsZ(cx
, *str
);
833 if (!sb
.append(str
)) {
834 return "<<error converting value to string>>";
836 str
= sb
.finishString();
838 return "<<error converting value to string>>";
840 bytes
= StringToNewUTF8CharsZ(cx
, *str
);
844 bool js::GetInternalError(JSContext
* cx
, unsigned errorNumber
,
845 MutableHandleValue error
) {
846 FixedInvokeArgs
<1> args(cx
);
847 args
[0].set(Int32Value(errorNumber
));
848 return CallSelfHostedFunction(cx
, cx
->names().GetInternalError
,
849 NullHandleValue
, args
, error
);
852 bool js::GetTypeError(JSContext
* cx
, unsigned errorNumber
,
853 MutableHandleValue error
) {
854 FixedInvokeArgs
<1> args(cx
);
855 args
[0].set(Int32Value(errorNumber
));
856 return CallSelfHostedFunction(cx
, cx
->names().GetTypeError
, NullHandleValue
,
860 bool js::GetAggregateError(JSContext
* cx
, unsigned errorNumber
,
861 MutableHandleValue error
) {
862 FixedInvokeArgs
<1> args(cx
);
863 args
[0].set(Int32Value(errorNumber
));
864 return CallSelfHostedFunction(cx
, cx
->names().GetAggregateError
,
865 NullHandleValue
, args
, error
);
868 JS_PUBLIC_API
mozilla::Maybe
<Value
> JS::GetExceptionCause(JSObject
* exc
) {
869 if (!exc
->is
<ErrorObject
>()) {
870 return mozilla::Nothing();
872 auto& error
= exc
->as
<ErrorObject
>();
873 return error
.getCause();