1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sw=2 et tw=80:
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "vm/ErrorObject-inl.h"
10 #include "mozilla/Assertions.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/DebugOnly.h"
13 #include "mozilla/Maybe.h"
19 #include "NamespaceImports.h"
21 #include "gc/AllocKind.h"
22 #include "gc/GCContext.h"
23 #include "js/CallArgs.h"
24 #include "js/CallNonGenericMethod.h"
25 #include "js/CharacterEncoding.h" // JS::ConstUTF8CharsZ
27 #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
28 #include "js/Conversions.h"
29 #include "js/ErrorReport.h"
30 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
31 #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
32 #include "js/PropertySpec.h"
33 #include "js/RootingAPI.h"
35 #include "js/TypeDecls.h"
36 #include "js/Utility.h"
38 #include "js/Wrapper.h"
39 #include "util/StringBuffer.h"
40 #include "vm/GlobalObject.h"
41 #include "vm/Iteration.h"
42 #include "vm/JSAtomUtils.h" // ClassName
43 #include "vm/JSFunction.h"
44 #include "vm/JSObject.h"
45 #include "vm/NativeObject.h"
46 #include "vm/ObjectOperations.h"
47 #include "vm/SavedStacks.h"
48 #include "vm/SelfHosting.h"
51 #include "vm/StringType.h"
52 #include "vm/ToSource.h" // js::ValueToSource
54 #include "vm/JSContext-inl.h"
55 #include "vm/JSObject-inl.h"
56 #include "vm/ObjectOperations-inl.h"
57 #include "vm/SavedStacks-inl.h"
58 #include "vm/Shape-inl.h"
62 #define IMPLEMENT_ERROR_PROTO_CLASS(name) \
63 {#name ".prototype", JSCLASS_HAS_CACHED_PROTO(JSProto_##name), \
65 &ErrorObject::classSpecs[JSProto_##name - JSProto_Error]}
67 const JSClass
ErrorObject::protoClasses
[JSEXN_ERROR_LIMIT
] = {
68 IMPLEMENT_ERROR_PROTO_CLASS(Error
),
70 IMPLEMENT_ERROR_PROTO_CLASS(InternalError
),
71 IMPLEMENT_ERROR_PROTO_CLASS(AggregateError
),
72 IMPLEMENT_ERROR_PROTO_CLASS(EvalError
),
73 IMPLEMENT_ERROR_PROTO_CLASS(RangeError
),
74 IMPLEMENT_ERROR_PROTO_CLASS(ReferenceError
),
75 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
76 IMPLEMENT_ERROR_PROTO_CLASS(SuppressedError
),
78 IMPLEMENT_ERROR_PROTO_CLASS(SyntaxError
),
79 IMPLEMENT_ERROR_PROTO_CLASS(TypeError
),
80 IMPLEMENT_ERROR_PROTO_CLASS(URIError
),
82 IMPLEMENT_ERROR_PROTO_CLASS(DebuggeeWouldRun
),
83 IMPLEMENT_ERROR_PROTO_CLASS(CompileError
),
84 IMPLEMENT_ERROR_PROTO_CLASS(LinkError
),
85 IMPLEMENT_ERROR_PROTO_CLASS(RuntimeError
)};
87 static bool exn_toSource(JSContext
* cx
, unsigned argc
, Value
* vp
);
89 static const JSFunctionSpec error_methods
[] = {
90 JS_FN("toSource", exn_toSource
, 0, 0),
91 JS_SELF_HOSTED_FN("toString", "ErrorToString", 0, 0), JS_FS_END
};
93 // Error.prototype and NativeError.prototype have own .message and .name
95 #define COMMON_ERROR_PROPERTIES(name) \
96 JS_STRING_PS("message", "", 0), JS_STRING_PS("name", #name, 0)
98 static const JSPropertySpec error_properties
[] = {
99 COMMON_ERROR_PROPERTIES(Error
),
100 // Only Error.prototype has .stack!
101 JS_PSGS("stack", ErrorObject::getStack
, ErrorObject::setStack
, 0),
104 #define IMPLEMENT_NATIVE_ERROR_PROPERTIES(name) \
105 static const JSPropertySpec name##_properties[] = { \
106 COMMON_ERROR_PROPERTIES(name), JS_PS_END};
108 IMPLEMENT_NATIVE_ERROR_PROPERTIES(InternalError
)
109 IMPLEMENT_NATIVE_ERROR_PROPERTIES(AggregateError
)
110 IMPLEMENT_NATIVE_ERROR_PROPERTIES(EvalError
)
111 IMPLEMENT_NATIVE_ERROR_PROPERTIES(RangeError
)
112 IMPLEMENT_NATIVE_ERROR_PROPERTIES(ReferenceError
)
113 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
114 IMPLEMENT_NATIVE_ERROR_PROPERTIES(SuppressedError
)
116 IMPLEMENT_NATIVE_ERROR_PROPERTIES(SyntaxError
)
117 IMPLEMENT_NATIVE_ERROR_PROPERTIES(TypeError
)
118 IMPLEMENT_NATIVE_ERROR_PROPERTIES(URIError
)
119 IMPLEMENT_NATIVE_ERROR_PROPERTIES(DebuggeeWouldRun
)
120 IMPLEMENT_NATIVE_ERROR_PROPERTIES(CompileError
)
121 IMPLEMENT_NATIVE_ERROR_PROPERTIES(LinkError
)
122 IMPLEMENT_NATIVE_ERROR_PROPERTIES(RuntimeError
)
124 #define IMPLEMENT_NATIVE_ERROR_SPEC(name) \
125 {ErrorObject::createConstructor, \
126 ErrorObject::createProto, \
134 #define IMPLEMENT_NONGLOBAL_ERROR_SPEC(name) \
135 {ErrorObject::createConstructor, \
136 ErrorObject::createProto, \
142 JSProto_Error | ClassSpec::DontDefineConstructor}
144 const ClassSpec
ErrorObject::classSpecs
[JSEXN_ERROR_LIMIT
] = {
145 {ErrorObject::createConstructor
, ErrorObject::createProto
, nullptr, nullptr,
146 error_methods
, error_properties
},
148 IMPLEMENT_NATIVE_ERROR_SPEC(InternalError
),
149 IMPLEMENT_NATIVE_ERROR_SPEC(AggregateError
),
150 IMPLEMENT_NATIVE_ERROR_SPEC(EvalError
),
151 IMPLEMENT_NATIVE_ERROR_SPEC(RangeError
),
152 IMPLEMENT_NATIVE_ERROR_SPEC(ReferenceError
),
153 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
154 IMPLEMENT_NATIVE_ERROR_SPEC(SuppressedError
),
156 IMPLEMENT_NATIVE_ERROR_SPEC(SyntaxError
),
157 IMPLEMENT_NATIVE_ERROR_SPEC(TypeError
),
158 IMPLEMENT_NATIVE_ERROR_SPEC(URIError
),
160 IMPLEMENT_NONGLOBAL_ERROR_SPEC(DebuggeeWouldRun
),
161 IMPLEMENT_NONGLOBAL_ERROR_SPEC(CompileError
),
162 IMPLEMENT_NONGLOBAL_ERROR_SPEC(LinkError
),
163 IMPLEMENT_NONGLOBAL_ERROR_SPEC(RuntimeError
)};
165 #define IMPLEMENT_ERROR_CLASS_CORE(name, reserved_slots) \
167 JSCLASS_HAS_CACHED_PROTO(JSProto_##name) | \
168 JSCLASS_HAS_RESERVED_SLOTS(reserved_slots) | \
169 JSCLASS_BACKGROUND_FINALIZE, \
170 &ErrorObjectClassOps, \
171 &ErrorObject::classSpecs[JSProto_##name - JSProto_Error]}
173 #define IMPLEMENT_ERROR_CLASS(name) \
174 IMPLEMENT_ERROR_CLASS_CORE(name, ErrorObject::RESERVED_SLOTS)
176 // Only used for classes that could be a Wasm trap. Classes that use this
177 // macro should be kept in sync with the exception types that mightBeWasmTrap()
178 // will return true for.
179 #define IMPLEMENT_ERROR_CLASS_MAYBE_WASM_TRAP(name) \
180 IMPLEMENT_ERROR_CLASS_CORE(name, ErrorObject::RESERVED_SLOTS_MAYBE_WASM_TRAP)
182 static void exn_finalize(JS::GCContext
* gcx
, JSObject
* obj
);
184 static const JSClassOps ErrorObjectClassOps
= {
185 nullptr, // addProperty
186 nullptr, // delProperty
187 nullptr, // enumerate
188 nullptr, // newEnumerate
190 nullptr, // mayResolve
191 exn_finalize
, // finalize
193 nullptr, // construct
197 const JSClass
ErrorObject::classes
[JSEXN_ERROR_LIMIT
] = {
198 IMPLEMENT_ERROR_CLASS(Error
),
199 IMPLEMENT_ERROR_CLASS_MAYBE_WASM_TRAP(InternalError
),
200 IMPLEMENT_ERROR_CLASS(AggregateError
), IMPLEMENT_ERROR_CLASS(EvalError
),
201 IMPLEMENT_ERROR_CLASS(RangeError
), IMPLEMENT_ERROR_CLASS(ReferenceError
),
202 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
203 IMPLEMENT_ERROR_CLASS(SuppressedError
),
205 IMPLEMENT_ERROR_CLASS(SyntaxError
), IMPLEMENT_ERROR_CLASS(TypeError
),
206 IMPLEMENT_ERROR_CLASS(URIError
),
207 // These Error subclasses are not accessible via the global object:
208 IMPLEMENT_ERROR_CLASS(DebuggeeWouldRun
),
209 IMPLEMENT_ERROR_CLASS(CompileError
), IMPLEMENT_ERROR_CLASS(LinkError
),
210 IMPLEMENT_ERROR_CLASS_MAYBE_WASM_TRAP(RuntimeError
)};
212 static void exn_finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
213 if (JSErrorReport
* report
= obj
->as
<ErrorObject
>().getErrorReport()) {
214 // Bug 1560019: This allocation is not currently tracked.
215 gcx
->deleteUntracked(report
);
219 static ErrorObject
* CreateErrorObject(JSContext
* cx
, const CallArgs
& args
,
220 unsigned messageArg
, JSExnType exnType
,
221 HandleObject proto
) {
222 // Compute the error message, if any.
223 RootedString
message(cx
, nullptr);
224 if (args
.hasDefined(messageArg
)) {
225 message
= ToString
<CanGC
>(cx
, args
[messageArg
]);
231 // Don't interpret the two parameters following the message parameter as the
232 // non-standard fileName and lineNumber arguments when we have an options
234 bool hasOptions
= args
.get(messageArg
+ 1).isObject();
236 Rooted
<mozilla::Maybe
<Value
>> cause(cx
, mozilla::Nothing());
238 RootedObject
options(cx
, &args
[messageArg
+ 1].toObject());
240 bool hasCause
= false;
241 if (!HasProperty(cx
, options
, cx
->names().cause
, &hasCause
)) {
246 RootedValue
causeValue(cx
);
247 if (!GetProperty(cx
, options
, options
, cx
->names().cause
, &causeValue
)) {
250 cause
= mozilla::Some(causeValue
.get());
254 // Find the scripted caller, but only ones we're allowed to know about.
255 NonBuiltinFrameIter
iter(cx
, cx
->realm()->principals());
257 RootedString
fileName(cx
);
258 uint32_t sourceId
= 0;
259 if (!hasOptions
&& args
.length() > messageArg
+ 1) {
260 fileName
= ToString
<CanGC
>(cx
, args
[messageArg
+ 1]);
262 fileName
= cx
->runtime()->emptyString
;
264 if (const char* cfilename
= iter
.filename()) {
265 fileName
= JS_NewStringCopyUTF8Z(
266 cx
, JS::ConstUTF8CharsZ(cfilename
, strlen(cfilename
)));
268 if (iter
.hasScript()) {
269 sourceId
= iter
.script()->scriptSource()->id();
278 JS::ColumnNumberOneOrigin columnNumber
;
279 if (!hasOptions
&& args
.length() > messageArg
+ 2) {
280 if (!ToUint32(cx
, args
[messageArg
+ 2], &lineNumber
)) {
284 JS::TaggedColumnNumberOneOrigin tmp
;
285 lineNumber
= iter
.done() ? 0 : iter
.computeLine(&tmp
);
286 columnNumber
= JS::ColumnNumberOneOrigin(tmp
.oneOriginValue());
289 RootedObject
stack(cx
);
290 if (!CaptureStack(cx
, &stack
)) {
294 return ErrorObject::create(cx
, exnType
, stack
, fileName
, sourceId
, lineNumber
,
295 columnNumber
, nullptr, message
, cause
, proto
);
298 static bool Error(JSContext
* cx
, unsigned argc
, Value
* vp
) {
299 CallArgs args
= CallArgsFromVp(argc
, vp
);
301 // ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
302 // called as functions, without operator new. But as we do not give
303 // each constructor a distinct JSClass, we must get the exception type
306 JSExnType(args
.callee().as
<JSFunction
>().getExtendedSlot(0).toInt32());
308 MOZ_ASSERT(exnType
!= JSEXN_AGGREGATEERR
,
309 "AggregateError has its own constructor function");
311 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
312 MOZ_ASSERT(exnType
!= JSEXN_SUPPRESSEDERR
,
313 "SuppressedError has its own constuctor function");
316 JSProtoKey protoKey
=
317 JSCLASS_CACHED_PROTO_KEY(&ErrorObject::classes
[exnType
]);
319 // ES6 19.5.1.1 mandates the .prototype lookup happens before the toString
320 RootedObject
proto(cx
);
321 if (!GetPrototypeFromBuiltinConstructor(cx
, args
, protoKey
, &proto
)) {
325 auto* obj
= CreateErrorObject(cx
, args
, 0, exnType
, proto
);
330 args
.rval().setObject(*obj
);
334 // AggregateError ( errors, message )
335 static bool AggregateError(JSContext
* cx
, unsigned argc
, Value
* vp
) {
336 CallArgs args
= CallArgsFromVp(argc
, vp
);
338 mozilla::DebugOnly
<JSExnType
> exnType
=
339 JSExnType(args
.callee().as
<JSFunction
>().getExtendedSlot(0).toInt32());
341 MOZ_ASSERT(exnType
== JSEXN_AGGREGATEERR
);
343 // Steps 1-2. (9.1.13 OrdinaryCreateFromConstructor, steps 1-2).
344 RootedObject
proto(cx
);
345 if (!GetPrototypeFromBuiltinConstructor(cx
, args
, JSProto_AggregateError
,
350 // TypeError anyway, but this gives a better error message.
351 if (!args
.requireAtLeast(cx
, "AggregateError", 1)) {
355 // 9.1.13 OrdinaryCreateFromConstructor, step 3.
357 Rooted
<ErrorObject
*> obj(
358 cx
, CreateErrorObject(cx
, args
, 1, JSEXN_AGGREGATEERR
, proto
));
365 Rooted
<ArrayObject
*> errorsList(cx
);
366 if (!IterableToArray(cx
, args
.get(0), &errorsList
)) {
371 RootedValue
errorsVal(cx
, JS::ObjectValue(*errorsList
));
372 if (!NativeDefineDataProperty(cx
, obj
, cx
->names().errors
, errorsVal
, 0)) {
377 args
.rval().setObject(*obj
);
381 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
382 // Explicit Resource Management Proposal
383 // SuppressedError ( error, suppressed, message )
384 // https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-suppressederror
385 static bool SuppressedError(JSContext
* cx
, unsigned argc
, Value
* vp
) {
386 CallArgs args
= CallArgsFromVp(argc
, vp
);
388 mozilla::DebugOnly
<JSExnType
> exnType
=
389 JSExnType(args
.callee().as
<JSFunction
>().getExtendedSlot(0).toInt32());
391 MOZ_ASSERT(exnType
== JSEXN_SUPPRESSEDERR
);
393 // Step 1. If NewTarget is undefined, let newTarget be the active function
394 // object; else let newTarget be NewTarget.
395 // Step 2. Let O be ? OrdinaryCreateFromConstructor(newTarget,
396 // "%SuppressedError.prototype%", « [[ErrorData]] »).
397 JS::Rooted
<JSObject
*> proto(cx
);
399 if (!GetPrototypeFromBuiltinConstructor(cx
, args
, JSProto_SuppressedError
,
404 // Step 3. If message is not undefined, then
405 // Step 3.a. Let messageString be ? ToString(message).
406 // Step 3.b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message",
408 JS::Rooted
<ErrorObject
*> obj(
409 cx
, CreateErrorObject(cx
, args
, 2, JSEXN_SUPPRESSEDERR
, proto
));
415 // Step 4. Perform CreateNonEnumerableDataPropertyOrThrow(O, "error", error).
416 JS::Rooted
<JS::Value
> errorVal(cx
, args
.get(0));
417 if (!NativeDefineDataProperty(cx
, obj
, cx
->names().error
, errorVal
, 0)) {
421 // Step 5. Perform CreateNonEnumerableDataPropertyOrThrow(O, "suppressed",
423 JS::Rooted
<JS::Value
> suppressedVal(cx
, args
.get(1));
424 if (!NativeDefineDataProperty(cx
, obj
, cx
->names().suppressed
, suppressedVal
,
430 args
.rval().setObject(*obj
);
436 JSObject
* ErrorObject::createProto(JSContext
* cx
, JSProtoKey key
) {
437 JSExnType type
= ExnTypeFromProtoKey(key
);
439 if (type
== JSEXN_ERR
) {
440 return GlobalObject::createBlankPrototype(
441 cx
, cx
->global(), &ErrorObject::protoClasses
[JSEXN_ERR
]);
444 RootedObject
protoProto(
445 cx
, GlobalObject::getOrCreateErrorPrototype(cx
, cx
->global()));
450 return GlobalObject::createBlankPrototypeInheriting(
451 cx
, &ErrorObject::protoClasses
[type
], protoProto
);
455 JSObject
* ErrorObject::createConstructor(JSContext
* cx
, JSProtoKey key
) {
456 JSExnType type
= ExnTypeFromProtoKey(key
);
457 RootedObject
ctor(cx
);
459 if (type
== JSEXN_ERR
) {
460 ctor
= GenericCreateConstructor
<Error
, 1, gc::AllocKind::FUNCTION_EXTENDED
>(
463 RootedFunction
proto(
464 cx
, GlobalObject::getOrCreateErrorConstructor(cx
, cx
->global()));
471 if (type
== JSEXN_AGGREGATEERR
) {
472 native
= AggregateError
;
475 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
476 else if (type
== JSEXN_SUPPRESSEDERR
) {
477 native
= SuppressedError
;
487 NewFunctionWithProto(cx
, native
, nargs
, FunctionFlags::NATIVE_CTOR
,
488 nullptr, ClassName(key
, cx
), proto
,
489 gc::AllocKind::FUNCTION_EXTENDED
, TenuredObject
);
496 ctor
->as
<JSFunction
>().setExtendedSlot(0, Int32Value(type
));
501 SharedShape
* js::ErrorObject::assignInitialShape(JSContext
* cx
,
502 Handle
<ErrorObject
*> obj
) {
503 MOZ_ASSERT(obj
->empty());
505 constexpr PropertyFlags propFlags
= {PropertyFlag::Configurable
,
506 PropertyFlag::Writable
};
508 if (!NativeObject::addPropertyInReservedSlot(cx
, obj
, cx
->names().fileName
,
509 FILENAME_SLOT
, propFlags
)) {
513 if (!NativeObject::addPropertyInReservedSlot(cx
, obj
, cx
->names().lineNumber
,
514 LINENUMBER_SLOT
, propFlags
)) {
518 if (!NativeObject::addPropertyInReservedSlot(
519 cx
, obj
, cx
->names().columnNumber
, COLUMNNUMBER_SLOT
, propFlags
)) {
523 return obj
->sharedShape();
527 bool js::ErrorObject::init(JSContext
* cx
, Handle
<ErrorObject
*> obj
,
528 JSExnType type
, UniquePtr
<JSErrorReport
> errorReport
,
529 HandleString fileName
, HandleObject stack
,
530 uint32_t sourceId
, uint32_t lineNumber
,
531 JS::ColumnNumberOneOrigin columnNumber
,
532 HandleString message
,
533 Handle
<mozilla::Maybe
<JS::Value
>> cause
) {
534 MOZ_ASSERT(JSEXN_ERR
<= type
&& type
< JSEXN_ERROR_LIMIT
);
535 AssertObjectIsSavedFrameOrWrapper(cx
, stack
);
536 cx
->check(obj
, stack
);
538 // Null out early in case of error, for exn_finalize's sake.
539 obj
->initReservedSlot(ERROR_REPORT_SLOT
, PrivateValue(nullptr));
541 if (!SharedShape::ensureInitialCustomShape
<ErrorObject
>(cx
, obj
)) {
545 // The .message property isn't part of the initial shape because it's
546 // present in some error objects -- |Error.prototype|, |new Error("f")|,
547 // |new Error("")| -- but not in others -- |new Error(undefined)|,
550 constexpr PropertyFlags propFlags
= {PropertyFlag::Configurable
,
551 PropertyFlag::Writable
};
552 if (!NativeObject::addPropertyInReservedSlot(cx
, obj
, cx
->names().message
,
553 MESSAGE_SLOT
, propFlags
)) {
558 // Similar to the .message property, .cause is present only in some error
559 // objects -- |new Error("f", {cause: cause})| -- but not in other --
560 // |Error.prototype|, |new Error()|, |new Error("f")|.
561 if (cause
.isSome()) {
562 constexpr PropertyFlags propFlags
= {PropertyFlag::Configurable
,
563 PropertyFlag::Writable
};
564 if (!NativeObject::addPropertyInReservedSlot(cx
, obj
, cx
->names().cause
,
565 CAUSE_SLOT
, propFlags
)) {
570 MOZ_ASSERT(obj
->lookupPure(NameToId(cx
->names().fileName
))->slot() ==
572 MOZ_ASSERT(obj
->lookupPure(NameToId(cx
->names().lineNumber
))->slot() ==
574 MOZ_ASSERT(obj
->lookupPure(NameToId(cx
->names().columnNumber
))->slot() ==
578 obj
->lookupPure(NameToId(cx
->names().message
))->slot() == MESSAGE_SLOT
);
581 obj
->lookupPure(NameToId(cx
->names().cause
))->slot() == CAUSE_SLOT
);
583 JSErrorReport
* report
= errorReport
.release();
584 obj
->initReservedSlot(STACK_SLOT
, ObjectOrNullValue(stack
));
585 obj
->setReservedSlot(ERROR_REPORT_SLOT
, PrivateValue(report
));
586 obj
->initReservedSlot(FILENAME_SLOT
, StringValue(fileName
));
587 obj
->initReservedSlot(LINENUMBER_SLOT
, Int32Value(lineNumber
));
588 obj
->initReservedSlot(COLUMNNUMBER_SLOT
,
589 Int32Value(columnNumber
.oneOriginValue()));
591 obj
->initReservedSlot(MESSAGE_SLOT
, StringValue(message
));
593 if (cause
.isSome()) {
594 obj
->initReservedSlot(CAUSE_SLOT
, *cause
.get());
596 obj
->initReservedSlot(CAUSE_SLOT
, MagicValue(JS_ERROR_WITHOUT_CAUSE
));
598 obj
->initReservedSlot(SOURCEID_SLOT
, Int32Value(sourceId
));
599 if (obj
->mightBeWasmTrap()) {
600 MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(obj
->getClass()) > WASM_TRAP_SLOT
);
601 obj
->initReservedSlot(WASM_TRAP_SLOT
, BooleanValue(false));
608 ErrorObject
* js::ErrorObject::create(JSContext
* cx
, JSExnType errorType
,
609 HandleObject stack
, HandleString fileName
,
610 uint32_t sourceId
, uint32_t lineNumber
,
611 JS::ColumnNumberOneOrigin columnNumber
,
612 UniquePtr
<JSErrorReport
> report
,
613 HandleString message
,
614 Handle
<mozilla::Maybe
<JS::Value
>> cause
,
615 HandleObject protoArg
/* = nullptr */) {
616 AssertObjectIsSavedFrameOrWrapper(cx
, stack
);
618 RootedObject
proto(cx
, protoArg
);
620 proto
= GlobalObject::getOrCreateCustomErrorPrototype(cx
, cx
->global(),
627 Rooted
<ErrorObject
*> errObject(cx
);
629 const JSClass
* clasp
= ErrorObject::classForType(errorType
);
630 JSObject
* obj
= NewObjectWithGivenProto(cx
, clasp
, proto
);
634 errObject
= &obj
->as
<ErrorObject
>();
637 if (!ErrorObject::init(cx
, errObject
, errorType
, std::move(report
), fileName
,
638 stack
, sourceId
, lineNumber
, columnNumber
, message
,
646 JSErrorReport
* js::ErrorObject::getOrCreateErrorReport(JSContext
* cx
) {
647 if (JSErrorReport
* r
= getErrorReport()) {
651 // We build an error report on the stack and then use CopyErrorReport to do
652 // the nitty-gritty malloc stuff.
653 JSErrorReport report
;
656 JSExnType type_
= type();
657 report
.exnType
= type_
;
660 RootedString
filename(cx
, fileName(cx
));
661 UniqueChars filenameStr
= JS_EncodeStringToUTF8(cx
, filename
);
665 report
.filename
= JS::ConstUTF8CharsZ(filenameStr
.get());
668 report
.sourceId
= sourceId();
669 report
.lineno
= lineNumber();
670 report
.column
= columnNumber();
672 // Message. Note that |new Error()| will result in an undefined |message|
673 // slot, so we need to explicitly substitute the empty string in that case.
674 RootedString
message(cx
, getMessage());
676 message
= cx
->runtime()->emptyString
;
679 UniqueChars utf8
= StringToNewUTF8CharsZ(cx
, *message
);
683 report
.initOwnedMessage(utf8
.release());
686 UniquePtr
<JSErrorReport
> copy
= CopyErrorReport(cx
, &report
);
690 setReservedSlot(ERROR_REPORT_SLOT
, PrivateValue(copy
.get()));
691 return copy
.release();
694 static bool FindErrorInstanceOrPrototype(JSContext
* cx
, HandleObject obj
,
695 MutableHandleObject result
) {
696 // Walk up the prototype chain until we find an error object instance or
697 // prototype object. This allows code like:
698 // Object.create(Error.prototype).stack
700 // function NYI() { }
701 // NYI.prototype = new Error;
703 // to continue returning stacks that are useless, but at least don't throw.
705 RootedObject
curr(cx
, obj
);
706 RootedObject
target(cx
);
708 target
= CheckedUnwrapStatic(curr
);
710 ReportAccessDenied(cx
);
713 if (IsErrorProtoKey(StandardProtoKeyOrNull(target
))) {
718 if (!GetPrototype(cx
, curr
, &curr
)) {
723 // We walked the whole prototype chain and did not find an Error
725 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
726 JSMSG_INCOMPATIBLE_PROTO
, "Error", "(get stack)",
727 obj
->getClass()->name
);
731 static MOZ_ALWAYS_INLINE
bool IsObject(HandleValue v
) { return v
.isObject(); }
734 bool js::ErrorObject::getStack(JSContext
* cx
, unsigned argc
, Value
* vp
) {
735 CallArgs args
= CallArgsFromVp(argc
, vp
);
736 // We accept any object here, because of poor-man's subclassing of Error.
737 return CallNonGenericMethod
<IsObject
, getStack_impl
>(cx
, args
);
741 bool js::ErrorObject::getStack_impl(JSContext
* cx
, const CallArgs
& args
) {
742 RootedObject
thisObj(cx
, &args
.thisv().toObject());
744 RootedObject
obj(cx
);
745 if (!FindErrorInstanceOrPrototype(cx
, thisObj
, &obj
)) {
749 if (!obj
->is
<ErrorObject
>()) {
750 args
.rval().setString(cx
->runtime()->emptyString
);
754 // Do frame filtering based on the ErrorObject's principals. This ensures we
755 // don't see chrome frames when chrome code accesses .stack over Xrays.
756 JSPrincipals
* principals
= obj
->as
<ErrorObject
>().realm()->principals();
758 RootedObject
savedFrameObj(cx
, obj
->as
<ErrorObject
>().stack());
759 RootedString
stackString(cx
);
760 if (!BuildStackString(cx
, principals
, savedFrameObj
, &stackString
)) {
764 if (cx
->runtime()->stackFormat() == js::StackFormat::V8
) {
765 // When emulating V8 stack frames, we also need to prepend the
766 // stringified Error to the stack string.
767 Handle
<PropertyName
*> name
= cx
->names().ErrorToStringWithTrailingNewline
;
768 FixedInvokeArgs
<0> args2(cx
);
769 RootedValue
rval(cx
);
770 if (!CallSelfHostedFunction(cx
, name
, args
.thisv(), args2
, &rval
)) {
774 if (!rval
.isString()) {
775 args
.rval().setString(cx
->runtime()->emptyString
);
779 RootedString
stringified(cx
, rval
.toString());
780 stackString
= ConcatStrings
<CanGC
>(cx
, stringified
, stackString
);
783 args
.rval().setString(stackString
);
788 bool js::ErrorObject::setStack(JSContext
* cx
, unsigned argc
, Value
* vp
) {
789 CallArgs args
= CallArgsFromVp(argc
, vp
);
790 // We accept any object here, because of poor-man's subclassing of Error.
791 return CallNonGenericMethod
<IsObject
, setStack_impl
>(cx
, args
);
795 bool js::ErrorObject::setStack_impl(JSContext
* cx
, const CallArgs
& args
) {
796 RootedObject
thisObj(cx
, &args
.thisv().toObject());
798 if (!args
.requireAtLeast(cx
, "(set stack)", 1)) {
801 RootedValue
val(cx
, args
[0]);
803 return DefineDataProperty(cx
, thisObj
, cx
->names().stack
, val
);
806 void js::ErrorObject::setFromWasmTrap() {
807 MOZ_ASSERT(mightBeWasmTrap());
808 MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(getClass()) > WASM_TRAP_SLOT
);
809 setReservedSlot(WASM_TRAP_SLOT
, BooleanValue(true));
812 JSString
* js::ErrorToSource(JSContext
* cx
, HandleObject obj
) {
813 RootedValue
nameVal(cx
);
814 RootedString
name(cx
);
815 if (!GetProperty(cx
, obj
, obj
, cx
->names().name
, &nameVal
) ||
816 !(name
= ToString
<CanGC
>(cx
, nameVal
))) {
820 RootedValue
messageVal(cx
);
821 RootedString
message(cx
);
822 if (!GetProperty(cx
, obj
, obj
, cx
->names().message
, &messageVal
) ||
823 !(message
= ValueToSource(cx
, messageVal
))) {
827 RootedValue
filenameVal(cx
);
828 RootedString
filename(cx
);
829 if (!GetProperty(cx
, obj
, obj
, cx
->names().fileName
, &filenameVal
) ||
830 !(filename
= ValueToSource(cx
, filenameVal
))) {
834 RootedValue
linenoVal(cx
);
836 if (!GetProperty(cx
, obj
, obj
, cx
->names().lineNumber
, &linenoVal
) ||
837 !ToUint32(cx
, linenoVal
, &lineno
)) {
841 JSStringBuilder
sb(cx
);
842 if (!sb
.append("(new ") || !sb
.append(name
) || !sb
.append("(")) {
846 if (!sb
.append(message
)) {
850 if (!filename
->empty()) {
851 if (!sb
.append(", ") || !sb
.append(filename
)) {
856 /* We have a line, but no filename, add empty string */
857 if (filename
->empty() && !sb
.append(", \"\"")) {
861 JSString
* linenumber
= ToString
<CanGC
>(cx
, linenoVal
);
865 if (!sb
.append(", ") || !sb
.append(linenumber
)) {
870 if (!sb
.append("))")) {
874 return sb
.finishString();
878 * Return a string that may eval to something similar to the original object.
880 static bool exn_toSource(JSContext
* cx
, unsigned argc
, Value
* vp
) {
881 AutoCheckRecursionLimit
recursion(cx
);
882 if (!recursion
.check(cx
)) {
885 CallArgs args
= CallArgsFromVp(argc
, vp
);
887 RootedObject
obj(cx
, ToObject(cx
, args
.thisv()));
892 JSString
* str
= ErrorToSource(cx
, obj
);
897 args
.rval().setString(str
);