1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sw=2 et tw=80:
3  *
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/. */
7 
8 #include "vm/ErrorObject-inl.h"
9 
10 #include "mozilla/Assertions.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/DebugOnly.h"
13 
14 #include <utility>
15 
16 #include "jsapi.h"
17 #include "jsexn.h"
18 #include "jsfriendapi.h"
19 #include "jsnum.h"
20 #include "jspubtd.h"
21 #include "NamespaceImports.h"
22 
23 #include "builtin/Array.h"
24 #include "gc/AllocKind.h"
25 #include "gc/FreeOp.h"
26 #include "gc/Rooting.h"
27 #include "js/CallArgs.h"
28 #include "js/CallNonGenericMethod.h"
29 #include "js/CharacterEncoding.h"
30 #include "js/Class.h"
31 #include "js/Conversions.h"
32 #include "js/ErrorReport.h"
33 #include "js/ForOfIterator.h"
34 #include "js/PropertySpec.h"
35 #include "js/RootingAPI.h"
36 #include "js/TypeDecls.h"
37 #include "js/Utility.h"
38 #include "js/Value.h"
39 #include "js/Wrapper.h"
40 #include "util/StringBuffer.h"
41 #include "vm/GlobalObject.h"
42 #include "vm/JSAtom.h"
43 #include "vm/JSFunction.h"
44 #include "vm/JSObject.h"
45 #include "vm/NativeObject.h"
46 #include "vm/ObjectGroup.h"
47 #include "vm/ObjectOperations.h"
48 #include "vm/SavedStacks.h"
49 #include "vm/SelfHosting.h"
50 #include "vm/Shape.h"
51 #include "vm/Stack.h"
52 #include "vm/StringType.h"
53 #include "vm/ToSource.h"  // js::ValueToSource
54 
55 #include "vm/ArrayObject-inl.h"
56 #include "vm/JSContext-inl.h"
57 #include "vm/JSObject-inl.h"
58 #include "vm/NativeObject-inl.h"
59 #include "vm/ObjectOperations-inl.h"
60 #include "vm/SavedStacks-inl.h"
61 #include "vm/Shape-inl.h"
62 
63 using namespace js;
64 
65 #define IMPLEMENT_ERROR_PROTO_CLASS(name)                        \
66   {                                                              \
67     js_Object_str, JSCLASS_HAS_CACHED_PROTO(JSProto_##name),     \
68         JS_NULL_CLASS_OPS,                                       \
69         &ErrorObject::classSpecs[JSProto_##name - JSProto_Error] \
70   }
71 
72 const JSClass ErrorObject::protoClasses[JSEXN_ERROR_LIMIT] = {
73     IMPLEMENT_ERROR_PROTO_CLASS(Error),
74 
75     IMPLEMENT_ERROR_PROTO_CLASS(InternalError),
76     IMPLEMENT_ERROR_PROTO_CLASS(AggregateError),
77     IMPLEMENT_ERROR_PROTO_CLASS(EvalError),
78     IMPLEMENT_ERROR_PROTO_CLASS(RangeError),
79     IMPLEMENT_ERROR_PROTO_CLASS(ReferenceError),
80     IMPLEMENT_ERROR_PROTO_CLASS(SyntaxError),
81     IMPLEMENT_ERROR_PROTO_CLASS(TypeError),
82     IMPLEMENT_ERROR_PROTO_CLASS(URIError),
83 
84     IMPLEMENT_ERROR_PROTO_CLASS(DebuggeeWouldRun),
85     IMPLEMENT_ERROR_PROTO_CLASS(CompileError),
86     IMPLEMENT_ERROR_PROTO_CLASS(LinkError),
87     IMPLEMENT_ERROR_PROTO_CLASS(RuntimeError)};
88 
89 static bool exn_toSource(JSContext* cx, unsigned argc, Value* vp);
90 
91 static const JSFunctionSpec error_methods[] = {
92     JS_FN(js_toSource_str, exn_toSource, 0, 0),
93     JS_SELF_HOSTED_FN(js_toString_str, "ErrorToString", 0, 0), JS_FS_END};
94 
95 // Error.prototype and NativeError.prototype have own .message and .name
96 // properties.
97 #define COMMON_ERROR_PROPERTIES(name) \
98   JS_STRING_PS("message", "", 0), JS_STRING_PS("name", #name, 0)
99 
100 static const JSPropertySpec error_properties[] = {
101     COMMON_ERROR_PROPERTIES(Error),
102     // Only Error.prototype has .stack!
103     JS_PSGS("stack", ErrorObject::getStack, ErrorObject::setStack, 0),
104     JS_PS_END};
105 
106 static const JSPropertySpec AggregateError_properties[] = {
107     COMMON_ERROR_PROPERTIES(AggregateError),
108     // Only AggregateError.prototype has .errors!
109     JS_PSG("errors", AggregateErrorObject::getErrors, 0), JS_PS_END};
110 
111 #define IMPLEMENT_NATIVE_ERROR_PROPERTIES(name)       \
112   static const JSPropertySpec name##_properties[] = { \
113       COMMON_ERROR_PROPERTIES(name), JS_PS_END};
114 
115 IMPLEMENT_NATIVE_ERROR_PROPERTIES(InternalError)
116 IMPLEMENT_NATIVE_ERROR_PROPERTIES(EvalError)
117 IMPLEMENT_NATIVE_ERROR_PROPERTIES(RangeError)
118 IMPLEMENT_NATIVE_ERROR_PROPERTIES(ReferenceError)
119 IMPLEMENT_NATIVE_ERROR_PROPERTIES(SyntaxError)
120 IMPLEMENT_NATIVE_ERROR_PROPERTIES(TypeError)
121 IMPLEMENT_NATIVE_ERROR_PROPERTIES(URIError)
122 IMPLEMENT_NATIVE_ERROR_PROPERTIES(DebuggeeWouldRun)
123 IMPLEMENT_NATIVE_ERROR_PROPERTIES(CompileError)
124 IMPLEMENT_NATIVE_ERROR_PROPERTIES(LinkError)
125 IMPLEMENT_NATIVE_ERROR_PROPERTIES(RuntimeError)
126 
127 #define IMPLEMENT_NATIVE_ERROR_SPEC(name)                              \
128   {                                                                    \
129     ErrorObject::createConstructor, ErrorObject::createProto, nullptr, \
130         nullptr, nullptr, name##_properties, nullptr, JSProto_Error    \
131   }
132 
133 #define IMPLEMENT_NONGLOBAL_ERROR_SPEC(name)                           \
134   {                                                                    \
135     ErrorObject::createConstructor, ErrorObject::createProto, nullptr, \
136         nullptr, nullptr, name##_properties, nullptr,                  \
137         JSProto_Error | ClassSpec::DontDefineConstructor               \
138   }
139 
140 const ClassSpec ErrorObject::classSpecs[JSEXN_ERROR_LIMIT] = {
141     {ErrorObject::createConstructor, ErrorObject::createProto, nullptr, nullptr,
142      error_methods, error_properties},
143 
144     IMPLEMENT_NATIVE_ERROR_SPEC(InternalError),
145     IMPLEMENT_NATIVE_ERROR_SPEC(AggregateError),
146     IMPLEMENT_NATIVE_ERROR_SPEC(EvalError),
147     IMPLEMENT_NATIVE_ERROR_SPEC(RangeError),
148     IMPLEMENT_NATIVE_ERROR_SPEC(ReferenceError),
149     IMPLEMENT_NATIVE_ERROR_SPEC(SyntaxError),
150     IMPLEMENT_NATIVE_ERROR_SPEC(TypeError),
151     IMPLEMENT_NATIVE_ERROR_SPEC(URIError),
152 
153     IMPLEMENT_NONGLOBAL_ERROR_SPEC(DebuggeeWouldRun),
154     IMPLEMENT_NONGLOBAL_ERROR_SPEC(CompileError),
155     IMPLEMENT_NONGLOBAL_ERROR_SPEC(LinkError),
156     IMPLEMENT_NONGLOBAL_ERROR_SPEC(RuntimeError)};
157 
158 #define IMPLEMENT_ERROR_CLASS_FROM(clazz, name)                  \
159   {                                                              \
160     js_Error_str, /* yes, really */                              \
161         JSCLASS_HAS_CACHED_PROTO(JSProto_##name) |               \
162             JSCLASS_HAS_RESERVED_SLOTS(clazz::RESERVED_SLOTS) |  \
163             JSCLASS_BACKGROUND_FINALIZE,                         \
164         &ErrorObjectClassOps,                                    \
165         &ErrorObject::classSpecs[JSProto_##name - JSProto_Error] \
166   }
167 
168 #define IMPLEMENT_ERROR_CLASS(name) \
169   IMPLEMENT_ERROR_CLASS_FROM(ErrorObject, name)
170 
171 static void exn_finalize(JSFreeOp* fop, JSObject* obj);
172 
173 static const JSClassOps ErrorObjectClassOps = {
174     nullptr,       // addProperty
175     nullptr,       // delProperty
176     nullptr,       // enumerate
177     nullptr,       // newEnumerate
178     nullptr,       // resolve
179     nullptr,       // mayResolve
180     exn_finalize,  // finalize
181     nullptr,       // call
182     nullptr,       // hasInstance
183     nullptr,       // construct
184     nullptr,       // trace
185 };
186 
187 const JSClass ErrorObject::classes[JSEXN_ERROR_LIMIT] = {
188     IMPLEMENT_ERROR_CLASS(Error), IMPLEMENT_ERROR_CLASS(InternalError),
189     IMPLEMENT_ERROR_CLASS_FROM(AggregateErrorObject, AggregateError),
190     IMPLEMENT_ERROR_CLASS(EvalError), IMPLEMENT_ERROR_CLASS(RangeError),
191     IMPLEMENT_ERROR_CLASS(ReferenceError), IMPLEMENT_ERROR_CLASS(SyntaxError),
192     IMPLEMENT_ERROR_CLASS(TypeError), IMPLEMENT_ERROR_CLASS(URIError),
193     // These Error subclasses are not accessible via the global object:
194     IMPLEMENT_ERROR_CLASS(DebuggeeWouldRun),
195     IMPLEMENT_ERROR_CLASS(CompileError), IMPLEMENT_ERROR_CLASS(LinkError),
196     IMPLEMENT_ERROR_CLASS(RuntimeError)};
197 
exn_finalize(JSFreeOp * fop,JSObject * obj)198 static void exn_finalize(JSFreeOp* fop, JSObject* obj) {
199   MOZ_ASSERT(fop->maybeOnHelperThread());
200   if (JSErrorReport* report = obj->as<ErrorObject>().getErrorReport()) {
201     // Bug 1560019: This allocation is not currently tracked.
202     fop->deleteUntracked(report);
203   }
204 }
205 
CreateErrorObject(JSContext * cx,const CallArgs & args,unsigned messageArg,JSExnType exnType,HandleObject proto)206 static ErrorObject* CreateErrorObject(JSContext* cx, const CallArgs& args,
207                                       unsigned messageArg, JSExnType exnType,
208                                       HandleObject proto) {
209   // Compute the error message, if any.
210   RootedString message(cx, nullptr);
211   if (args.hasDefined(messageArg)) {
212     message = ToString<CanGC>(cx, args[messageArg]);
213     if (!message) {
214       return nullptr;
215     }
216   }
217 
218   // Find the scripted caller, but only ones we're allowed to know about.
219   NonBuiltinFrameIter iter(cx, cx->realm()->principals());
220 
221   RootedString fileName(cx);
222   uint32_t sourceId = 0;
223   if (args.length() > messageArg + 1) {
224     fileName = ToString<CanGC>(cx, args[messageArg + 1]);
225   } else {
226     fileName = cx->runtime()->emptyString;
227     if (!iter.done()) {
228       if (const char* cfilename = iter.filename()) {
229         fileName = JS_NewStringCopyZ(cx, cfilename);
230       }
231       if (iter.hasScript()) {
232         sourceId = iter.script()->scriptSource()->id();
233       }
234     }
235   }
236   if (!fileName) {
237     return nullptr;
238   }
239 
240   uint32_t lineNumber, columnNumber = 0;
241   if (args.length() > messageArg + 2) {
242     if (!ToUint32(cx, args[messageArg + 2], &lineNumber)) {
243       return nullptr;
244     }
245   } else {
246     lineNumber = iter.done() ? 0 : iter.computeLine(&columnNumber);
247     columnNumber = FixupColumnForDisplay(columnNumber);
248   }
249 
250   RootedObject stack(cx);
251   if (!CaptureStack(cx, &stack)) {
252     return nullptr;
253   }
254 
255   return ErrorObject::create(cx, exnType, stack, fileName, sourceId, lineNumber,
256                              columnNumber, nullptr, message, proto);
257 }
258 
Error(JSContext * cx,unsigned argc,Value * vp)259 static bool Error(JSContext* cx, unsigned argc, Value* vp) {
260   CallArgs args = CallArgsFromVp(argc, vp);
261 
262   // ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
263   // called as functions, without operator new.  But as we do not give
264   // each constructor a distinct JSClass, we must get the exception type
265   // ourselves.
266   JSExnType exnType =
267       JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32());
268 
269   MOZ_ASSERT(exnType != JSEXN_AGGREGATEERR,
270              "AggregateError has its own constructor function");
271 
272   JSProtoKey protoKey =
273       JSCLASS_CACHED_PROTO_KEY(&ErrorObject::classes[exnType]);
274 
275   // ES6 19.5.1.1 mandates the .prototype lookup happens before the toString
276   RootedObject proto(cx);
277   if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey, &proto)) {
278     return false;
279   }
280 
281   auto* obj = CreateErrorObject(cx, args, 0, exnType, proto);
282   if (!obj) {
283     return false;
284   }
285 
286   args.rval().setObject(*obj);
287   return true;
288 }
289 
IterableToArray(JSContext * cx,HandleValue iterable)290 static ArrayObject* IterableToArray(JSContext* cx, HandleValue iterable) {
291   JS::ForOfIterator iterator(cx);
292   if (!iterator.init(iterable, JS::ForOfIterator::ThrowOnNonIterable)) {
293     return nullptr;
294   }
295 
296   RootedArrayObject array(cx, NewDenseEmptyArray(cx));
297 
298   RootedValue nextValue(cx);
299   while (true) {
300     bool done;
301     if (!iterator.next(&nextValue, &done)) {
302       return nullptr;
303     }
304     if (done) {
305       return array;
306     }
307 
308     if (!NewbornArrayPush(cx, array, nextValue)) {
309       return nullptr;
310     }
311   }
312 }
313 
314 // AggregateError ( errors, message )
AggregateError(JSContext * cx,unsigned argc,Value * vp)315 static bool AggregateError(JSContext* cx, unsigned argc, Value* vp) {
316   CallArgs args = CallArgsFromVp(argc, vp);
317 
318   mozilla::DebugOnly<JSExnType> exnType =
319       JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32());
320 
321   MOZ_ASSERT(exnType == JSEXN_AGGREGATEERR);
322 
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;
328   }
329 
330   // Step 3 (Inlined IterableToList).
331 
332   if (!args.requireAtLeast(cx, "AggregateError", 1)) {
333     return false;
334   }
335 
336   RootedArrayObject errorsList(cx, IterableToArray(cx, args.get(0)));
337   if (!errorsList) {
338     return false;
339   }
340 
341   // 9.1.13 OrdinaryCreateFromConstructor, step 3.
342   // Step 5.
343   auto* obj = CreateErrorObject(cx, args, 1, JSEXN_AGGREGATEERR, proto);
344   if (!obj) {
345     return false;
346   }
347 
348   // Step 4.
349   obj->as<AggregateErrorObject>().setAggregateErrors(errorsList);
350 
351   // Step 6.
352   args.rval().setObject(*obj);
353   return true;
354 }
355 
356 /* static */
createProto(JSContext * cx,JSProtoKey key)357 JSObject* ErrorObject::createProto(JSContext* cx, JSProtoKey key) {
358   JSExnType type = ExnTypeFromProtoKey(key);
359 
360   if (type == JSEXN_ERR) {
361     return GlobalObject::createBlankPrototype(
362         cx, cx->global(), &ErrorObject::protoClasses[JSEXN_ERR]);
363   }
364 
365   RootedObject protoProto(
366       cx, GlobalObject::getOrCreateErrorPrototype(cx, cx->global()));
367   if (!protoProto) {
368     return nullptr;
369   }
370 
371   return GlobalObject::createBlankPrototypeInheriting(
372       cx, &ErrorObject::protoClasses[type], protoProto);
373 }
374 
375 /* static */
createConstructor(JSContext * cx,JSProtoKey key)376 JSObject* ErrorObject::createConstructor(JSContext* cx, JSProtoKey key) {
377   JSExnType type = ExnTypeFromProtoKey(key);
378   RootedObject ctor(cx);
379 
380   if (type == JSEXN_ERR) {
381     ctor = GenericCreateConstructor<Error, 1, gc::AllocKind::FUNCTION_EXTENDED>(
382         cx, key);
383   } else {
384     RootedFunction proto(
385         cx, GlobalObject::getOrCreateErrorConstructor(cx, cx->global()));
386     if (!proto) {
387       return nullptr;
388     }
389 
390     Native native;
391     unsigned nargs;
392     if (type == JSEXN_AGGREGATEERR) {
393       native = AggregateError;
394       nargs = 2;
395     } else {
396       native = Error;
397       nargs = 1;
398     }
399 
400     ctor =
401         NewFunctionWithProto(cx, native, nargs, FunctionFlags::NATIVE_CTOR,
402                              nullptr, ClassName(key, cx), proto,
403                              gc::AllocKind::FUNCTION_EXTENDED, SingletonObject);
404   }
405 
406   if (!ctor) {
407     return nullptr;
408   }
409 
410   ctor->as<JSFunction>().setExtendedSlot(0, Int32Value(type));
411   return ctor;
412 }
413 
414 /* static */
assignInitialShape(JSContext * cx,Handle<ErrorObject * > obj)415 Shape* js::ErrorObject::assignInitialShape(JSContext* cx,
416                                            Handle<ErrorObject*> obj) {
417   MOZ_ASSERT(obj->empty());
418 
419   if (!NativeObject::addDataProperty(cx, obj, cx->names().fileName,
420                                      FILENAME_SLOT, 0)) {
421     return nullptr;
422   }
423   if (!NativeObject::addDataProperty(cx, obj, cx->names().lineNumber,
424                                      LINENUMBER_SLOT, 0)) {
425     return nullptr;
426   }
427   return NativeObject::addDataProperty(cx, obj, cx->names().columnNumber,
428                                        COLUMNNUMBER_SLOT, 0);
429 }
430 
431 /* static */
init(JSContext * cx,Handle<ErrorObject * > obj,JSExnType type,UniquePtr<JSErrorReport> errorReport,HandleString fileName,HandleObject stack,uint32_t sourceId,uint32_t lineNumber,uint32_t columnNumber,HandleString message)432 bool js::ErrorObject::init(JSContext* cx, Handle<ErrorObject*> obj,
433                            JSExnType type, UniquePtr<JSErrorReport> errorReport,
434                            HandleString fileName, HandleObject stack,
435                            uint32_t sourceId, uint32_t lineNumber,
436                            uint32_t columnNumber, HandleString message) {
437   AssertObjectIsSavedFrameOrWrapper(cx, stack);
438   cx->check(obj, stack);
439 
440   // Null out early in case of error, for exn_finalize's sake.
441   obj->initReservedSlot(ERROR_REPORT_SLOT, PrivateValue(nullptr));
442 
443   if (!EmptyShape::ensureInitialCustomShape<ErrorObject>(cx, obj)) {
444     return false;
445   }
446 
447   // The .message property isn't part of the initial shape because it's
448   // present in some error objects -- |Error.prototype|, |new Error("f")|,
449   // |new Error("")| -- but not in others -- |new Error(undefined)|,
450   // |new Error()|.
451   RootedShape messageShape(cx);
452   if (message) {
453     messageShape = NativeObject::addDataProperty(cx, obj, cx->names().message,
454                                                  MESSAGE_SLOT, 0);
455     if (!messageShape) {
456       return false;
457     }
458     MOZ_ASSERT(messageShape->slot() == MESSAGE_SLOT);
459   }
460 
461   MOZ_ASSERT(obj->lookupPure(NameToId(cx->names().fileName))->slot() ==
462              FILENAME_SLOT);
463   MOZ_ASSERT(obj->lookupPure(NameToId(cx->names().lineNumber))->slot() ==
464              LINENUMBER_SLOT);
465   MOZ_ASSERT(obj->lookupPure(NameToId(cx->names().columnNumber))->slot() ==
466              COLUMNNUMBER_SLOT);
467   MOZ_ASSERT_IF(
468       message,
469       obj->lookupPure(NameToId(cx->names().message))->slot() == MESSAGE_SLOT);
470 
471   MOZ_ASSERT(JSEXN_ERR <= type && type < JSEXN_LIMIT);
472 
473   JSErrorReport* report = errorReport.release();
474   obj->initReservedSlot(EXNTYPE_SLOT, Int32Value(type));
475   obj->initReservedSlot(STACK_SLOT, ObjectOrNullValue(stack));
476   obj->setReservedSlot(ERROR_REPORT_SLOT, PrivateValue(report));
477   obj->initReservedSlot(FILENAME_SLOT, StringValue(fileName));
478   obj->initReservedSlot(LINENUMBER_SLOT, Int32Value(lineNumber));
479   obj->initReservedSlot(COLUMNNUMBER_SLOT, Int32Value(columnNumber));
480   if (message) {
481     obj->setSlotWithType(cx, messageShape, StringValue(message));
482   }
483   obj->initReservedSlot(SOURCEID_SLOT, Int32Value(sourceId));
484 
485   return true;
486 }
487 
488 /* static */
create(JSContext * cx,JSExnType errorType,HandleObject stack,HandleString fileName,uint32_t sourceId,uint32_t lineNumber,uint32_t columnNumber,UniquePtr<JSErrorReport> report,HandleString message,HandleObject protoArg)489 ErrorObject* js::ErrorObject::create(JSContext* cx, JSExnType errorType,
490                                      HandleObject stack, HandleString fileName,
491                                      uint32_t sourceId, uint32_t lineNumber,
492                                      uint32_t columnNumber,
493                                      UniquePtr<JSErrorReport> report,
494                                      HandleString message,
495                                      HandleObject protoArg /* = nullptr */) {
496   AssertObjectIsSavedFrameOrWrapper(cx, stack);
497 
498   RootedObject proto(cx, protoArg);
499   if (!proto) {
500     proto = GlobalObject::getOrCreateCustomErrorPrototype(cx, cx->global(),
501                                                           errorType);
502     if (!proto) {
503       return nullptr;
504     }
505   }
506 
507   Rooted<ErrorObject*> errObject(cx);
508   {
509     const JSClass* clasp = ErrorObject::classForType(errorType);
510     JSObject* obj = NewObjectWithGivenProto(cx, clasp, proto);
511     if (!obj) {
512       return nullptr;
513     }
514     errObject = &obj->as<ErrorObject>();
515   }
516 
517   if (!ErrorObject::init(cx, errObject, errorType, std::move(report), fileName,
518                          stack, sourceId, lineNumber, columnNumber, message)) {
519     return nullptr;
520   }
521 
522   return errObject;
523 }
524 
getOrCreateErrorReport(JSContext * cx)525 JSErrorReport* js::ErrorObject::getOrCreateErrorReport(JSContext* cx) {
526   if (JSErrorReport* r = getErrorReport()) {
527     return r;
528   }
529 
530   // We build an error report on the stack and then use CopyErrorReport to do
531   // the nitty-gritty malloc stuff.
532   JSErrorReport report;
533 
534   // Type.
535   JSExnType type_ = type();
536   report.exnType = type_;
537 
538   // Filename.
539   UniqueChars filenameStr = JS_EncodeStringToLatin1(cx, fileName(cx));
540   if (!filenameStr) {
541     return nullptr;
542   }
543   report.filename = filenameStr.get();
544 
545   // Coordinates.
546   report.sourceId = sourceId();
547   report.lineno = lineNumber();
548   report.column = columnNumber();
549 
550   // Message. Note that |new Error()| will result in an undefined |message|
551   // slot, so we need to explicitly substitute the empty string in that case.
552   RootedString message(cx, getMessage());
553   if (!message) {
554     message = cx->runtime()->emptyString;
555   }
556 
557   UniqueChars utf8 = StringToNewUTF8CharsZ(cx, *message);
558   if (!utf8) {
559     return nullptr;
560   }
561   report.initOwnedMessage(utf8.release());
562 
563   // Cache and return.
564   UniquePtr<JSErrorReport> copy = CopyErrorReport(cx, &report);
565   if (!copy) {
566     return nullptr;
567   }
568   setReservedSlot(ERROR_REPORT_SLOT, PrivateValue(copy.get()));
569   return copy.release();
570 }
571 
FindErrorInstanceOrPrototype(JSContext * cx,HandleObject obj,MutableHandleObject result)572 static bool FindErrorInstanceOrPrototype(JSContext* cx, HandleObject obj,
573                                          MutableHandleObject result) {
574   // Walk up the prototype chain until we find an error object instance or
575   // prototype object. This allows code like:
576   //  Object.create(Error.prototype).stack
577   // or
578   //   function NYI() { }
579   //   NYI.prototype = new Error;
580   //   (new NYI).stack
581   // to continue returning stacks that are useless, but at least don't throw.
582 
583   RootedObject target(cx, CheckedUnwrapStatic(obj));
584   if (!target) {
585     ReportAccessDenied(cx);
586     return false;
587   }
588 
589   RootedObject proto(cx);
590   while (!IsErrorProtoKey(StandardProtoKeyOrNull(target))) {
591     if (!GetPrototype(cx, target, &proto)) {
592       return false;
593     }
594 
595     if (!proto) {
596       // We walked the whole prototype chain and did not find an Error
597       // object.
598       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
599                                 JSMSG_INCOMPATIBLE_PROTO, js_Error_str,
600                                 "(get stack)", obj->getClass()->name);
601       return false;
602     }
603 
604     target = CheckedUnwrapStatic(proto);
605     if (!target) {
606       ReportAccessDenied(cx);
607       return false;
608     }
609   }
610 
611   result.set(target);
612   return true;
613 }
614 
IsObject(HandleValue v)615 static MOZ_ALWAYS_INLINE bool IsObject(HandleValue v) { return v.isObject(); }
616 
617 /* static */
getStack(JSContext * cx,unsigned argc,Value * vp)618 bool js::ErrorObject::getStack(JSContext* cx, unsigned argc, Value* vp) {
619   CallArgs args = CallArgsFromVp(argc, vp);
620   // We accept any object here, because of poor-man's subclassing of Error.
621   return CallNonGenericMethod<IsObject, getStack_impl>(cx, args);
622 }
623 
624 /* static */
getStack_impl(JSContext * cx,const CallArgs & args)625 bool js::ErrorObject::getStack_impl(JSContext* cx, const CallArgs& args) {
626   RootedObject thisObj(cx, &args.thisv().toObject());
627 
628   RootedObject obj(cx);
629   if (!FindErrorInstanceOrPrototype(cx, thisObj, &obj)) {
630     return false;
631   }
632 
633   if (!obj->is<ErrorObject>()) {
634     args.rval().setString(cx->runtime()->emptyString);
635     return true;
636   }
637 
638   // Do frame filtering based on the ErrorObject's principals. This ensures we
639   // don't see chrome frames when chrome code accesses .stack over Xrays.
640   JSPrincipals* principals = obj->as<ErrorObject>().realm()->principals();
641 
642   RootedObject savedFrameObj(cx, obj->as<ErrorObject>().stack());
643   RootedString stackString(cx);
644   if (!BuildStackString(cx, principals, savedFrameObj, &stackString)) {
645     return false;
646   }
647 
648   if (cx->runtime()->stackFormat() == js::StackFormat::V8) {
649     // When emulating V8 stack frames, we also need to prepend the
650     // stringified Error to the stack string.
651     HandlePropertyName name = cx->names().ErrorToStringWithTrailingNewline;
652     FixedInvokeArgs<0> args2(cx);
653     RootedValue rval(cx);
654     if (!CallSelfHostedFunction(cx, name, args.thisv(), args2, &rval)) {
655       return false;
656     }
657 
658     if (!rval.isString()) {
659       args.rval().setString(cx->runtime()->emptyString);
660       return true;
661     }
662 
663     RootedString stringified(cx, rval.toString());
664     stackString = ConcatStrings<CanGC>(cx, stringified, stackString);
665   }
666 
667   args.rval().setString(stackString);
668   return true;
669 }
670 
671 /* static */
setStack(JSContext * cx,unsigned argc,Value * vp)672 bool js::ErrorObject::setStack(JSContext* cx, unsigned argc, Value* vp) {
673   CallArgs args = CallArgsFromVp(argc, vp);
674   // We accept any object here, because of poor-man's subclassing of Error.
675   return CallNonGenericMethod<IsObject, setStack_impl>(cx, args);
676 }
677 
678 /* static */
setStack_impl(JSContext * cx,const CallArgs & args)679 bool js::ErrorObject::setStack_impl(JSContext* cx, const CallArgs& args) {
680   RootedObject thisObj(cx, &args.thisv().toObject());
681 
682   if (!args.requireAtLeast(cx, "(set stack)", 1)) {
683     return false;
684   }
685   RootedValue val(cx, args[0]);
686 
687   return DefineDataProperty(cx, thisObj, cx->names().stack, val);
688 }
689 
ErrorToSource(JSContext * cx,HandleObject obj)690 JSString* js::ErrorToSource(JSContext* cx, HandleObject obj) {
691   RootedValue nameVal(cx);
692   RootedString name(cx);
693   if (!GetProperty(cx, obj, obj, cx->names().name, &nameVal) ||
694       !(name = ToString<CanGC>(cx, nameVal))) {
695     return nullptr;
696   }
697 
698   RootedValue messageVal(cx);
699   RootedString message(cx);
700   if (!GetProperty(cx, obj, obj, cx->names().message, &messageVal) ||
701       !(message = ValueToSource(cx, messageVal))) {
702     return nullptr;
703   }
704 
705   RootedValue filenameVal(cx);
706   RootedString filename(cx);
707   if (!GetProperty(cx, obj, obj, cx->names().fileName, &filenameVal) ||
708       !(filename = ValueToSource(cx, filenameVal))) {
709     return nullptr;
710   }
711 
712   RootedValue linenoVal(cx);
713   uint32_t lineno;
714   if (!GetProperty(cx, obj, obj, cx->names().lineNumber, &linenoVal) ||
715       !ToUint32(cx, linenoVal, &lineno)) {
716     return nullptr;
717   }
718 
719   JSStringBuilder sb(cx);
720   if (!sb.append("(new ") || !sb.append(name) || !sb.append("(")) {
721     return nullptr;
722   }
723 
724   if (!sb.append(message)) {
725     return nullptr;
726   }
727 
728   if (!filename->empty()) {
729     if (!sb.append(", ") || !sb.append(filename)) {
730       return nullptr;
731     }
732   }
733   if (lineno != 0) {
734     /* We have a line, but no filename, add empty string */
735     if (filename->empty() && !sb.append(", \"\"")) {
736       return nullptr;
737     }
738 
739     JSString* linenumber = ToString<CanGC>(cx, linenoVal);
740     if (!linenumber) {
741       return nullptr;
742     }
743     if (!sb.append(", ") || !sb.append(linenumber)) {
744       return nullptr;
745     }
746   }
747 
748   if (!sb.append("))")) {
749     return nullptr;
750   }
751 
752   return sb.finishString();
753 }
754 
755 /*
756  * Return a string that may eval to something similar to the original object.
757  */
exn_toSource(JSContext * cx,unsigned argc,Value * vp)758 static bool exn_toSource(JSContext* cx, unsigned argc, Value* vp) {
759   if (!CheckRecursionLimit(cx)) {
760     return false;
761   }
762   CallArgs args = CallArgsFromVp(argc, vp);
763 
764   RootedObject obj(cx, ToObject(cx, args.thisv()));
765   if (!obj) {
766     return false;
767   }
768 
769   JSString* str = ErrorToSource(cx, obj);
770   if (!str) {
771     return false;
772   }
773 
774   args.rval().setString(str);
775   return true;
776 }
777 
aggregateErrors() const778 ArrayObject* js::AggregateErrorObject::aggregateErrors() const {
779   const Value& val = getReservedSlot(AGGREGATE_ERRORS_SLOT);
780   if (val.isUndefined()) {
781     return nullptr;
782   }
783   return &val.toObject().as<ArrayObject>();
784 }
785 
setAggregateErrors(ArrayObject * errors)786 void js::AggregateErrorObject::setAggregateErrors(ArrayObject* errors) {
787   MOZ_ASSERT(!aggregateErrors(),
788              "aggregated errors mustn't be modified once set");
789   setReservedSlot(AGGREGATE_ERRORS_SLOT, ObjectValue(*errors));
790 }
791 
IsAggregateError(HandleValue v)792 static inline bool IsAggregateError(HandleValue v) {
793   return v.isObject() && v.toObject().is<AggregateErrorObject>();
794 }
795 
796 // get AggregateError.prototype.errors
getErrors(JSContext * cx,unsigned argc,Value * vp)797 bool js::AggregateErrorObject::getErrors(JSContext* cx, unsigned argc,
798                                          Value* vp) {
799   CallArgs args = CallArgsFromVp(argc, vp);
800 
801   // Steps 1-4.
802   return CallNonGenericMethod<IsAggregateError, getErrors_impl>(cx, args);
803 }
804 
805 // get AggregateError.prototype.errors
getErrors_impl(JSContext * cx,const CallArgs & args)806 bool js::AggregateErrorObject::getErrors_impl(JSContext* cx,
807                                               const CallArgs& args) {
808   MOZ_ASSERT(IsAggregateError(args.thisv()));
809 
810   auto* obj = &args.thisv().toObject().as<AggregateErrorObject>();
811 
812   // Step 5.
813   // Create a copy of the [[AggregateErrors]] list.
814 
815   RootedArrayObject errorsList(cx, obj->aggregateErrors());
816 
817   // [[AggregateErrors]] may be absent when this error was created through
818   // JS_ReportError.
819   if (!errorsList) {
820     ArrayObject* result = NewDenseEmptyArray(cx);
821     if (!result) {
822       return false;
823     }
824 
825     args.rval().setObject(*result);
826     return true;
827   }
828 
829   uint32_t length = errorsList->length();
830 
831   ArrayObject* result = NewDenseFullyAllocatedArray(cx, length);
832   if (!result) {
833     return false;
834   }
835 
836   result->setLength(cx, length);
837 
838   if (length > 0) {
839     result->initDenseElements(errorsList, 0, length);
840   }
841 
842   args.rval().setObject(*result);
843   return true;
844 }
845