mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
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:
parent
fd0ad767f8
commit
8e0ef36914
@ -46,6 +46,7 @@ enum JSExnType {
|
||||
JSEXN_ERR,
|
||||
JSEXN_FIRST = JSEXN_ERR,
|
||||
JSEXN_INTERNALERR,
|
||||
JSEXN_AGGREGATEERR,
|
||||
JSEXN_EVALERR,
|
||||
JSEXN_RANGEERR,
|
||||
JSEXN_REFERENCEERR,
|
||||
|
@ -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)) \
|
||||
|
@ -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 &&
|
||||
|
82
js/src/tests/non262/Error/AggregateError.js
Normal file
82
js/src/tests/non262/Error/AggregateError.js
Normal 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);
|
@ -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;
|
||||
}
|
||||
|
@ -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_
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user