tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ErrorObject.cpp (35243B)


      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 #include "mozilla/Maybe.h"
     14 
     15 #include <utility>
     16 
     17 #include "jsexn.h"
     18 #include "jspubtd.h"
     19 #include "NamespaceImports.h"
     20 
     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/StringBuilder.h"
     40 #include "vm/ErrorReporting.h"
     41 #include "vm/GlobalObject.h"
     42 #include "vm/Iteration.h"
     43 #include "vm/JSAtomUtils.h"  // ClassName
     44 #include "vm/JSFunction.h"
     45 #include "vm/JSObject.h"
     46 #include "vm/NativeObject.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/JSContext-inl.h"
     56 #include "vm/JSObject-inl.h"
     57 #include "vm/ObjectOperations-inl.h"
     58 #include "vm/Realm-inl.h"
     59 #include "vm/SavedStacks-inl.h"
     60 #include "vm/Shape-inl.h"
     61 
     62 using namespace js;
     63 
     64 #define IMPLEMENT_ERROR_PROTO_CLASS(name)                        \
     65  {#name ".prototype", JSCLASS_HAS_CACHED_PROTO(JSProto_##name), \
     66   JS_NULL_CLASS_OPS,                                            \
     67   &ErrorObject::classSpecs[JSProto_##name - JSProto_Error]}
     68 
     69 const JSClass ErrorObject::protoClasses[JSEXN_ERROR_LIMIT] = {
     70    IMPLEMENT_ERROR_PROTO_CLASS(Error),
     71 
     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 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
     78    IMPLEMENT_ERROR_PROTO_CLASS(SuppressedError),
     79 #endif
     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 #ifdef ENABLE_WASM_JSPI
     89    IMPLEMENT_ERROR_PROTO_CLASS(SuspendError),
     90 #endif
     91 };
     92 
     93 static bool exn_toSource(JSContext* cx, unsigned argc, Value* vp);
     94 
     95 static const JSFunctionSpec error_methods[] = {
     96    JS_FN("toSource", exn_toSource, 0, 0),
     97    JS_SELF_HOSTED_FN("toString", "ErrorToString", 0, 0),
     98    JS_FS_END,
     99 };
    100 
    101 static bool exn_isError(JSContext* cx, unsigned argc, Value* vp);
    102 
    103 static bool exn_captureStackTrace(JSContext* cx, unsigned argc, Value* vp);
    104 
    105 static const JSFunctionSpec error_static_methods[] = {
    106    JS_FN("isError", exn_isError, 1, 0),
    107    JS_FN("captureStackTrace", exn_captureStackTrace, 2, 0),
    108    JS_FS_END,
    109 };
    110 
    111 // Error.prototype and NativeError.prototype have own .message and .name
    112 // properties.
    113 #define COMMON_ERROR_PROPERTIES(name) \
    114  JS_STRING_PS("message", "", 0), JS_STRING_PS("name", #name, 0)
    115 
    116 static const JSPropertySpec error_properties[] = {
    117    COMMON_ERROR_PROPERTIES(Error),
    118    // Only Error.prototype has .stack!
    119    JS_PSGS("stack", ErrorObject::getStack, ErrorObject::setStack, 0),
    120    JS_PS_END,
    121 };
    122 
    123 #define IMPLEMENT_NATIVE_ERROR_PROPERTIES(name)       \
    124  static const JSPropertySpec name##_properties[] = { \
    125      COMMON_ERROR_PROPERTIES(name), JS_PS_END};
    126 
    127 IMPLEMENT_NATIVE_ERROR_PROPERTIES(InternalError)
    128 IMPLEMENT_NATIVE_ERROR_PROPERTIES(AggregateError)
    129 IMPLEMENT_NATIVE_ERROR_PROPERTIES(EvalError)
    130 IMPLEMENT_NATIVE_ERROR_PROPERTIES(RangeError)
    131 IMPLEMENT_NATIVE_ERROR_PROPERTIES(ReferenceError)
    132 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    133 IMPLEMENT_NATIVE_ERROR_PROPERTIES(SuppressedError)
    134 #endif
    135 IMPLEMENT_NATIVE_ERROR_PROPERTIES(SyntaxError)
    136 IMPLEMENT_NATIVE_ERROR_PROPERTIES(TypeError)
    137 IMPLEMENT_NATIVE_ERROR_PROPERTIES(URIError)
    138 IMPLEMENT_NATIVE_ERROR_PROPERTIES(DebuggeeWouldRun)
    139 IMPLEMENT_NATIVE_ERROR_PROPERTIES(CompileError)
    140 IMPLEMENT_NATIVE_ERROR_PROPERTIES(LinkError)
    141 IMPLEMENT_NATIVE_ERROR_PROPERTIES(RuntimeError)
    142 #ifdef ENABLE_WASM_JSPI
    143 IMPLEMENT_NATIVE_ERROR_PROPERTIES(SuspendError)
    144 #endif
    145 
    146 #define IMPLEMENT_NATIVE_ERROR_SPEC(name) \
    147  {ErrorObject::createConstructor,        \
    148   ErrorObject::createProto,              \
    149   nullptr,                               \
    150   nullptr,                               \
    151   nullptr,                               \
    152   name##_properties,                     \
    153   nullptr,                               \
    154   JSProto_Error}
    155 
    156 #define IMPLEMENT_NONGLOBAL_ERROR_SPEC(name) \
    157  {ErrorObject::createConstructor,           \
    158   ErrorObject::createProto,                 \
    159   nullptr,                                  \
    160   nullptr,                                  \
    161   nullptr,                                  \
    162   name##_properties,                        \
    163   nullptr,                                  \
    164   JSProto_Error | ClassSpec::DontDefineConstructor}
    165 
    166 const ClassSpec ErrorObject::classSpecs[JSEXN_ERROR_LIMIT] = {
    167    {ErrorObject::createConstructor, ErrorObject::createProto,
    168     error_static_methods, nullptr, error_methods, error_properties},
    169 
    170    IMPLEMENT_NATIVE_ERROR_SPEC(InternalError),
    171    IMPLEMENT_NATIVE_ERROR_SPEC(AggregateError),
    172    IMPLEMENT_NATIVE_ERROR_SPEC(EvalError),
    173    IMPLEMENT_NATIVE_ERROR_SPEC(RangeError),
    174    IMPLEMENT_NATIVE_ERROR_SPEC(ReferenceError),
    175 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    176    IMPLEMENT_NATIVE_ERROR_SPEC(SuppressedError),
    177 #endif
    178    IMPLEMENT_NATIVE_ERROR_SPEC(SyntaxError),
    179    IMPLEMENT_NATIVE_ERROR_SPEC(TypeError),
    180    IMPLEMENT_NATIVE_ERROR_SPEC(URIError),
    181 
    182    IMPLEMENT_NONGLOBAL_ERROR_SPEC(DebuggeeWouldRun),
    183    IMPLEMENT_NONGLOBAL_ERROR_SPEC(CompileError),
    184    IMPLEMENT_NONGLOBAL_ERROR_SPEC(LinkError),
    185    IMPLEMENT_NONGLOBAL_ERROR_SPEC(RuntimeError),
    186 #ifdef ENABLE_WASM_JSPI
    187    IMPLEMENT_NONGLOBAL_ERROR_SPEC(SuspendError),
    188 #endif
    189 };
    190 
    191 #define IMPLEMENT_ERROR_CLASS_CORE(name, reserved_slots) \
    192  {#name,                                                \
    193   JSCLASS_HAS_CACHED_PROTO(JSProto_##name) |            \
    194       JSCLASS_HAS_RESERVED_SLOTS(reserved_slots) |      \
    195       JSCLASS_BACKGROUND_FINALIZE,                      \
    196   &ErrorObjectClassOps,                                 \
    197   &ErrorObject::classSpecs[JSProto_##name - JSProto_Error]}
    198 
    199 #define IMPLEMENT_ERROR_CLASS(name) \
    200  IMPLEMENT_ERROR_CLASS_CORE(name, ErrorObject::RESERVED_SLOTS)
    201 
    202 // Only used for classes that could be a Wasm trap. Classes that use this
    203 // macro should be kept in sync with the exception types that mightBeWasmTrap()
    204 // will return true for.
    205 #define IMPLEMENT_ERROR_CLASS_MAYBE_WASM_TRAP(name) \
    206  IMPLEMENT_ERROR_CLASS_CORE(name, ErrorObject::RESERVED_SLOTS_MAYBE_WASM_TRAP)
    207 
    208 static void exn_finalize(JS::GCContext* gcx, JSObject* obj);
    209 
    210 static const JSClassOps ErrorObjectClassOps = {
    211    nullptr,       // addProperty
    212    nullptr,       // delProperty
    213    nullptr,       // enumerate
    214    nullptr,       // newEnumerate
    215    nullptr,       // resolve
    216    nullptr,       // mayResolve
    217    exn_finalize,  // finalize
    218    nullptr,       // call
    219    nullptr,       // construct
    220    nullptr,       // trace
    221 };
    222 
    223 const JSClass ErrorObject::classes[JSEXN_ERROR_LIMIT] = {
    224    IMPLEMENT_ERROR_CLASS(Error),
    225    IMPLEMENT_ERROR_CLASS_MAYBE_WASM_TRAP(InternalError),
    226    IMPLEMENT_ERROR_CLASS(AggregateError),
    227    IMPLEMENT_ERROR_CLASS(EvalError),
    228    IMPLEMENT_ERROR_CLASS(RangeError),
    229    IMPLEMENT_ERROR_CLASS(ReferenceError),
    230 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    231    IMPLEMENT_ERROR_CLASS(SuppressedError),
    232 #endif
    233    IMPLEMENT_ERROR_CLASS(SyntaxError),
    234    IMPLEMENT_ERROR_CLASS(TypeError),
    235    IMPLEMENT_ERROR_CLASS(URIError),
    236    // These Error subclasses are not accessible via the global object:
    237    IMPLEMENT_ERROR_CLASS(DebuggeeWouldRun),
    238    IMPLEMENT_ERROR_CLASS(CompileError),
    239    IMPLEMENT_ERROR_CLASS(LinkError),
    240    IMPLEMENT_ERROR_CLASS_MAYBE_WASM_TRAP(RuntimeError),
    241 #ifdef ENABLE_WASM_JSPI
    242    IMPLEMENT_ERROR_CLASS(SuspendError),
    243 #endif
    244 };
    245 
    246 static void exn_finalize(JS::GCContext* gcx, JSObject* obj) {
    247  if (JSErrorReport* report = obj->as<ErrorObject>().getErrorReport()) {
    248    // Bug 1560019: This allocation is not currently tracked.
    249    gcx->deleteUntracked(report);
    250  }
    251 }
    252 
    253 static ErrorObject* CreateErrorObject(JSContext* cx, const CallArgs& args,
    254                                      unsigned messageArg, JSExnType exnType,
    255                                      HandleObject proto) {
    256  // Compute the error message, if any.
    257  RootedString message(cx, nullptr);
    258  if (args.hasDefined(messageArg)) {
    259    message = ToString<CanGC>(cx, args[messageArg]);
    260    if (!message) {
    261      return nullptr;
    262    }
    263  }
    264 
    265  // Don't interpret the two parameters following the message parameter as the
    266  // non-standard fileName and lineNumber arguments when we have an options
    267  // object argument and the exception type is not SuppressedError.
    268  bool hasOptions =
    269 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    270      args.get(messageArg + 1).isObject() && exnType != JSEXN_SUPPRESSEDERR;
    271 #else
    272      args.get(messageArg + 1).isObject();
    273 #endif
    274 
    275  Rooted<mozilla::Maybe<Value>> cause(cx, mozilla::Nothing());
    276  if (hasOptions) {
    277    RootedObject options(cx, &args[messageArg + 1].toObject());
    278 
    279    bool hasCause = false;
    280    if (!HasProperty(cx, options, cx->names().cause, &hasCause)) {
    281      return nullptr;
    282    }
    283 
    284    if (hasCause) {
    285      RootedValue causeValue(cx);
    286      if (!GetProperty(cx, options, options, cx->names().cause, &causeValue)) {
    287        return nullptr;
    288      }
    289      cause = mozilla::Some(causeValue.get());
    290    }
    291  }
    292 
    293  // Find the scripted caller, but only ones we're allowed to know about.
    294  NonBuiltinFrameIter iter(cx, cx->realm()->principals());
    295 
    296  RootedString fileName(cx);
    297  uint32_t sourceId = 0;
    298  if (!hasOptions && args.length() > messageArg + 1) {
    299    fileName = ToString<CanGC>(cx, args[messageArg + 1]);
    300  } else {
    301    fileName = cx->runtime()->emptyString;
    302    if (!iter.done()) {
    303      if (const char* cfilename = iter.filename()) {
    304        fileName = JS_NewStringCopyUTF8Z(
    305            cx, JS::ConstUTF8CharsZ(cfilename, strlen(cfilename)));
    306      }
    307      if (iter.hasScript()) {
    308        sourceId = iter.script()->scriptSource()->id();
    309      }
    310    }
    311  }
    312  if (!fileName) {
    313    return nullptr;
    314  }
    315 
    316  uint32_t lineNumber;
    317  JS::ColumnNumberOneOrigin columnNumber;
    318  if (!hasOptions && args.length() > messageArg + 2) {
    319    if (!ToUint32(cx, args[messageArg + 2], &lineNumber)) {
    320      return nullptr;
    321    }
    322  } else {
    323    JS::TaggedColumnNumberOneOrigin tmp;
    324    lineNumber = iter.done() ? 0 : iter.computeLine(&tmp);
    325    columnNumber = JS::ColumnNumberOneOrigin(tmp.oneOriginValue());
    326  }
    327 
    328  RootedObject stack(cx);
    329  if (!CaptureStack(cx, &stack)) {
    330    return nullptr;
    331  }
    332 
    333  return ErrorObject::create(cx, exnType, stack, fileName, sourceId, lineNumber,
    334                             columnNumber, nullptr, message, cause, proto);
    335 }
    336 
    337 static bool Error(JSContext* cx, unsigned argc, Value* vp) {
    338  CallArgs args = CallArgsFromVp(argc, vp);
    339 
    340  // ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
    341  // called as functions, without operator new.  But as we do not give
    342  // each constructor a distinct JSClass, we must get the exception type
    343  // ourselves.
    344  JSExnType exnType =
    345      JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32());
    346 
    347  MOZ_ASSERT(exnType != JSEXN_AGGREGATEERR,
    348             "AggregateError has its own constructor function");
    349 
    350 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    351  MOZ_ASSERT(exnType != JSEXN_SUPPRESSEDERR,
    352             "SuppressedError has its own constuctor function");
    353 #endif
    354 
    355  JSProtoKey protoKey =
    356      JSCLASS_CACHED_PROTO_KEY(&ErrorObject::classes[exnType]);
    357 
    358  // ES6 19.5.1.1 mandates the .prototype lookup happens before the toString
    359  RootedObject proto(cx);
    360  if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey, &proto)) {
    361    return false;
    362  }
    363 
    364  auto* obj = CreateErrorObject(cx, args, 0, exnType, proto);
    365  if (!obj) {
    366    return false;
    367  }
    368 
    369  args.rval().setObject(*obj);
    370  return true;
    371 }
    372 
    373 // AggregateError ( errors, message )
    374 static bool AggregateError(JSContext* cx, unsigned argc, Value* vp) {
    375  CallArgs args = CallArgsFromVp(argc, vp);
    376 
    377  mozilla::DebugOnly<JSExnType> exnType =
    378      JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32());
    379 
    380  MOZ_ASSERT(exnType == JSEXN_AGGREGATEERR);
    381 
    382  // Steps 1-2. (9.1.13 OrdinaryCreateFromConstructor, steps 1-2).
    383  RootedObject proto(cx);
    384  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_AggregateError,
    385                                          &proto)) {
    386    return false;
    387  }
    388 
    389  // TypeError anyway, but this gives a better error message.
    390  if (!args.requireAtLeast(cx, "AggregateError", 1)) {
    391    return false;
    392  }
    393 
    394  // 9.1.13 OrdinaryCreateFromConstructor, step 3.
    395  // Step 3.
    396  Rooted<ErrorObject*> obj(
    397      cx, CreateErrorObject(cx, args, 1, JSEXN_AGGREGATEERR, proto));
    398  if (!obj) {
    399    return false;
    400  }
    401 
    402  // Step 4.
    403 
    404  Rooted<ArrayObject*> errorsList(cx);
    405  if (!IterableToArray(cx, args.get(0), &errorsList)) {
    406    return false;
    407  }
    408 
    409  // Step 5.
    410  RootedValue errorsVal(cx, JS::ObjectValue(*errorsList));
    411  if (!NativeDefineDataProperty(cx, obj, cx->names().errors, errorsVal, 0)) {
    412    return false;
    413  }
    414 
    415  // Step 6.
    416  args.rval().setObject(*obj);
    417  return true;
    418 }
    419 
    420 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    421 // Explicit Resource Management Proposal
    422 // SuppressedError ( error, suppressed, message )
    423 // https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-suppressederror
    424 static bool SuppressedError(JSContext* cx, unsigned argc, Value* vp) {
    425  CallArgs args = CallArgsFromVp(argc, vp);
    426 
    427  mozilla::DebugOnly<JSExnType> exnType =
    428      JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32());
    429 
    430  MOZ_ASSERT(exnType == JSEXN_SUPPRESSEDERR);
    431 
    432  // Step 1. If NewTarget is undefined, let newTarget be the active function
    433  // object; else let newTarget be NewTarget.
    434  // Step 2. Let O be ? OrdinaryCreateFromConstructor(newTarget,
    435  // "%SuppressedError.prototype%", « [[ErrorData]] »).
    436  JS::Rooted<JSObject*> proto(cx);
    437 
    438  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_SuppressedError,
    439                                          &proto)) {
    440    return false;
    441  }
    442 
    443  // Step 3. If message is not undefined, then
    444  // Step 3.a. Let messageString be ? ToString(message).
    445  // Step 3.b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message",
    446  // messageString).
    447  JS::Rooted<ErrorObject*> obj(
    448      cx, CreateErrorObject(cx, args, 2, JSEXN_SUPPRESSEDERR, proto));
    449 
    450  if (!obj) {
    451    return false;
    452  }
    453 
    454  // Step 4. Perform CreateNonEnumerableDataPropertyOrThrow(O, "error", error).
    455  JS::Rooted<JS::Value> errorVal(cx, args.get(0));
    456  if (!NativeDefineDataProperty(cx, obj, cx->names().error, errorVal, 0)) {
    457    return false;
    458  }
    459 
    460  // Step 5. Perform CreateNonEnumerableDataPropertyOrThrow(O, "suppressed",
    461  // suppressed).
    462  JS::Rooted<JS::Value> suppressedVal(cx, args.get(1));
    463  if (!NativeDefineDataProperty(cx, obj, cx->names().suppressed, suppressedVal,
    464                                0)) {
    465    return false;
    466  }
    467 
    468  // Step 6. Return O.
    469  args.rval().setObject(*obj);
    470  return true;
    471 }
    472 #endif
    473 
    474 /* static */
    475 JSObject* ErrorObject::createProto(JSContext* cx, JSProtoKey key) {
    476  JSExnType type = ExnTypeFromProtoKey(key);
    477 
    478  if (type == JSEXN_ERR) {
    479    return GlobalObject::createBlankPrototype(
    480        cx, cx->global(), &ErrorObject::protoClasses[JSEXN_ERR]);
    481  }
    482 
    483  RootedObject protoProto(
    484      cx, GlobalObject::getOrCreateErrorPrototype(cx, cx->global()));
    485  if (!protoProto) {
    486    return nullptr;
    487  }
    488 
    489  return GlobalObject::createBlankPrototypeInheriting(
    490      cx, &ErrorObject::protoClasses[type], protoProto);
    491 }
    492 
    493 /* static */
    494 JSObject* ErrorObject::createConstructor(JSContext* cx, JSProtoKey key) {
    495  JSExnType type = ExnTypeFromProtoKey(key);
    496  RootedObject ctor(cx);
    497 
    498  if (type == JSEXN_ERR) {
    499    ctor = GenericCreateConstructor<Error, 1, gc::AllocKind::FUNCTION_EXTENDED>(
    500        cx, key);
    501  } else {
    502    RootedFunction proto(
    503        cx, GlobalObject::getOrCreateErrorConstructor(cx, cx->global()));
    504    if (!proto) {
    505      return nullptr;
    506    }
    507 
    508    Native native;
    509    unsigned nargs;
    510    if (type == JSEXN_AGGREGATEERR) {
    511      native = AggregateError;
    512      nargs = 2;
    513    }
    514 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    515    else if (type == JSEXN_SUPPRESSEDERR) {
    516      native = SuppressedError;
    517      nargs = 3;
    518    }
    519 #endif
    520    else {
    521      native = Error;
    522      nargs = 1;
    523    }
    524 
    525    ctor =
    526        NewFunctionWithProto(cx, native, nargs, FunctionFlags::NATIVE_CTOR,
    527                             nullptr, ClassName(key, cx), proto,
    528                             gc::AllocKind::FUNCTION_EXTENDED, TenuredObject);
    529  }
    530 
    531  if (!ctor) {
    532    return nullptr;
    533  }
    534 
    535  ctor->as<JSFunction>().setExtendedSlot(0, Int32Value(type));
    536  return ctor;
    537 }
    538 
    539 /* static */
    540 SharedShape* js::ErrorObject::assignInitialShape(JSContext* cx,
    541                                                 Handle<ErrorObject*> obj) {
    542  MOZ_ASSERT(obj->empty());
    543 
    544  constexpr PropertyFlags propFlags = {PropertyFlag::Configurable,
    545                                       PropertyFlag::Writable};
    546 
    547  if (!NativeObject::addPropertyInReservedSlot(cx, obj, cx->names().fileName,
    548                                               FILENAME_SLOT, propFlags)) {
    549    return nullptr;
    550  }
    551 
    552  if (!NativeObject::addPropertyInReservedSlot(cx, obj, cx->names().lineNumber,
    553                                               LINENUMBER_SLOT, propFlags)) {
    554    return nullptr;
    555  }
    556 
    557  if (!NativeObject::addPropertyInReservedSlot(
    558          cx, obj, cx->names().columnNumber, COLUMNNUMBER_SLOT, propFlags)) {
    559    return nullptr;
    560  }
    561 
    562  return obj->sharedShape();
    563 }
    564 
    565 /* static */
    566 bool js::ErrorObject::init(JSContext* cx, Handle<ErrorObject*> obj,
    567                           JSExnType type, UniquePtr<JSErrorReport> errorReport,
    568                           HandleString fileName, HandleObject stack,
    569                           uint32_t sourceId, uint32_t lineNumber,
    570                           JS::ColumnNumberOneOrigin columnNumber,
    571                           HandleString message,
    572                           Handle<mozilla::Maybe<JS::Value>> cause) {
    573  MOZ_ASSERT(JSEXN_ERR <= type && type < JSEXN_ERROR_LIMIT);
    574  AssertObjectIsSavedFrameOrWrapper(cx, stack);
    575  cx->check(obj, stack);
    576 
    577  // Null out early in case of error, for exn_finalize's sake.
    578  obj->initReservedSlot(ERROR_REPORT_SLOT, PrivateValue(nullptr));
    579 
    580  if (!SharedShape::ensureInitialCustomShape<ErrorObject>(cx, obj)) {
    581    return false;
    582  }
    583 
    584  // The .message property isn't part of the initial shape because it's
    585  // present in some error objects -- |Error.prototype|, |new Error("f")|,
    586  // |new Error("")| -- but not in others -- |new Error(undefined)|,
    587  // |new Error()|.
    588  if (message) {
    589    constexpr PropertyFlags propFlags = {PropertyFlag::Configurable,
    590                                         PropertyFlag::Writable};
    591    if (!NativeObject::addPropertyInReservedSlot(cx, obj, cx->names().message,
    592                                                 MESSAGE_SLOT, propFlags)) {
    593      return false;
    594    }
    595  }
    596 
    597  // Similar to the .message property, .cause is present only in some error
    598  // objects -- |new Error("f", {cause: cause})| -- but not in other --
    599  // |Error.prototype|, |new Error()|, |new Error("f")|.
    600  if (cause.isSome()) {
    601    constexpr PropertyFlags propFlags = {PropertyFlag::Configurable,
    602                                         PropertyFlag::Writable};
    603    if (!NativeObject::addPropertyInReservedSlot(cx, obj, cx->names().cause,
    604                                                 CAUSE_SLOT, propFlags)) {
    605      return false;
    606    }
    607  }
    608 
    609  MOZ_ASSERT(obj->lookupPure(NameToId(cx->names().fileName))->slot() ==
    610             FILENAME_SLOT);
    611  MOZ_ASSERT(obj->lookupPure(NameToId(cx->names().lineNumber))->slot() ==
    612             LINENUMBER_SLOT);
    613  MOZ_ASSERT(obj->lookupPure(NameToId(cx->names().columnNumber))->slot() ==
    614             COLUMNNUMBER_SLOT);
    615  MOZ_ASSERT_IF(
    616      message,
    617      obj->lookupPure(NameToId(cx->names().message))->slot() == MESSAGE_SLOT);
    618  MOZ_ASSERT_IF(
    619      cause.isSome(),
    620      obj->lookupPure(NameToId(cx->names().cause))->slot() == CAUSE_SLOT);
    621 
    622  JSErrorReport* report = errorReport.release();
    623  obj->initReservedSlot(STACK_SLOT, ObjectOrNullValue(stack));
    624  obj->setReservedSlot(ERROR_REPORT_SLOT, PrivateValue(report));
    625  obj->initReservedSlot(FILENAME_SLOT, StringValue(fileName));
    626  obj->initReservedSlot(LINENUMBER_SLOT, Int32Value(lineNumber));
    627  obj->initReservedSlot(COLUMNNUMBER_SLOT,
    628                        Int32Value(columnNumber.oneOriginValue()));
    629  if (message) {
    630    obj->initReservedSlot(MESSAGE_SLOT, StringValue(message));
    631  }
    632  if (cause.isSome()) {
    633    obj->initReservedSlot(CAUSE_SLOT, *cause.get());
    634  } else {
    635    obj->initReservedSlot(CAUSE_SLOT, MagicValue(JS_ERROR_WITHOUT_CAUSE));
    636  }
    637  obj->initReservedSlot(SOURCEID_SLOT, Int32Value(sourceId));
    638  if (obj->mightBeWasmTrap()) {
    639    MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(obj->getClass()) > WASM_TRAP_SLOT);
    640    obj->initReservedSlot(WASM_TRAP_SLOT, BooleanValue(false));
    641  }
    642 
    643  return true;
    644 }
    645 
    646 /* static */
    647 ErrorObject* js::ErrorObject::create(JSContext* cx, JSExnType errorType,
    648                                     HandleObject stack, HandleString fileName,
    649                                     uint32_t sourceId, uint32_t lineNumber,
    650                                     JS::ColumnNumberOneOrigin columnNumber,
    651                                     UniquePtr<JSErrorReport> report,
    652                                     HandleString message,
    653                                     Handle<mozilla::Maybe<JS::Value>> cause,
    654                                     HandleObject protoArg /* = nullptr */) {
    655  AssertObjectIsSavedFrameOrWrapper(cx, stack);
    656 
    657  RootedObject proto(cx, protoArg);
    658  if (!proto) {
    659    proto = GlobalObject::getOrCreateCustomErrorPrototype(cx, cx->global(),
    660                                                          errorType);
    661    if (!proto) {
    662      return nullptr;
    663    }
    664  }
    665 
    666  Rooted<ErrorObject*> errObject(cx);
    667  {
    668    const JSClass* clasp = ErrorObject::classForType(errorType);
    669    JSObject* obj = NewObjectWithGivenProto(cx, clasp, proto);
    670    if (!obj) {
    671      return nullptr;
    672    }
    673    errObject = &obj->as<ErrorObject>();
    674  }
    675 
    676  if (!ErrorObject::init(cx, errObject, errorType, std::move(report), fileName,
    677                         stack, sourceId, lineNumber, columnNumber, message,
    678                         cause)) {
    679    return nullptr;
    680  }
    681 
    682  return errObject;
    683 }
    684 
    685 JSErrorReport* js::ErrorObject::getOrCreateErrorReport(JSContext* cx) {
    686  if (JSErrorReport* r = getErrorReport()) {
    687    return r;
    688  }
    689 
    690  // We build an error report on the stack and then use CopyErrorReport to do
    691  // the nitty-gritty malloc stuff.
    692  JSErrorReport report;
    693 
    694  // Type.
    695  JSExnType type_ = type();
    696  report.exnType = type_;
    697 
    698  // Filename.
    699  RootedString filename(cx, fileName(cx));
    700  UniqueChars filenameStr = JS_EncodeStringToUTF8(cx, filename);
    701  if (!filenameStr) {
    702    return nullptr;
    703  }
    704  report.filename = JS::ConstUTF8CharsZ(filenameStr.get());
    705 
    706  // Coordinates.
    707  report.sourceId = sourceId();
    708  report.lineno = lineNumber();
    709  report.column = columnNumber();
    710 
    711  // Message. Note that |new Error()| will result in an undefined |message|
    712  // slot, so we need to explicitly substitute the empty string in that case.
    713  RootedString message(cx, getMessage());
    714  if (!message) {
    715    message = cx->runtime()->emptyString;
    716  }
    717 
    718  UniqueChars utf8 = StringToNewUTF8CharsZ(cx, *message);
    719  if (!utf8) {
    720    return nullptr;
    721  }
    722  report.initOwnedMessage(utf8.release());
    723 
    724  // Cache and return.
    725  UniquePtr<JSErrorReport> copy = CopyErrorReport(cx, &report);
    726  if (!copy) {
    727    return nullptr;
    728  }
    729  setReservedSlot(ERROR_REPORT_SLOT, PrivateValue(copy.get()));
    730  return copy.release();
    731 }
    732 
    733 static bool FindErrorInstanceOrPrototype(JSContext* cx, HandleObject obj,
    734                                         MutableHandleObject result) {
    735  // Walk up the prototype chain until we find an error object instance or
    736  // prototype object. This allows code like:
    737  //  Object.create(Error.prototype).stack
    738  // or
    739  //   function NYI() { }
    740  //   NYI.prototype = new Error;
    741  //   (new NYI).stack
    742  // to continue returning stacks that are useless, but at least don't throw.
    743 
    744  RootedObject curr(cx, obj);
    745  RootedObject target(cx);
    746  do {
    747    target = CheckedUnwrapStatic(curr);
    748    if (!target) {
    749      ReportAccessDenied(cx);
    750      return false;
    751    }
    752    if (IsErrorProtoKey(StandardProtoKeyOrNull(target))) {
    753      result.set(target);
    754      return true;
    755    }
    756 
    757    if (!GetPrototype(cx, curr, &curr)) {
    758      return false;
    759    }
    760  } while (curr);
    761 
    762  // We walked the whole prototype chain and did not find an Error
    763  // object.
    764  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    765                            JSMSG_INCOMPATIBLE_PROTO, "Error", "(get stack)",
    766                            obj->getClass()->name);
    767  return false;
    768 }
    769 
    770 static MOZ_ALWAYS_INLINE bool IsObject(HandleValue v) { return v.isObject(); }
    771 
    772 /* static */
    773 bool js::ErrorObject::getStack(JSContext* cx, unsigned argc, Value* vp) {
    774  CallArgs args = CallArgsFromVp(argc, vp);
    775  // We accept any object here, because of poor-man's subclassing of Error.
    776  return CallNonGenericMethod<IsObject, getStack_impl>(cx, args);
    777 }
    778 
    779 /* static */
    780 bool js::ErrorObject::getStack_impl(JSContext* cx, const CallArgs& args) {
    781  RootedObject thisObj(cx, &args.thisv().toObject());
    782 
    783  RootedObject obj(cx);
    784  if (!FindErrorInstanceOrPrototype(cx, thisObj, &obj)) {
    785    return false;
    786  }
    787 
    788  if (!obj->is<ErrorObject>()) {
    789    args.rval().setString(cx->runtime()->emptyString);
    790    return true;
    791  }
    792 
    793  // Do frame filtering based on the ErrorObject's principals. This ensures we
    794  // don't see chrome frames when chrome code accesses .stack over Xrays.
    795  JSPrincipals* principals = obj->as<ErrorObject>().realm()->principals();
    796 
    797  RootedObject savedFrameObj(cx, obj->as<ErrorObject>().stack());
    798  RootedString stackString(cx);
    799  if (!BuildStackString(cx, principals, savedFrameObj, &stackString)) {
    800    return false;
    801  }
    802 
    803  if (cx->runtime()->stackFormat() == js::StackFormat::V8) {
    804    // When emulating V8 stack frames, we also need to prepend the
    805    // stringified Error to the stack string.
    806    Handle<PropertyName*> name = cx->names().ErrorToStringWithTrailingNewline;
    807    FixedInvokeArgs<0> args2(cx);
    808    RootedValue rval(cx);
    809    if (!CallSelfHostedFunction(cx, name, args.thisv(), args2, &rval)) {
    810      return false;
    811    }
    812 
    813    if (!rval.isString()) {
    814      args.rval().setString(cx->runtime()->emptyString);
    815      return true;
    816    }
    817 
    818    RootedString stringified(cx, rval.toString());
    819    stackString = ConcatStrings<CanGC>(cx, stringified, stackString);
    820  }
    821 
    822  args.rval().setString(stackString);
    823  return true;
    824 }
    825 
    826 /* static */
    827 bool js::ErrorObject::setStack(JSContext* cx, unsigned argc, Value* vp) {
    828  CallArgs args = CallArgsFromVp(argc, vp);
    829  // We accept any object here, because of poor-man's subclassing of Error.
    830  return CallNonGenericMethod<IsObject, setStack_impl>(cx, args);
    831 }
    832 
    833 /* static */
    834 bool js::ErrorObject::setStack_impl(JSContext* cx, const CallArgs& args) {
    835  RootedObject thisObj(cx, &args.thisv().toObject());
    836 
    837  if (!args.requireAtLeast(cx, "(set stack)", 1)) {
    838    return false;
    839  }
    840 
    841  return DefineDataProperty(cx, thisObj, cx->names().stack, args[0]);
    842 }
    843 
    844 void js::ErrorObject::setFromWasmTrap() {
    845  MOZ_ASSERT(mightBeWasmTrap());
    846  MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(getClass()) > WASM_TRAP_SLOT);
    847  setReservedSlot(WASM_TRAP_SLOT, BooleanValue(true));
    848 }
    849 
    850 JSString* js::ErrorToSource(JSContext* cx, HandleObject obj) {
    851  AutoCycleDetector detector(cx, obj);
    852  if (!detector.init()) {
    853    return nullptr;
    854  }
    855  if (detector.foundCycle()) {
    856    return NewStringCopyZ<CanGC>(cx, "{}");
    857  }
    858 
    859  RootedValue nameVal(cx);
    860  RootedString name(cx);
    861  if (!GetProperty(cx, obj, obj, cx->names().name, &nameVal) ||
    862      !(name = ToString<CanGC>(cx, nameVal))) {
    863    return nullptr;
    864  }
    865 
    866  RootedValue messageVal(cx);
    867  RootedString message(cx);
    868  if (!GetProperty(cx, obj, obj, cx->names().message, &messageVal) ||
    869      !(message = ValueToSource(cx, messageVal))) {
    870    return nullptr;
    871  }
    872 
    873  RootedValue filenameVal(cx);
    874  RootedString filename(cx);
    875  if (!GetProperty(cx, obj, obj, cx->names().fileName, &filenameVal) ||
    876      !(filename = ValueToSource(cx, filenameVal))) {
    877    return nullptr;
    878  }
    879 
    880  RootedValue errorsVal(cx);
    881  RootedString errors(cx);
    882  bool isAggregateError = obj->is<ErrorObject>() &&
    883                          obj->as<ErrorObject>().type() == JSEXN_AGGREGATEERR;
    884  if (isAggregateError) {
    885    if (!GetProperty(cx, obj, obj, cx->names().errors, &errorsVal) ||
    886        !(errors = ValueToSource(cx, errorsVal))) {
    887      return nullptr;
    888    }
    889  }
    890 
    891  RootedValue linenoVal(cx);
    892  uint32_t lineno;
    893  if (!GetProperty(cx, obj, obj, cx->names().lineNumber, &linenoVal) ||
    894      !ToUint32(cx, linenoVal, &lineno)) {
    895    return nullptr;
    896  }
    897 
    898  JSStringBuilder sb(cx);
    899  if (!sb.append("(new ") || !sb.append(name) || !sb.append("(")) {
    900    return nullptr;
    901  }
    902 
    903  if (isAggregateError) {
    904    if (!sb.append(errors) || !sb.append(", ")) {
    905      return nullptr;
    906    }
    907  }
    908 
    909  if (!sb.append(message)) {
    910    return nullptr;
    911  }
    912 
    913  if (!filename->empty()) {
    914    if (!sb.append(", ") || !sb.append(filename)) {
    915      return nullptr;
    916    }
    917  }
    918  if (lineno != 0) {
    919    /* We have a line, but no filename, add empty string */
    920    if (filename->empty() && !sb.append(", \"\"")) {
    921      return nullptr;
    922    }
    923 
    924    JSString* linenumber = ToString<CanGC>(cx, linenoVal);
    925    if (!linenumber) {
    926      return nullptr;
    927    }
    928    if (!sb.append(", ") || !sb.append(linenumber)) {
    929      return nullptr;
    930    }
    931  }
    932 
    933  if (!sb.append("))")) {
    934    return nullptr;
    935  }
    936 
    937  return sb.finishString();
    938 }
    939 
    940 /*
    941 * Return a string that may eval to something similar to the original object.
    942 */
    943 static bool exn_toSource(JSContext* cx, unsigned argc, Value* vp) {
    944  AutoCheckRecursionLimit recursion(cx);
    945  if (!recursion.check(cx)) {
    946    return false;
    947  }
    948  CallArgs args = CallArgsFromVp(argc, vp);
    949 
    950  RootedObject obj(cx, ToObject(cx, args.thisv()));
    951  if (!obj) {
    952    return false;
    953  }
    954 
    955  JSString* str = ErrorToSource(cx, obj);
    956  if (!str) {
    957    return false;
    958  }
    959 
    960  args.rval().setString(str);
    961  return true;
    962 }
    963 
    964 /**
    965 * Error.isError Proposal
    966 * Error.isError ( arg )
    967 * https://tc39.es/proposal-is-error/#sec-error.iserror
    968 * IsError ( argument )
    969 * https://tc39.es/proposal-is-error/#sec-iserror
    970 */
    971 static bool exn_isError(JSContext* cx, unsigned argc, Value* vp) {
    972  CallArgs args = CallArgsFromVp(argc, vp);
    973 
    974  // Error.isError ( arg )
    975  // Step 1. Return IsError(arg).
    976 
    977  // IsError ( argument )
    978  // Step 1. If argument is not an Object, return false.
    979  if (!args.get(0).isObject()) {
    980    args.rval().setBoolean(false);
    981    return true;
    982  }
    983 
    984  JSObject* unwrappedObject = CheckedUnwrapStatic(&args.get(0).toObject());
    985  if (!unwrappedObject) {
    986    JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
    987                             JSMSG_OBJECT_ACCESS_DENIED);
    988    return false;
    989  }
    990 
    991  if (JS_IsDeadWrapper(unwrappedObject)) {
    992    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
    993    return false;
    994  }
    995 
    996  // Step 2. If argument has an [[ErrorData]] internal slot, return true.
    997  if (unwrappedObject->is<ErrorObject>()) {
    998    args.rval().setBoolean(true);
    999    return true;
   1000  }
   1001  if (unwrappedObject->getClass()->isDOMClass()) {
   1002    args.rval().setBoolean(cx->runtime()->DOMcallbacks->instanceClassIsError(
   1003        unwrappedObject->getClass()));
   1004    return true;
   1005  }
   1006 
   1007  // Step 3. Return false
   1008  args.rval().setBoolean(false);
   1009  return true;
   1010 }
   1011 
   1012 // The below is the "documentation" from https://v8.dev/docs/stack-trace-api
   1013 //
   1014 //  ## Stack trace collection for custom exceptions
   1015 //
   1016 //  The stack trace mechanism used for built-in errors is implemented using a
   1017 //  general stack trace collection API that is also available to user scripts.
   1018 //  The function
   1019 //
   1020 //   Error.captureStackTrace(error, constructorOpt)
   1021 //
   1022 //  adds a stack property to the given error object that yields the stack trace
   1023 //  at the time captureStackTrace was called. Stack traces collected through
   1024 //  Error.captureStackTrace are immediately collected, formatted, and attached
   1025 //  to the given error object.
   1026 //
   1027 //  The optional constructorOpt parameter allows you to pass in a function
   1028 //  value. When collecting the stack trace all frames above the topmost call to
   1029 //  this function, including that call, are left out of the stack trace. This
   1030 //  can be useful to hide implementation details that won’t be useful to the
   1031 //  user. The usual way of defining a custom error that captures a stack trace
   1032 //  would be:
   1033 //
   1034 //   function MyError() {
   1035 //     Error.captureStackTrace(this, MyError);
   1036 //     // Any other initialization goes here.
   1037 //   }
   1038 //
   1039 //  Passing in MyError as a second argument means that the constructor call to
   1040 //  MyError won’t show up in the stack trace.
   1041 
   1042 static bool exn_captureStackTrace(JSContext* cx, unsigned argc, Value* vp) {
   1043  CallArgs args = CallArgsFromVp(argc, vp);
   1044  const char* callerName = "Error.captureStackTrace";
   1045 
   1046  if (!args.requireAtLeast(cx, callerName, 1)) {
   1047    return false;
   1048  }
   1049 
   1050  Rooted<JSObject*> obj(cx,
   1051                        RequireObjectArg(cx, "`target`", callerName, args[0]));
   1052  if (!obj) {
   1053    return false;
   1054  }
   1055 
   1056  Rooted<JSObject*> caller(cx, nullptr);
   1057  if (args.length() > 1 && args[1].isObject() &&
   1058      args[1].toObject().isCallable()) {
   1059    caller = CheckedUnwrapStatic(&args[1].toObject());
   1060    if (!caller) {
   1061      ReportAccessDenied(cx);
   1062      return false;
   1063    }
   1064  }
   1065 
   1066  RootedObject stack(cx);
   1067  if (!CaptureCurrentStack(
   1068          cx, &stack, JS::StackCapture(JS::MaxFrames(MAX_REPORTED_STACK_DEPTH)),
   1069          caller)) {
   1070    return false;
   1071  }
   1072 
   1073  RootedString stackString(cx);
   1074 
   1075  // Do frame filtering based on the current realm, to filter out any
   1076  // chrome frames which could exist on the stack.
   1077  JSPrincipals* principals = cx->realm()->principals();
   1078  if (!BuildStackString(cx, principals, stack, &stackString)) {
   1079    return false;
   1080  }
   1081 
   1082  // V8 installs a non-enumerable, configurable getter-setter on the object.
   1083  // JSC installs a non-enumerable, configurable, writable value on the
   1084  // object. We are following JSC here, not V8.
   1085  RootedValue string(cx, StringValue(stackString));
   1086  if (!DefineDataProperty(cx, obj, cx->names().stack, string, 0)) {
   1087    return false;
   1088  }
   1089 
   1090  args.rval().setUndefined();
   1091  return true;
   1092 }