Bug 1568903 - Part 4: Implement AggregateError for Nightly. r=jorendorff
☠☠ backed out by cfe990fab350 ☠ ☠
authorAndré Bargull <andre.bargull@gmail.com>
Fri, 15 Nov 2019 10:29:30 +0000
changeset 502140 127a44494b67989bfe3afcd02ee48041ca566f16
parent 502139 27c54b414c35ad96a4319eba9ba2da7782fb37e4
child 502141 567d497a39f42a347f0cdddb720cfc68256167a2
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1568903
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1568903 - Part 4: Implement AggregateError for Nightly. r=jorendorff Adds AggregateError, but only enables it for Nightly builds, because the draft proposal is still incomplete, so it doesn't make sense to let this feature ride the trains at this moment. - The `other_error_properties` array was changed to individual static variables, because AggregateError has more than three properties, which prevents it to be stored in `JSPropertySpec[][3]`. - `AggregateErrorObject` can't use the normal `ErrorObject` class, because it needs an additional slot for the [[AggregateErrors]]. - For similar reasons it can't use the shared `Error` constructor function, because the `AggregateError` constructor has an additional `errors` iterable argument which it needs to process. Differential Revision: https://phabricator.services.mozilla.com/D51653
js/public/ErrorReport.h
js/public/ProtoKey.h
js/src/jsexn.h
js/src/tests/non262/Error/AggregateError.js
js/src/vm/ErrorObject.cpp
js/src/vm/ErrorObject.h
js/src/vm/GlobalObject.cpp
js/xpconnect/wrappers/XrayWrapper.cpp
--- a/js/public/ErrorReport.h
+++ b/js/public/ErrorReport.h
@@ -41,16 +41,17 @@ class JS_PUBLIC_API JSString;
  * JSEXN_WARN is used for warnings in js.msg files (for instance because we
  * don't want to prepend 'Error:' to warning messages). This value can go away
  * if we ever decide to use an entirely separate mechanism for warnings.
  */
 enum JSExnType {
   JSEXN_ERR,
   JSEXN_FIRST = JSEXN_ERR,
   JSEXN_INTERNALERR,
+  JSEXN_AGGREGATEERR,
   JSEXN_EVALERR,
   JSEXN_RANGEERR,
   JSEXN_REFERENCEERR,
   JSEXN_SYNTAXERR,
   JSEXN_TYPEERR,
   JSEXN_URIERR,
   JSEXN_DEBUGGEEWOULDRUN,
   JSEXN_WASMCOMPILEERROR,
--- a/js/public/ProtoKey.h
+++ b/js/public/ProtoKey.h
@@ -57,16 +57,17 @@
   REAL(JSON, InitJSONClass, CLASP(JSON))                                     \
   REAL(Date, InitViaClassSpec, OCLASP(Date))                                 \
   REAL(Math, InitMathClass, CLASP(Math))                                     \
   REAL(Number, InitNumberClass, OCLASP(Number))                              \
   REAL(String, InitStringClass, OCLASP(String))                              \
   REAL(RegExp, InitViaClassSpec, OCLASP(RegExp))                             \
   REAL(Error, InitViaClassSpec, ERROR_CLASP(JSEXN_ERR))                      \
   REAL(InternalError, InitViaClassSpec, ERROR_CLASP(JSEXN_INTERNALERR))      \
+  REAL(AggregateError, InitViaClassSpec, ERROR_CLASP(JSEXN_AGGREGATEERR))    \
   REAL(EvalError, InitViaClassSpec, ERROR_CLASP(JSEXN_EVALERR))              \
   REAL(RangeError, InitViaClassSpec, ERROR_CLASP(JSEXN_RANGEERR))            \
   REAL(ReferenceError, InitViaClassSpec, ERROR_CLASP(JSEXN_REFERENCEERR))    \
   REAL(SyntaxError, InitViaClassSpec, ERROR_CLASP(JSEXN_SYNTAXERR))          \
   REAL(TypeError, InitViaClassSpec, ERROR_CLASP(JSEXN_TYPEERR))              \
   REAL(URIError, InitViaClassSpec, ERROR_CLASP(JSEXN_URIERR))                \
   REAL(DebuggeeWouldRun, InitViaClassSpec,                                   \
        ERROR_CLASP(JSEXN_DEBUGGEEWOULDRUN))                                  \
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -63,16 +63,17 @@ extern JSErrorReport* ErrorFromException
  * prototype objects (errobj->getPrivate() must not be nullptr).
  */
 extern JSObject* CopyErrorObject(JSContext* cx,
                                  JS::Handle<ErrorObject*> errobj);
 
 static_assert(
     JSEXN_ERR == 0 &&
         JSProto_Error + JSEXN_INTERNALERR == JSProto_InternalError &&
+        JSProto_Error + JSEXN_AGGREGATEERR == JSProto_AggregateError &&
         JSProto_Error + JSEXN_EVALERR == JSProto_EvalError &&
         JSProto_Error + JSEXN_RANGEERR == JSProto_RangeError &&
         JSProto_Error + JSEXN_REFERENCEERR == JSProto_ReferenceError &&
         JSProto_Error + JSEXN_SYNTAXERR == JSProto_SyntaxError &&
         JSProto_Error + JSEXN_TYPEERR == JSProto_TypeError &&
         JSProto_Error + JSEXN_URIERR == JSProto_URIError &&
         JSProto_Error + JSEXN_DEBUGGEEWOULDRUN == JSProto_DebuggeeWouldRun &&
         JSProto_Error + JSEXN_WASMCOMPILEERROR == JSProto_CompileError &&
new file mode 100644
--- /dev/null
+++ b/js/src/tests/non262/Error/AggregateError.js
@@ -0,0 +1,82 @@
+// |reftest| skip-if(release_or_beta)
+
+assertEq(typeof AggregateError, "function");
+assertEq(Object.getPrototypeOf(AggregateError), Error);
+assertEq(AggregateError.name, "AggregateError");
+assertEq(AggregateError.length, 2);
+
+assertEq(Object.getPrototypeOf(AggregateError.prototype), Error.prototype);
+assertEq(AggregateError.prototype.name, "AggregateError");
+assertEq(AggregateError.prototype.message, "");
+
+// AggregateError.prototype isn't an AggregateError instance.
+assertThrowsInstanceOf(() => AggregateError.prototype.errors, TypeError);
+
+// The |errors| argument is mandatory.
+assertThrowsInstanceOf(() => new AggregateError(), TypeError);
+assertThrowsInstanceOf(() => AggregateError(), TypeError);
+
+// The .errors getter returns an array object.
+{
+  let err = new AggregateError([]);
+
+  let {errors} = err;
+  assertEq(Array.isArray(errors), true);
+  assertEq(errors.length, 0);
+
+  // A fresh object is returned each time calling the getter.
+  assertEq(errors === err.errors, false);
+
+  // The errors object is modifiable.
+  errors.push(123);
+  assertEq(errors.length, 1);
+  assertEq(errors[0], 123);
+}
+
+// The errors argument can be any iterable.
+{
+  function* g() { yield* [1, 2, 3]; }
+
+  let {errors} = new AggregateError(g());
+  assertEqArray(errors, [1, 2, 3]);
+}
+
+// The message property is populated by the second argument.
+{
+  let err;
+
+  err = new AggregateError([]);
+  assertEq(err.message, "");
+
+  err = new AggregateError([], "my message");
+  assertEq(err.message, "my message");
+}
+
+{
+  const {
+    get: getErrors,
+    set: setErrors,
+  } = Object.getOwnPropertyDescriptor(AggregateError.prototype, "errors");
+  assertEq(typeof getErrors, "function");
+  assertEq(typeof setErrors, "undefined");
+
+  // The |this| argument must be an AggregateError instance.
+  assertThrowsInstanceOf(() => getErrors.call(null), TypeError);
+  assertThrowsInstanceOf(() => getErrors.call({}), TypeError);
+  assertThrowsInstanceOf(() => getErrors.call(new Error), TypeError);
+
+  const g = newGlobal();
+
+  let obj = {};
+  let errors = getErrors.call(new g.AggregateError([obj]));
+
+  assertEq(errors.length, 1);
+  assertEq(errors[0], obj);
+
+  // The prototype is (incorrectly) |g.Array.prototype| in the cross-compartment case.
+  let proto = Object.getPrototypeOf(errors);
+  assertEq(proto === Array.prototype || proto === g.Array.prototype, true);
+}
+
+if (typeof reportCompare === "function")
+  reportCompare(0, 0);
--- a/js/src/vm/ErrorObject.cpp
+++ b/js/src/vm/ErrorObject.cpp
@@ -4,36 +4,39 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/ErrorObject-inl.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
 #include "mozilla/RecordReplay.h"
 
 #include <utility>
 
 #include "jsapi.h"
 #include "jsexn.h"
 #include "jsfriendapi.h"
 #include "jsnum.h"
 #include "jspubtd.h"
 #include "NamespaceImports.h"
 
+#include "builtin/Array.h"
 #include "gc/AllocKind.h"
 #include "gc/FreeOp.h"
 #include "gc/Rooting.h"
 #include "js/CallArgs.h"
 #include "js/CallNonGenericMethod.h"
 #include "js/CharacterEncoding.h"
 #include "js/Class.h"
 #include "js/Conversions.h"
 #include "js/ErrorReport.h"
+#include "js/ForOfIterator.h"
 #include "js/PropertySpec.h"
 #include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 #include "js/Value.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
 #include "vm/GlobalObject.h"
@@ -44,16 +47,17 @@
 #include "vm/ObjectGroup.h"
 #include "vm/ObjectOperations.h"
 #include "vm/SavedStacks.h"
 #include "vm/SelfHosting.h"
 #include "vm/Shape.h"
 #include "vm/Stack.h"
 #include "vm/StringType.h"
 
+#include "vm/ArrayObject-inl.h"
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/ObjectOperations-inl.h"
 #include "vm/SavedStacks-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
@@ -64,16 +68,17 @@ using namespace js;
         JS_NULL_CLASS_OPS,                                       \
         &ErrorObject::classSpecs[JSProto_##name - JSProto_Error] \
   }
 
 const JSClass ErrorObject::protoClasses[JSEXN_ERROR_LIMIT] = {
     IMPLEMENT_ERROR_PROTO_CLASS(Error),
 
     IMPLEMENT_ERROR_PROTO_CLASS(InternalError),
+    IMPLEMENT_ERROR_PROTO_CLASS(AggregateError),
     IMPLEMENT_ERROR_PROTO_CLASS(EvalError),
     IMPLEMENT_ERROR_PROTO_CLASS(RangeError),
     IMPLEMENT_ERROR_PROTO_CLASS(ReferenceError),
     IMPLEMENT_ERROR_PROTO_CLASS(SyntaxError),
     IMPLEMENT_ERROR_PROTO_CLASS(TypeError),
     IMPLEMENT_ERROR_PROTO_CLASS(URIError),
 
     IMPLEMENT_ERROR_PROTO_CLASS(DebuggeeWouldRun),
@@ -82,81 +87,92 @@ const JSClass ErrorObject::protoClasses[
     IMPLEMENT_ERROR_PROTO_CLASS(RuntimeError)};
 
 static bool exn_toSource(JSContext* cx, unsigned argc, Value* vp);
 
 static const JSFunctionSpec error_methods[] = {
     JS_FN(js_toSource_str, exn_toSource, 0, 0),
     JS_SELF_HOSTED_FN(js_toString_str, "ErrorToString", 0, 0), JS_FS_END};
 
+// Error.prototype and NativeError.prototype have own .message and .name
+// properties.
+#define COMMON_ERROR_PROPERTIES(name) \
+  JS_STRING_PS("message", "", 0), JS_STRING_PS("name", #name, 0)
+
 static const JSPropertySpec error_properties[] = {
-    JS_STRING_PS("message", "", 0), JS_STRING_PS("name", "Error", 0),
+    COMMON_ERROR_PROPERTIES(Error),
     // Only Error.prototype has .stack!
     JS_PSGS("stack", ErrorObject::getStack, ErrorObject::setStack, 0),
     JS_PS_END};
 
-#define IMPLEMENT_ERROR_PROPERTIES(name) \
-  { JS_STRING_PS("message", "", 0), JS_STRING_PS("name", #name, 0), JS_PS_END }
+static const JSPropertySpec AggregateError_properties[] = {
+    COMMON_ERROR_PROPERTIES(AggregateError),
+    // Only AggregateError.prototype has .errors!
+    JS_PSG("errors", AggregateErrorObject::getErrors, 0), JS_PS_END};
+
+#define IMPLEMENT_NATIVE_ERROR_PROPERTIES(name)       \
+  static const JSPropertySpec name##_properties[] = { \
+      COMMON_ERROR_PROPERTIES(name), JS_PS_END};
 
-static const JSPropertySpec other_error_properties[JSEXN_ERROR_LIMIT - 1][3] = {
-    IMPLEMENT_ERROR_PROPERTIES(InternalError),
-    IMPLEMENT_ERROR_PROPERTIES(EvalError),
-    IMPLEMENT_ERROR_PROPERTIES(RangeError),
-    IMPLEMENT_ERROR_PROPERTIES(ReferenceError),
-    IMPLEMENT_ERROR_PROPERTIES(SyntaxError),
-    IMPLEMENT_ERROR_PROPERTIES(TypeError),
-    IMPLEMENT_ERROR_PROPERTIES(URIError),
-    IMPLEMENT_ERROR_PROPERTIES(DebuggeeWouldRun),
-    IMPLEMENT_ERROR_PROPERTIES(CompileError),
-    IMPLEMENT_ERROR_PROPERTIES(LinkError),
-    IMPLEMENT_ERROR_PROPERTIES(RuntimeError)};
+IMPLEMENT_NATIVE_ERROR_PROPERTIES(InternalError)
+IMPLEMENT_NATIVE_ERROR_PROPERTIES(EvalError)
+IMPLEMENT_NATIVE_ERROR_PROPERTIES(RangeError)
+IMPLEMENT_NATIVE_ERROR_PROPERTIES(ReferenceError)
+IMPLEMENT_NATIVE_ERROR_PROPERTIES(SyntaxError)
+IMPLEMENT_NATIVE_ERROR_PROPERTIES(TypeError)
+IMPLEMENT_NATIVE_ERROR_PROPERTIES(URIError)
+IMPLEMENT_NATIVE_ERROR_PROPERTIES(DebuggeeWouldRun)
+IMPLEMENT_NATIVE_ERROR_PROPERTIES(CompileError)
+IMPLEMENT_NATIVE_ERROR_PROPERTIES(LinkError)
+IMPLEMENT_NATIVE_ERROR_PROPERTIES(RuntimeError)
 
-#define IMPLEMENT_NATIVE_ERROR_SPEC(name)                                    \
-  {                                                                          \
-    ErrorObject::createConstructor, ErrorObject::createProto, nullptr,       \
-        nullptr, nullptr,                                                    \
-        other_error_properties[JSProto_##name - JSProto_Error - 1], nullptr, \
-        JSProto_Error                                                        \
+#define IMPLEMENT_NATIVE_ERROR_SPEC(name)                              \
+  {                                                                    \
+    ErrorObject::createConstructor, ErrorObject::createProto, nullptr, \
+        nullptr, nullptr, name##_properties, nullptr, JSProto_Error    \
   }
 
-#define IMPLEMENT_NONGLOBAL_ERROR_SPEC(name)                                 \
-  {                                                                          \
-    ErrorObject::createConstructor, ErrorObject::createProto, nullptr,       \
-        nullptr, nullptr,                                                    \
-        other_error_properties[JSProto_##name - JSProto_Error - 1], nullptr, \
-        JSProto_Error | ClassSpec::DontDefineConstructor                     \
+#define IMPLEMENT_NONGLOBAL_ERROR_SPEC(name)                           \
+  {                                                                    \
+    ErrorObject::createConstructor, ErrorObject::createProto, nullptr, \
+        nullptr, nullptr, name##_properties, nullptr,                  \
+        JSProto_Error | ClassSpec::DontDefineConstructor               \
   }
 
 const ClassSpec ErrorObject::classSpecs[JSEXN_ERROR_LIMIT] = {
     {ErrorObject::createConstructor, ErrorObject::createProto, nullptr, nullptr,
      error_methods, error_properties},
 
     IMPLEMENT_NATIVE_ERROR_SPEC(InternalError),
+    IMPLEMENT_NATIVE_ERROR_SPEC(AggregateError),
     IMPLEMENT_NATIVE_ERROR_SPEC(EvalError),
     IMPLEMENT_NATIVE_ERROR_SPEC(RangeError),
     IMPLEMENT_NATIVE_ERROR_SPEC(ReferenceError),
     IMPLEMENT_NATIVE_ERROR_SPEC(SyntaxError),
     IMPLEMENT_NATIVE_ERROR_SPEC(TypeError),
     IMPLEMENT_NATIVE_ERROR_SPEC(URIError),
 
     IMPLEMENT_NONGLOBAL_ERROR_SPEC(DebuggeeWouldRun),
     IMPLEMENT_NONGLOBAL_ERROR_SPEC(CompileError),
     IMPLEMENT_NONGLOBAL_ERROR_SPEC(LinkError),
     IMPLEMENT_NONGLOBAL_ERROR_SPEC(RuntimeError)};
 
-#define IMPLEMENT_ERROR_CLASS(name)                                   \
-  {                                                                   \
-    js_Error_str, /* yes, really */                                   \
-        JSCLASS_HAS_CACHED_PROTO(JSProto_##name) |                    \
-            JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS) | \
-            JSCLASS_BACKGROUND_FINALIZE,                              \
-        &ErrorObjectClassOps,                                         \
-        &ErrorObject::classSpecs[JSProto_##name - JSProto_Error]      \
+#define IMPLEMENT_ERROR_CLASS_FROM(clazz, name)                  \
+  {                                                              \
+    js_Error_str, /* yes, really */                              \
+        JSCLASS_HAS_CACHED_PROTO(JSProto_##name) |               \
+            JSCLASS_HAS_RESERVED_SLOTS(clazz::RESERVED_SLOTS) |  \
+            JSCLASS_BACKGROUND_FINALIZE,                         \
+        &ErrorObjectClassOps,                                    \
+        &ErrorObject::classSpecs[JSProto_##name - JSProto_Error] \
   }
 
+#define IMPLEMENT_ERROR_CLASS(name) \
+  IMPLEMENT_ERROR_CLASS_FROM(ErrorObject, name)
+
 static void exn_finalize(JSFreeOp* fop, JSObject* obj);
 
 static const JSClassOps ErrorObjectClassOps = {
     nullptr,               /* addProperty */
     nullptr,               /* delProperty */
     nullptr,               /* enumerate */
     nullptr,               /* newEnumerate */
     nullptr,               /* resolve */
@@ -164,108 +180,183 @@ static const JSClassOps ErrorObjectClass
     exn_finalize, nullptr, /* call        */
     nullptr,               /* hasInstance */
     nullptr,               /* construct   */
     nullptr,               /* trace       */
 };
 
 const JSClass ErrorObject::classes[JSEXN_ERROR_LIMIT] = {
     IMPLEMENT_ERROR_CLASS(Error), IMPLEMENT_ERROR_CLASS(InternalError),
+    IMPLEMENT_ERROR_CLASS_FROM(AggregateErrorObject, AggregateError),
     IMPLEMENT_ERROR_CLASS(EvalError), IMPLEMENT_ERROR_CLASS(RangeError),
     IMPLEMENT_ERROR_CLASS(ReferenceError), IMPLEMENT_ERROR_CLASS(SyntaxError),
     IMPLEMENT_ERROR_CLASS(TypeError), IMPLEMENT_ERROR_CLASS(URIError),
     // These Error subclasses are not accessible via the global object:
     IMPLEMENT_ERROR_CLASS(DebuggeeWouldRun),
     IMPLEMENT_ERROR_CLASS(CompileError), IMPLEMENT_ERROR_CLASS(LinkError),
     IMPLEMENT_ERROR_CLASS(RuntimeError)};
 
 static void exn_finalize(JSFreeOp* fop, JSObject* obj) {
   MOZ_ASSERT(fop->maybeOnHelperThread());
   if (JSErrorReport* report = obj->as<ErrorObject>().getErrorReport()) {
     // Bug 1560019: This allocation is not currently tracked.
     fop->deleteUntracked(report);
   }
 }
 
-static bool Error(JSContext* cx, unsigned argc, Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
-
-  // ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
-  // called as functions, without operator new.  But as we do not give
-  // each constructor a distinct JSClass, we must get the exception type
-  // ourselves.
-  JSExnType exnType =
-      JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32());
-
-  JSProtoKey protoKey =
-      JSCLASS_CACHED_PROTO_KEY(&ErrorObject::classes[exnType]);
-
-  // ES6 19.5.1.1 mandates the .prototype lookup happens before the toString
-  RootedObject proto(cx);
-  if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey, &proto)) {
-    return false;
-  }
-
+static ErrorObject* CreateErrorObject(JSContext* cx, const CallArgs& args,
+                                      unsigned messageArg, JSExnType exnType,
+                                      HandleObject proto) {
   // Compute the error message, if any.
   RootedString message(cx, nullptr);
-  if (args.hasDefined(0)) {
-    message = ToString<CanGC>(cx, args[0]);
+  if (args.hasDefined(messageArg)) {
+    message = ToString<CanGC>(cx, args[messageArg]);
     if (!message) {
-      return false;
+      return nullptr;
     }
   }
 
   // Find the scripted caller, but only ones we're allowed to know about.
   NonBuiltinFrameIter iter(cx, cx->realm()->principals());
 
   RootedString fileName(cx);
   uint32_t sourceId = 0;
-  if (args.length() > 1) {
-    fileName = ToString<CanGC>(cx, args[1]);
+  if (args.length() > messageArg + 1) {
+    fileName = ToString<CanGC>(cx, args[messageArg + 1]);
   } else {
     fileName = cx->runtime()->emptyString;
     if (!iter.done()) {
       if (const char* cfilename = iter.filename()) {
         fileName = JS_NewStringCopyZ(cx, cfilename);
       }
       if (iter.hasScript()) {
         sourceId = iter.script()->scriptSource()->id();
       }
     }
   }
   if (!fileName) {
-    return false;
+    return nullptr;
   }
 
   uint32_t lineNumber, columnNumber = 0;
-  if (args.length() > 2) {
-    if (!ToUint32(cx, args[2], &lineNumber)) {
-      return false;
+  if (args.length() > messageArg + 2) {
+    if (!ToUint32(cx, args[messageArg + 2], &lineNumber)) {
+      return nullptr;
     }
   } else {
     lineNumber = iter.done() ? 0 : iter.computeLine(&columnNumber);
     columnNumber = FixupColumnForDisplay(columnNumber);
   }
 
   RootedObject stack(cx);
   if (!CaptureStack(cx, &stack)) {
+    return nullptr;
+  }
+
+  return ErrorObject::create(cx, exnType, stack, fileName, sourceId, lineNumber,
+                             columnNumber, nullptr, message, proto);
+}
+
+static bool Error(JSContext* cx, unsigned argc, Value* vp) {
+  CallArgs args = CallArgsFromVp(argc, vp);
+
+  // ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
+  // called as functions, without operator new.  But as we do not give
+  // each constructor a distinct JSClass, we must get the exception type
+  // ourselves.
+  JSExnType exnType =
+      JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32());
+
+  MOZ_ASSERT(exnType != JSEXN_AGGREGATEERR,
+             "AggregateError has its own constructor function");
+
+  JSProtoKey protoKey =
+      JSCLASS_CACHED_PROTO_KEY(&ErrorObject::classes[exnType]);
+
+  // ES6 19.5.1.1 mandates the .prototype lookup happens before the toString
+  RootedObject proto(cx);
+  if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey, &proto)) {
     return false;
   }
 
-  RootedObject obj(cx, ErrorObject::create(cx, exnType, stack, fileName,
-                                           sourceId, lineNumber, columnNumber,
-                                           nullptr, message, proto));
+  auto* obj = CreateErrorObject(cx, args, 0, exnType, proto);
   if (!obj) {
     return false;
   }
 
   args.rval().setObject(*obj);
   return true;
 }
 
+static ArrayObject* IterableToArray(JSContext* cx, HandleValue iterable) {
+  JS::ForOfIterator iterator(cx);
+  if (!iterator.init(iterable, JS::ForOfIterator::ThrowOnNonIterable)) {
+    return nullptr;
+  }
+
+  RootedArrayObject array(cx, NewDenseEmptyArray(cx));
+
+  RootedValue nextValue(cx);
+  while (true) {
+    bool done;
+    if (!iterator.next(&nextValue, &done)) {
+      return nullptr;
+    }
+    if (done) {
+      return array;
+    }
+
+    if (!NewbornArrayPush(cx, array, nextValue)) {
+      return nullptr;
+    }
+  }
+}
+
+// AggregateError ( errors, message )
+static bool AggregateError(JSContext* cx, unsigned argc, Value* vp) {
+  CallArgs args = CallArgsFromVp(argc, vp);
+
+  mozilla::DebugOnly<JSExnType> exnType =
+      JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32());
+
+  MOZ_ASSERT(exnType == JSEXN_AGGREGATEERR);
+
+  // Steps 1-2. (9.1.13 OrdinaryCreateFromConstructor, steps 1-2).
+  RootedObject proto(cx);
+  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_AggregateError,
+                                          &proto)) {
+    return false;
+  }
+
+  // Step 3 (Inlined IterableToList).
+
+  if (!args.requireAtLeast(cx, "AggregateError", 1)) {
+    return false;
+  }
+
+  RootedArrayObject errorsList(cx, IterableToArray(cx, args.get(0)));
+  if (!errorsList) {
+    return false;
+  }
+
+  // 9.1.13 OrdinaryCreateFromConstructor, step 3.
+  // Step 5.
+  auto* obj = CreateErrorObject(cx, args, 1, JSEXN_AGGREGATEERR, proto);
+  if (!obj) {
+    return false;
+  }
+
+  // Step 4.
+  obj->as<AggregateErrorObject>().setAggregateErrors(errorsList);
+
+  // Step 6.
+  args.rval().setObject(*obj);
+  return true;
+}
+
 /* static */
 JSObject* ErrorObject::createProto(JSContext* cx, JSProtoKey key) {
   JSExnType type = ExnTypeFromProtoKey(key);
 
   if (type == JSEXN_ERR) {
     return GlobalObject::createBlankPrototype(
         cx, cx->global(), &ErrorObject::protoClasses[JSEXN_ERR]);
   }
@@ -290,19 +381,30 @@ JSObject* ErrorObject::createConstructor
         cx, key);
   } else {
     RootedFunction proto(
         cx, GlobalObject::getOrCreateErrorConstructor(cx, cx->global()));
     if (!proto) {
       return nullptr;
     }
 
-    ctor = NewFunctionWithProto(
-        cx, Error, 1, FunctionFlags::NATIVE_CTOR, nullptr, ClassName(key, cx),
-        proto, gc::AllocKind::FUNCTION_EXTENDED, SingletonObject);
+    Native native;
+    unsigned nargs;
+    if (type == JSEXN_AGGREGATEERR) {
+      native = AggregateError;
+      nargs = 2;
+    } else {
+      native = Error;
+      nargs = 1;
+    }
+
+    ctor =
+        NewFunctionWithProto(cx, native, nargs, FunctionFlags::NATIVE_CTOR,
+                             nullptr, ClassName(key, cx), proto,
+                             gc::AllocKind::FUNCTION_EXTENDED, SingletonObject);
   }
 
   if (!ctor) {
     return nullptr;
   }
 
   ctor->as<JSFunction>().setExtendedSlot(0, Int32Value(type));
   return ctor;
@@ -674,8 +776,76 @@ static bool exn_toSource(JSContext* cx, 
 
   JSString* str = sb.finishString();
   if (!str) {
     return false;
   }
   args.rval().setString(str);
   return true;
 }
+
+ArrayObject* js::AggregateErrorObject::aggregateErrors() const {
+  const Value& val = getReservedSlot(AGGREGATE_ERRORS_SLOT);
+  if (val.isUndefined()) {
+    return nullptr;
+  }
+  return &val.toObject().as<ArrayObject>();
+}
+
+void js::AggregateErrorObject::setAggregateErrors(ArrayObject* errors) {
+  MOZ_ASSERT(!aggregateErrors(),
+             "aggregated errors mustn't be modified once set");
+  setReservedSlot(AGGREGATE_ERRORS_SLOT, ObjectValue(*errors));
+}
+
+static inline bool IsAggregateError(HandleValue v) {
+  return v.isObject() && v.toObject().is<AggregateErrorObject>();
+}
+
+// get AggregateError.prototype.errors
+bool js::AggregateErrorObject::getErrors(JSContext* cx, unsigned argc,
+                                         Value* vp) {
+  CallArgs args = CallArgsFromVp(argc, vp);
+
+  // Steps 1-4.
+  return CallNonGenericMethod<IsAggregateError, getErrors_impl>(cx, args);
+}
+
+// get AggregateError.prototype.errors
+bool js::AggregateErrorObject::getErrors_impl(JSContext* cx,
+                                              const CallArgs& args) {
+  MOZ_ASSERT(IsAggregateError(args.thisv()));
+
+  auto* obj = &args.thisv().toObject().as<AggregateErrorObject>();
+
+  // Step 5.
+  // Create a copy of the [[AggregateErrors]] list.
+
+  RootedArrayObject errorsList(cx, obj->aggregateErrors());
+
+  // [[AggregateErrors]] may be absent when this error was created through
+  // JS_ReportError.
+  if (!errorsList) {
+    ArrayObject* result = NewDenseEmptyArray(cx);
+    if (!result) {
+      return false;
+    }
+
+    args.rval().setObject(*result);
+    return true;
+  }
+
+  uint32_t length = errorsList->length();
+
+  ArrayObject* result = NewDenseFullyAllocatedArray(cx, length);
+  if (!result) {
+    return false;
+  }
+
+  result->setLength(cx, length);
+
+  if (length > 0) {
+    result->initDenseElements(errorsList, 0, length);
+  }
+
+  args.rval().setObject(*result);
+  return true;
+}
--- a/js/src/vm/ErrorObject.h
+++ b/js/src/vm/ErrorObject.h
@@ -22,16 +22,17 @@
 #include "js/TypeDecls.h"
 #include "js/UniquePtr.h"
 #include "js/Value.h"
 #include "vm/JSObject.h"
 #include "vm/NativeObject.h"
 #include "vm/Shape.h"
 
 namespace js {
+class ArrayObject;
 
 class ErrorObject : public NativeObject {
   static JSObject* createProto(JSContext* cx, JSProtoKey key);
 
   static JSObject* createConstructor(JSContext* cx, JSProtoKey key);
 
   static bool init(JSContext* cx, Handle<ErrorObject*> obj, JSExnType type,
                    UniquePtr<JSErrorReport> errorReport, HandleString fileName,
@@ -113,16 +114,37 @@ class ErrorObject : public NativeObject 
 
   // Getter and setter for the Error.prototype.stack accessor.
   static bool getStack(JSContext* cx, unsigned argc, Value* vp);
   static bool getStack_impl(JSContext* cx, const CallArgs& args);
   static bool setStack(JSContext* cx, unsigned argc, Value* vp);
   static bool setStack_impl(JSContext* cx, const CallArgs& args);
 };
 
+class AggregateErrorObject : public ErrorObject {
+  friend class ErrorObject;
+
+  // [[AggregateErrors]] slot of AggregateErrorObjects.
+  static const uint32_t AGGREGATE_ERRORS_SLOT = ErrorObject::RESERVED_SLOTS;
+  static const uint32_t RESERVED_SLOTS = AGGREGATE_ERRORS_SLOT + 1;
+
+ public:
+  ArrayObject* aggregateErrors() const;
+  void setAggregateErrors(ArrayObject* errors);
+
+  // Getter for the AggregateError.prototype.errors accessor.
+  static bool getErrors(JSContext* cx, unsigned argc, Value* vp);
+  static bool getErrors_impl(JSContext* cx, const CallArgs& args);
+};
+
 }  // namespace js
 
 template <>
 inline bool JSObject::is<js::ErrorObject>() const {
   return js::ErrorObject::isErrorClass(getClass());
 }
 
+template <>
+inline bool JSObject::is<js::AggregateErrorObject>() const {
+  return hasClass(js::ErrorObject::classForType(JSEXN_AGGREGATEERR));
+}
+
 #endif  // vm_ErrorObject_h_
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -136,16 +136,21 @@ bool GlobalObject::skipDeselectedConstru
     // Return true if the given constructor has been disabled at run-time.
     case JSProto_Atomics:
     case JSProto_SharedArrayBuffer:
       return !cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled();
 
     case JSProto_FinalizationGroup:
       return !cx->realm()->creationOptions().getWeakRefsEnabled();
 
+#ifndef NIGHTLY_BUILD
+    case JSProto_AggregateError:
+      return true;
+#endif
+
     default:
       return false;
   }
 }
 
 /* static*/
 bool GlobalObject::resolveConstructor(JSContext* cx,
                                       Handle<GlobalObject*> global,
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -40,25 +40,25 @@ using js::UncheckedUnwrap;
 using js::Wrapper;
 
 namespace xpc {
 
 using namespace XrayUtils;
 
 #define Between(x, a, b) (a <= x && x <= b)
 
-static_assert(JSProto_URIError - JSProto_Error == 7,
+static_assert(JSProto_URIError - JSProto_Error == 8,
               "New prototype added in error object range");
 #define AssertErrorObjectKeyInBounds(key)                      \
   static_assert(Between(key, JSProto_Error, JSProto_URIError), \
                 "We depend on js/ProtoKey.h ordering here");
 MOZ_FOR_EACH(AssertErrorObjectKeyInBounds, (),
-             (JSProto_Error, JSProto_InternalError, JSProto_EvalError,
-              JSProto_RangeError, JSProto_ReferenceError, JSProto_SyntaxError,
-              JSProto_TypeError, JSProto_URIError));
+             (JSProto_Error, JSProto_InternalError, JSProto_AggregateError,
+              JSProto_EvalError, JSProto_RangeError, JSProto_ReferenceError,
+              JSProto_SyntaxError, JSProto_TypeError, JSProto_URIError));
 
 static_assert(JSProto_Uint8ClampedArray - JSProto_Int8Array == 8,
               "New prototype added in typed array range");
 #define AssertTypedArrayKeyInBounds(key)                                    \
   static_assert(Between(key, JSProto_Int8Array, JSProto_Uint8ClampedArray), \
                 "We depend on js/ProtoKey.h ordering here");
 MOZ_FOR_EACH(AssertTypedArrayKeyInBounds, (),
              (JSProto_Int8Array, JSProto_Uint8Array, JSProto_Int16Array,