Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / js / src / vm / ErrorObject.cpp
blob193733d67a600238c597c65331185e1e7e265865
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"
15 #include <utility>
17 #include "jsexn.h"
18 #include "jspubtd.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
26 #include "js/Class.h"
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"
34 #include "js/Stack.h"
35 #include "js/TypeDecls.h"
36 #include "js/Utility.h"
37 #include "js/Value.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"
49 #include "vm/Shape.h"
50 #include "vm/Stack.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"
60 using namespace js;
62 #define IMPLEMENT_ERROR_PROTO_CLASS(name) \
63 { \
64 #name ".prototype", JSCLASS_HAS_CACHED_PROTO(JSProto_##name), \
65 JS_NULL_CLASS_OPS, \
66 &ErrorObject::classSpecs[JSProto_##name - JSProto_Error] \
69 const JSClass ErrorObject::protoClasses[JSEXN_ERROR_LIMIT] = {
70 IMPLEMENT_ERROR_PROTO_CLASS(Error),
72 IMPLEMENT_ERROR_PROTO_CLASS(InternalError),
73 IMPLEMENT_ERROR_PROTO_CLASS(AggregateError),
74 IMPLEMENT_ERROR_PROTO_CLASS(EvalError),
75 IMPLEMENT_ERROR_PROTO_CLASS(RangeError),
76 IMPLEMENT_ERROR_PROTO_CLASS(ReferenceError),
77 IMPLEMENT_ERROR_PROTO_CLASS(SyntaxError),
78 IMPLEMENT_ERROR_PROTO_CLASS(TypeError),
79 IMPLEMENT_ERROR_PROTO_CLASS(URIError),
81 IMPLEMENT_ERROR_PROTO_CLASS(DebuggeeWouldRun),
82 IMPLEMENT_ERROR_PROTO_CLASS(CompileError),
83 IMPLEMENT_ERROR_PROTO_CLASS(LinkError),
84 IMPLEMENT_ERROR_PROTO_CLASS(RuntimeError)};
86 static bool exn_toSource(JSContext* cx, unsigned argc, Value* vp);
88 static const JSFunctionSpec error_methods[] = {
89 JS_FN("toSource", exn_toSource, 0, 0),
90 JS_SELF_HOSTED_FN("toString", "ErrorToString", 0, 0), JS_FS_END};
92 // Error.prototype and NativeError.prototype have own .message and .name
93 // properties.
94 #define COMMON_ERROR_PROPERTIES(name) \
95 JS_STRING_PS("message", "", 0), JS_STRING_PS("name", #name, 0)
97 static const JSPropertySpec error_properties[] = {
98 COMMON_ERROR_PROPERTIES(Error),
99 // Only Error.prototype has .stack!
100 JS_PSGS("stack", ErrorObject::getStack, ErrorObject::setStack, 0),
101 JS_PS_END};
103 #define IMPLEMENT_NATIVE_ERROR_PROPERTIES(name) \
104 static const JSPropertySpec name##_properties[] = { \
105 COMMON_ERROR_PROPERTIES(name), JS_PS_END};
107 IMPLEMENT_NATIVE_ERROR_PROPERTIES(InternalError)
108 IMPLEMENT_NATIVE_ERROR_PROPERTIES(AggregateError)
109 IMPLEMENT_NATIVE_ERROR_PROPERTIES(EvalError)
110 IMPLEMENT_NATIVE_ERROR_PROPERTIES(RangeError)
111 IMPLEMENT_NATIVE_ERROR_PROPERTIES(ReferenceError)
112 IMPLEMENT_NATIVE_ERROR_PROPERTIES(SyntaxError)
113 IMPLEMENT_NATIVE_ERROR_PROPERTIES(TypeError)
114 IMPLEMENT_NATIVE_ERROR_PROPERTIES(URIError)
115 IMPLEMENT_NATIVE_ERROR_PROPERTIES(DebuggeeWouldRun)
116 IMPLEMENT_NATIVE_ERROR_PROPERTIES(CompileError)
117 IMPLEMENT_NATIVE_ERROR_PROPERTIES(LinkError)
118 IMPLEMENT_NATIVE_ERROR_PROPERTIES(RuntimeError)
120 #define IMPLEMENT_NATIVE_ERROR_SPEC(name) \
122 ErrorObject::createConstructor, ErrorObject::createProto, nullptr, \
123 nullptr, nullptr, name##_properties, nullptr, JSProto_Error \
126 #define IMPLEMENT_NONGLOBAL_ERROR_SPEC(name) \
128 ErrorObject::createConstructor, ErrorObject::createProto, nullptr, \
129 nullptr, nullptr, name##_properties, nullptr, \
130 JSProto_Error | ClassSpec::DontDefineConstructor \
133 const ClassSpec ErrorObject::classSpecs[JSEXN_ERROR_LIMIT] = {
134 {ErrorObject::createConstructor, ErrorObject::createProto, nullptr, nullptr,
135 error_methods, error_properties},
137 IMPLEMENT_NATIVE_ERROR_SPEC(InternalError),
138 IMPLEMENT_NATIVE_ERROR_SPEC(AggregateError),
139 IMPLEMENT_NATIVE_ERROR_SPEC(EvalError),
140 IMPLEMENT_NATIVE_ERROR_SPEC(RangeError),
141 IMPLEMENT_NATIVE_ERROR_SPEC(ReferenceError),
142 IMPLEMENT_NATIVE_ERROR_SPEC(SyntaxError),
143 IMPLEMENT_NATIVE_ERROR_SPEC(TypeError),
144 IMPLEMENT_NATIVE_ERROR_SPEC(URIError),
146 IMPLEMENT_NONGLOBAL_ERROR_SPEC(DebuggeeWouldRun),
147 IMPLEMENT_NONGLOBAL_ERROR_SPEC(CompileError),
148 IMPLEMENT_NONGLOBAL_ERROR_SPEC(LinkError),
149 IMPLEMENT_NONGLOBAL_ERROR_SPEC(RuntimeError)};
151 #define IMPLEMENT_ERROR_CLASS_CORE(name, reserved_slots) \
153 #name, \
154 JSCLASS_HAS_CACHED_PROTO(JSProto_##name) | \
155 JSCLASS_HAS_RESERVED_SLOTS(reserved_slots) | \
156 JSCLASS_BACKGROUND_FINALIZE, \
157 &ErrorObjectClassOps, \
158 &ErrorObject::classSpecs[JSProto_##name - JSProto_Error] \
161 #define IMPLEMENT_ERROR_CLASS(name) \
162 IMPLEMENT_ERROR_CLASS_CORE(name, ErrorObject::RESERVED_SLOTS)
164 // Only used for classes that could be a Wasm trap. Classes that use this
165 // macro should be kept in sync with the exception types that mightBeWasmTrap()
166 // will return true for.
167 #define IMPLEMENT_ERROR_CLASS_MAYBE_WASM_TRAP(name) \
168 IMPLEMENT_ERROR_CLASS_CORE(name, ErrorObject::RESERVED_SLOTS_MAYBE_WASM_TRAP)
170 static void exn_finalize(JS::GCContext* gcx, JSObject* obj);
172 static const JSClassOps ErrorObjectClassOps = {
173 nullptr, // addProperty
174 nullptr, // delProperty
175 nullptr, // enumerate
176 nullptr, // newEnumerate
177 nullptr, // resolve
178 nullptr, // mayResolve
179 exn_finalize, // finalize
180 nullptr, // call
181 nullptr, // construct
182 nullptr, // trace
185 const JSClass ErrorObject::classes[JSEXN_ERROR_LIMIT] = {
186 IMPLEMENT_ERROR_CLASS(Error),
187 IMPLEMENT_ERROR_CLASS_MAYBE_WASM_TRAP(InternalError),
188 IMPLEMENT_ERROR_CLASS(AggregateError), IMPLEMENT_ERROR_CLASS(EvalError),
189 IMPLEMENT_ERROR_CLASS(RangeError), IMPLEMENT_ERROR_CLASS(ReferenceError),
190 IMPLEMENT_ERROR_CLASS(SyntaxError), IMPLEMENT_ERROR_CLASS(TypeError),
191 IMPLEMENT_ERROR_CLASS(URIError),
192 // These Error subclasses are not accessible via the global object:
193 IMPLEMENT_ERROR_CLASS(DebuggeeWouldRun),
194 IMPLEMENT_ERROR_CLASS(CompileError), IMPLEMENT_ERROR_CLASS(LinkError),
195 IMPLEMENT_ERROR_CLASS_MAYBE_WASM_TRAP(RuntimeError)};
197 static void exn_finalize(JS::GCContext* gcx, JSObject* obj) {
198 if (JSErrorReport* report = obj->as<ErrorObject>().getErrorReport()) {
199 // Bug 1560019: This allocation is not currently tracked.
200 gcx->deleteUntracked(report);
204 static ErrorObject* CreateErrorObject(JSContext* cx, const CallArgs& args,
205 unsigned messageArg, JSExnType exnType,
206 HandleObject proto) {
207 // Compute the error message, if any.
208 RootedString message(cx, nullptr);
209 if (args.hasDefined(messageArg)) {
210 message = ToString<CanGC>(cx, args[messageArg]);
211 if (!message) {
212 return nullptr;
216 // Don't interpret the two parameters following the message parameter as the
217 // non-standard fileName and lineNumber arguments when we have an options
218 // object argument.
219 bool hasOptions = args.get(messageArg + 1).isObject();
221 Rooted<mozilla::Maybe<Value>> cause(cx, mozilla::Nothing());
222 if (hasOptions) {
223 RootedObject options(cx, &args[messageArg + 1].toObject());
225 bool hasCause = false;
226 if (!HasProperty(cx, options, cx->names().cause, &hasCause)) {
227 return nullptr;
230 if (hasCause) {
231 RootedValue causeValue(cx);
232 if (!GetProperty(cx, options, options, cx->names().cause, &causeValue)) {
233 return nullptr;
235 cause = mozilla::Some(causeValue.get());
239 // Find the scripted caller, but only ones we're allowed to know about.
240 NonBuiltinFrameIter iter(cx, cx->realm()->principals());
242 RootedString fileName(cx);
243 uint32_t sourceId = 0;
244 if (!hasOptions && args.length() > messageArg + 1) {
245 fileName = ToString<CanGC>(cx, args[messageArg + 1]);
246 } else {
247 fileName = cx->runtime()->emptyString;
248 if (!iter.done()) {
249 if (const char* cfilename = iter.filename()) {
250 fileName = JS_NewStringCopyUTF8Z(
251 cx, JS::ConstUTF8CharsZ(cfilename, strlen(cfilename)));
253 if (iter.hasScript()) {
254 sourceId = iter.script()->scriptSource()->id();
258 if (!fileName) {
259 return nullptr;
262 uint32_t lineNumber;
263 JS::ColumnNumberOneOrigin columnNumber;
264 if (!hasOptions && args.length() > messageArg + 2) {
265 if (!ToUint32(cx, args[messageArg + 2], &lineNumber)) {
266 return nullptr;
268 } else {
269 JS::TaggedColumnNumberOneOrigin tmp;
270 lineNumber = iter.done() ? 0 : iter.computeLine(&tmp);
271 columnNumber = JS::ColumnNumberOneOrigin(tmp.oneOriginValue());
274 RootedObject stack(cx);
275 if (!CaptureStack(cx, &stack)) {
276 return nullptr;
279 return ErrorObject::create(cx, exnType, stack, fileName, sourceId, lineNumber,
280 columnNumber, nullptr, message, cause, proto);
283 static bool Error(JSContext* cx, unsigned argc, Value* vp) {
284 CallArgs args = CallArgsFromVp(argc, vp);
286 // ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
287 // called as functions, without operator new. But as we do not give
288 // each constructor a distinct JSClass, we must get the exception type
289 // ourselves.
290 JSExnType exnType =
291 JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32());
293 MOZ_ASSERT(exnType != JSEXN_AGGREGATEERR,
294 "AggregateError has its own constructor function");
296 JSProtoKey protoKey =
297 JSCLASS_CACHED_PROTO_KEY(&ErrorObject::classes[exnType]);
299 // ES6 19.5.1.1 mandates the .prototype lookup happens before the toString
300 RootedObject proto(cx);
301 if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey, &proto)) {
302 return false;
305 auto* obj = CreateErrorObject(cx, args, 0, exnType, proto);
306 if (!obj) {
307 return false;
310 args.rval().setObject(*obj);
311 return true;
314 // AggregateError ( errors, message )
315 static bool AggregateError(JSContext* cx, unsigned argc, Value* vp) {
316 CallArgs args = CallArgsFromVp(argc, vp);
318 mozilla::DebugOnly<JSExnType> exnType =
319 JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32());
321 MOZ_ASSERT(exnType == JSEXN_AGGREGATEERR);
323 // Steps 1-2. (9.1.13 OrdinaryCreateFromConstructor, steps 1-2).
324 RootedObject proto(cx);
325 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_AggregateError,
326 &proto)) {
327 return false;
330 // TypeError anyway, but this gives a better error message.
331 if (!args.requireAtLeast(cx, "AggregateError", 1)) {
332 return false;
335 // 9.1.13 OrdinaryCreateFromConstructor, step 3.
336 // Step 3.
337 Rooted<ErrorObject*> obj(
338 cx, CreateErrorObject(cx, args, 1, JSEXN_AGGREGATEERR, proto));
339 if (!obj) {
340 return false;
343 // Step 4.
345 Rooted<ArrayObject*> errorsList(cx);
346 if (!IterableToArray(cx, args.get(0), &errorsList)) {
347 return false;
350 // Step 5.
351 RootedValue errorsVal(cx, JS::ObjectValue(*errorsList));
352 if (!NativeDefineDataProperty(cx, obj, cx->names().errors, errorsVal, 0)) {
353 return false;
356 // Step 6.
357 args.rval().setObject(*obj);
358 return true;
361 /* static */
362 JSObject* ErrorObject::createProto(JSContext* cx, JSProtoKey key) {
363 JSExnType type = ExnTypeFromProtoKey(key);
365 if (type == JSEXN_ERR) {
366 return GlobalObject::createBlankPrototype(
367 cx, cx->global(), &ErrorObject::protoClasses[JSEXN_ERR]);
370 RootedObject protoProto(
371 cx, GlobalObject::getOrCreateErrorPrototype(cx, cx->global()));
372 if (!protoProto) {
373 return nullptr;
376 return GlobalObject::createBlankPrototypeInheriting(
377 cx, &ErrorObject::protoClasses[type], protoProto);
380 /* static */
381 JSObject* ErrorObject::createConstructor(JSContext* cx, JSProtoKey key) {
382 JSExnType type = ExnTypeFromProtoKey(key);
383 RootedObject ctor(cx);
385 if (type == JSEXN_ERR) {
386 ctor = GenericCreateConstructor<Error, 1, gc::AllocKind::FUNCTION_EXTENDED>(
387 cx, key);
388 } else {
389 RootedFunction proto(
390 cx, GlobalObject::getOrCreateErrorConstructor(cx, cx->global()));
391 if (!proto) {
392 return nullptr;
395 Native native;
396 unsigned nargs;
397 if (type == JSEXN_AGGREGATEERR) {
398 native = AggregateError;
399 nargs = 2;
400 } else {
401 native = Error;
402 nargs = 1;
405 ctor =
406 NewFunctionWithProto(cx, native, nargs, FunctionFlags::NATIVE_CTOR,
407 nullptr, ClassName(key, cx), proto,
408 gc::AllocKind::FUNCTION_EXTENDED, TenuredObject);
411 if (!ctor) {
412 return nullptr;
415 ctor->as<JSFunction>().setExtendedSlot(0, Int32Value(type));
416 return ctor;
419 /* static */
420 SharedShape* js::ErrorObject::assignInitialShape(JSContext* cx,
421 Handle<ErrorObject*> obj) {
422 MOZ_ASSERT(obj->empty());
424 constexpr PropertyFlags propFlags = {PropertyFlag::Configurable,
425 PropertyFlag::Writable};
427 if (!NativeObject::addPropertyInReservedSlot(cx, obj, cx->names().fileName,
428 FILENAME_SLOT, propFlags)) {
429 return nullptr;
432 if (!NativeObject::addPropertyInReservedSlot(cx, obj, cx->names().lineNumber,
433 LINENUMBER_SLOT, propFlags)) {
434 return nullptr;
437 if (!NativeObject::addPropertyInReservedSlot(
438 cx, obj, cx->names().columnNumber, COLUMNNUMBER_SLOT, propFlags)) {
439 return nullptr;
442 return obj->sharedShape();
445 /* static */
446 bool js::ErrorObject::init(JSContext* cx, Handle<ErrorObject*> obj,
447 JSExnType type, UniquePtr<JSErrorReport> errorReport,
448 HandleString fileName, HandleObject stack,
449 uint32_t sourceId, uint32_t lineNumber,
450 JS::ColumnNumberOneOrigin columnNumber,
451 HandleString message,
452 Handle<mozilla::Maybe<JS::Value>> cause) {
453 MOZ_ASSERT(JSEXN_ERR <= type && type < JSEXN_ERROR_LIMIT);
454 AssertObjectIsSavedFrameOrWrapper(cx, stack);
455 cx->check(obj, stack);
457 // Null out early in case of error, for exn_finalize's sake.
458 obj->initReservedSlot(ERROR_REPORT_SLOT, PrivateValue(nullptr));
460 if (!SharedShape::ensureInitialCustomShape<ErrorObject>(cx, obj)) {
461 return false;
464 // The .message property isn't part of the initial shape because it's
465 // present in some error objects -- |Error.prototype|, |new Error("f")|,
466 // |new Error("")| -- but not in others -- |new Error(undefined)|,
467 // |new Error()|.
468 if (message) {
469 constexpr PropertyFlags propFlags = {PropertyFlag::Configurable,
470 PropertyFlag::Writable};
471 if (!NativeObject::addPropertyInReservedSlot(cx, obj, cx->names().message,
472 MESSAGE_SLOT, propFlags)) {
473 return false;
477 // Similar to the .message property, .cause is present only in some error
478 // objects -- |new Error("f", {cause: cause})| -- but not in other --
479 // |Error.prototype|, |new Error()|, |new Error("f")|.
480 if (cause.isSome()) {
481 constexpr PropertyFlags propFlags = {PropertyFlag::Configurable,
482 PropertyFlag::Writable};
483 if (!NativeObject::addPropertyInReservedSlot(cx, obj, cx->names().cause,
484 CAUSE_SLOT, propFlags)) {
485 return false;
489 MOZ_ASSERT(obj->lookupPure(NameToId(cx->names().fileName))->slot() ==
490 FILENAME_SLOT);
491 MOZ_ASSERT(obj->lookupPure(NameToId(cx->names().lineNumber))->slot() ==
492 LINENUMBER_SLOT);
493 MOZ_ASSERT(obj->lookupPure(NameToId(cx->names().columnNumber))->slot() ==
494 COLUMNNUMBER_SLOT);
495 MOZ_ASSERT_IF(
496 message,
497 obj->lookupPure(NameToId(cx->names().message))->slot() == MESSAGE_SLOT);
498 MOZ_ASSERT_IF(
499 cause.isSome(),
500 obj->lookupPure(NameToId(cx->names().cause))->slot() == CAUSE_SLOT);
502 JSErrorReport* report = errorReport.release();
503 obj->initReservedSlot(STACK_SLOT, ObjectOrNullValue(stack));
504 obj->setReservedSlot(ERROR_REPORT_SLOT, PrivateValue(report));
505 obj->initReservedSlot(FILENAME_SLOT, StringValue(fileName));
506 obj->initReservedSlot(LINENUMBER_SLOT, Int32Value(lineNumber));
507 obj->initReservedSlot(COLUMNNUMBER_SLOT,
508 Int32Value(columnNumber.oneOriginValue()));
509 if (message) {
510 obj->initReservedSlot(MESSAGE_SLOT, StringValue(message));
512 if (cause.isSome()) {
513 obj->initReservedSlot(CAUSE_SLOT, *cause.get());
514 } else {
515 obj->initReservedSlot(CAUSE_SLOT, MagicValue(JS_ERROR_WITHOUT_CAUSE));
517 obj->initReservedSlot(SOURCEID_SLOT, Int32Value(sourceId));
518 if (obj->mightBeWasmTrap()) {
519 MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(obj->getClass()) > WASM_TRAP_SLOT);
520 obj->initReservedSlot(WASM_TRAP_SLOT, BooleanValue(false));
523 return true;
526 /* static */
527 ErrorObject* js::ErrorObject::create(JSContext* cx, JSExnType errorType,
528 HandleObject stack, HandleString fileName,
529 uint32_t sourceId, uint32_t lineNumber,
530 JS::ColumnNumberOneOrigin columnNumber,
531 UniquePtr<JSErrorReport> report,
532 HandleString message,
533 Handle<mozilla::Maybe<JS::Value>> cause,
534 HandleObject protoArg /* = nullptr */) {
535 AssertObjectIsSavedFrameOrWrapper(cx, stack);
537 RootedObject proto(cx, protoArg);
538 if (!proto) {
539 proto = GlobalObject::getOrCreateCustomErrorPrototype(cx, cx->global(),
540 errorType);
541 if (!proto) {
542 return nullptr;
546 Rooted<ErrorObject*> errObject(cx);
548 const JSClass* clasp = ErrorObject::classForType(errorType);
549 JSObject* obj = NewObjectWithGivenProto(cx, clasp, proto);
550 if (!obj) {
551 return nullptr;
553 errObject = &obj->as<ErrorObject>();
556 if (!ErrorObject::init(cx, errObject, errorType, std::move(report), fileName,
557 stack, sourceId, lineNumber, columnNumber, message,
558 cause)) {
559 return nullptr;
562 return errObject;
565 JSErrorReport* js::ErrorObject::getOrCreateErrorReport(JSContext* cx) {
566 if (JSErrorReport* r = getErrorReport()) {
567 return r;
570 // We build an error report on the stack and then use CopyErrorReport to do
571 // the nitty-gritty malloc stuff.
572 JSErrorReport report;
574 // Type.
575 JSExnType type_ = type();
576 report.exnType = type_;
578 // Filename.
579 RootedString filename(cx, fileName(cx));
580 UniqueChars filenameStr = JS_EncodeStringToUTF8(cx, filename);
581 if (!filenameStr) {
582 return nullptr;
584 report.filename = JS::ConstUTF8CharsZ(filenameStr.get());
586 // Coordinates.
587 report.sourceId = sourceId();
588 report.lineno = lineNumber();
589 report.column = columnNumber();
591 // Message. Note that |new Error()| will result in an undefined |message|
592 // slot, so we need to explicitly substitute the empty string in that case.
593 RootedString message(cx, getMessage());
594 if (!message) {
595 message = cx->runtime()->emptyString;
598 UniqueChars utf8 = StringToNewUTF8CharsZ(cx, *message);
599 if (!utf8) {
600 return nullptr;
602 report.initOwnedMessage(utf8.release());
604 // Cache and return.
605 UniquePtr<JSErrorReport> copy = CopyErrorReport(cx, &report);
606 if (!copy) {
607 return nullptr;
609 setReservedSlot(ERROR_REPORT_SLOT, PrivateValue(copy.get()));
610 return copy.release();
613 static bool FindErrorInstanceOrPrototype(JSContext* cx, HandleObject obj,
614 MutableHandleObject result) {
615 // Walk up the prototype chain until we find an error object instance or
616 // prototype object. This allows code like:
617 // Object.create(Error.prototype).stack
618 // or
619 // function NYI() { }
620 // NYI.prototype = new Error;
621 // (new NYI).stack
622 // to continue returning stacks that are useless, but at least don't throw.
624 RootedObject curr(cx, obj);
625 RootedObject target(cx);
626 do {
627 target = CheckedUnwrapStatic(curr);
628 if (!target) {
629 ReportAccessDenied(cx);
630 return false;
632 if (IsErrorProtoKey(StandardProtoKeyOrNull(target))) {
633 result.set(target);
634 return true;
637 if (!GetPrototype(cx, curr, &curr)) {
638 return false;
640 } while (curr);
642 // We walked the whole prototype chain and did not find an Error
643 // object.
644 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
645 JSMSG_INCOMPATIBLE_PROTO, "Error", "(get stack)",
646 obj->getClass()->name);
647 return false;
650 static MOZ_ALWAYS_INLINE bool IsObject(HandleValue v) { return v.isObject(); }
652 /* static */
653 bool js::ErrorObject::getStack(JSContext* cx, unsigned argc, Value* vp) {
654 CallArgs args = CallArgsFromVp(argc, vp);
655 // We accept any object here, because of poor-man's subclassing of Error.
656 return CallNonGenericMethod<IsObject, getStack_impl>(cx, args);
659 /* static */
660 bool js::ErrorObject::getStack_impl(JSContext* cx, const CallArgs& args) {
661 RootedObject thisObj(cx, &args.thisv().toObject());
663 RootedObject obj(cx);
664 if (!FindErrorInstanceOrPrototype(cx, thisObj, &obj)) {
665 return false;
668 if (!obj->is<ErrorObject>()) {
669 args.rval().setString(cx->runtime()->emptyString);
670 return true;
673 // Do frame filtering based on the ErrorObject's principals. This ensures we
674 // don't see chrome frames when chrome code accesses .stack over Xrays.
675 JSPrincipals* principals = obj->as<ErrorObject>().realm()->principals();
677 RootedObject savedFrameObj(cx, obj->as<ErrorObject>().stack());
678 RootedString stackString(cx);
679 if (!BuildStackString(cx, principals, savedFrameObj, &stackString)) {
680 return false;
683 if (cx->runtime()->stackFormat() == js::StackFormat::V8) {
684 // When emulating V8 stack frames, we also need to prepend the
685 // stringified Error to the stack string.
686 Handle<PropertyName*> name = cx->names().ErrorToStringWithTrailingNewline;
687 FixedInvokeArgs<0> args2(cx);
688 RootedValue rval(cx);
689 if (!CallSelfHostedFunction(cx, name, args.thisv(), args2, &rval)) {
690 return false;
693 if (!rval.isString()) {
694 args.rval().setString(cx->runtime()->emptyString);
695 return true;
698 RootedString stringified(cx, rval.toString());
699 stackString = ConcatStrings<CanGC>(cx, stringified, stackString);
702 args.rval().setString(stackString);
703 return true;
706 /* static */
707 bool js::ErrorObject::setStack(JSContext* cx, unsigned argc, Value* vp) {
708 CallArgs args = CallArgsFromVp(argc, vp);
709 // We accept any object here, because of poor-man's subclassing of Error.
710 return CallNonGenericMethod<IsObject, setStack_impl>(cx, args);
713 /* static */
714 bool js::ErrorObject::setStack_impl(JSContext* cx, const CallArgs& args) {
715 RootedObject thisObj(cx, &args.thisv().toObject());
717 if (!args.requireAtLeast(cx, "(set stack)", 1)) {
718 return false;
720 RootedValue val(cx, args[0]);
722 return DefineDataProperty(cx, thisObj, cx->names().stack, val);
725 void js::ErrorObject::setFromWasmTrap() {
726 MOZ_ASSERT(mightBeWasmTrap());
727 MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(getClass()) > WASM_TRAP_SLOT);
728 setReservedSlot(WASM_TRAP_SLOT, BooleanValue(true));
731 JSString* js::ErrorToSource(JSContext* cx, HandleObject obj) {
732 RootedValue nameVal(cx);
733 RootedString name(cx);
734 if (!GetProperty(cx, obj, obj, cx->names().name, &nameVal) ||
735 !(name = ToString<CanGC>(cx, nameVal))) {
736 return nullptr;
739 RootedValue messageVal(cx);
740 RootedString message(cx);
741 if (!GetProperty(cx, obj, obj, cx->names().message, &messageVal) ||
742 !(message = ValueToSource(cx, messageVal))) {
743 return nullptr;
746 RootedValue filenameVal(cx);
747 RootedString filename(cx);
748 if (!GetProperty(cx, obj, obj, cx->names().fileName, &filenameVal) ||
749 !(filename = ValueToSource(cx, filenameVal))) {
750 return nullptr;
753 RootedValue linenoVal(cx);
754 uint32_t lineno;
755 if (!GetProperty(cx, obj, obj, cx->names().lineNumber, &linenoVal) ||
756 !ToUint32(cx, linenoVal, &lineno)) {
757 return nullptr;
760 JSStringBuilder sb(cx);
761 if (!sb.append("(new ") || !sb.append(name) || !sb.append("(")) {
762 return nullptr;
765 if (!sb.append(message)) {
766 return nullptr;
769 if (!filename->empty()) {
770 if (!sb.append(", ") || !sb.append(filename)) {
771 return nullptr;
774 if (lineno != 0) {
775 /* We have a line, but no filename, add empty string */
776 if (filename->empty() && !sb.append(", \"\"")) {
777 return nullptr;
780 JSString* linenumber = ToString<CanGC>(cx, linenoVal);
781 if (!linenumber) {
782 return nullptr;
784 if (!sb.append(", ") || !sb.append(linenumber)) {
785 return nullptr;
789 if (!sb.append("))")) {
790 return nullptr;
793 return sb.finishString();
797 * Return a string that may eval to something similar to the original object.
799 static bool exn_toSource(JSContext* cx, unsigned argc, Value* vp) {
800 AutoCheckRecursionLimit recursion(cx);
801 if (!recursion.check(cx)) {
802 return false;
804 CallArgs args = CallArgsFromVp(argc, vp);
806 RootedObject obj(cx, ToObject(cx, args.thisv()));
807 if (!obj) {
808 return false;
811 JSString* str = ErrorToSource(cx, obj);
812 if (!str) {
813 return false;
816 args.rval().setString(str);
817 return true;