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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
André Bargull 2019-11-22 18:12:56 +00:00
parent fd0ad767f8
commit 8e0ef36914
8 changed files with 355 additions and 73 deletions

View File

@ -46,6 +46,7 @@ enum JSExnType {
JSEXN_ERR,
JSEXN_FIRST = JSEXN_ERR,
JSEXN_INTERNALERR,
JSEXN_AGGREGATEERR,
JSEXN_EVALERR,
JSEXN_RANGEERR,
JSEXN_REFERENCEERR,

View File

@ -60,6 +60,7 @@
REAL(RegExp, OCLASP(RegExp)) \
REAL(Error, ERROR_CLASP(JSEXN_ERR)) \
REAL(InternalError, ERROR_CLASP(JSEXN_INTERNALERR)) \
REAL(AggregateError, ERROR_CLASP(JSEXN_AGGREGATEERR)) \
REAL(EvalError, ERROR_CLASP(JSEXN_EVALERR)) \
REAL(RangeError, ERROR_CLASP(JSEXN_RANGEERR)) \
REAL(ReferenceError, ERROR_CLASP(JSEXN_REFERENCEERR)) \

View File

@ -68,6 +68,7 @@ extern JSObject* CopyErrorObject(JSContext* cx,
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 &&

View File

@ -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);

View File

@ -9,6 +9,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/RecordReplay.h"
#include <utility>
@ -20,6 +21,7 @@
#include "jspubtd.h"
#include "NamespaceImports.h"
#include "builtin/Array.h"
#include "gc/AllocKind.h"
#include "gc/FreeOp.h"
#include "gc/Rooting.h"
@ -29,6 +31,7 @@
#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"
@ -49,6 +52,7 @@
#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"
@ -69,6 +73,7 @@ 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),
@ -87,42 +92,49 @@ 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};
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)};
#define IMPLEMENT_NATIVE_ERROR_PROPERTIES(name) \
static const JSPropertySpec name##_properties[] = { \
COMMON_ERROR_PROPERTIES(name), JS_PS_END};
#define IMPLEMENT_NATIVE_ERROR_SPEC(name) \
{ \
ErrorObject::createConstructor, ErrorObject::createProto, nullptr, \
nullptr, nullptr, \
other_error_properties[JSProto_##name - JSProto_Error - 1], nullptr, \
JSProto_Error \
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, 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] = {
@ -130,6 +142,7 @@ const ClassSpec ErrorObject::classSpecs[JSEXN_ERROR_LIMIT] = {
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),
@ -142,16 +155,19 @@ const ClassSpec ErrorObject::classSpecs[JSEXN_ERROR_LIMIT] = {
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 = {
@ -169,6 +185,7 @@ static const JSClassOps ErrorObjectClassOps = {
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),
@ -185,31 +202,15 @@ static void exn_finalize(JSFreeOp* fop, JSObject* obj) {
}
}
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;
}
}
@ -218,8 +219,8 @@ static bool Error(JSContext* cx, unsigned argc, Value* vp) {
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()) {
@ -232,13 +233,13 @@ static bool Error(JSContext* cx, unsigned argc, Value* vp) {
}
}
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);
@ -247,12 +248,36 @@ static bool Error(JSContext* cx, unsigned argc, Value* vp) {
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;
}
@ -261,6 +286,72 @@ static bool Error(JSContext* cx, unsigned argc, Value* vp) {
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);
@ -295,9 +386,20 @@ JSObject* ErrorObject::createConstructor(JSContext* cx, JSProtoKey key) {
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) {
@ -679,3 +781,71 @@ static bool exn_toSource(JSContext* cx, unsigned argc, Value* vp) {
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;
}

View File

@ -27,6 +27,7 @@
#include "vm/Shape.h"
namespace js {
class ArrayObject;
class ErrorObject : public NativeObject {
static JSObject* createProto(JSContext* cx, JSProtoKey key);
@ -118,6 +119,22 @@ class ErrorObject : public NativeObject {
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 <>
@ -125,4 +142,9 @@ 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_

View File

@ -129,6 +129,11 @@ bool GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key) {
case JSProto_FinalizationGroup:
return !cx->realm()->creationOptions().getWeakRefsEnabled();
#ifndef NIGHTLY_BUILD
case JSProto_AggregateError:
return true;
#endif
default:
return false;
}

View File

@ -45,15 +45,15 @@ 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");