зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1641355
- Change AggregateError.errors to a data property on instances. r=tcampbell,peterv.
The changes in xpconnect are necessary because this is not being specified in the usual way, with a getter. Ordinary data properties require an explicit loophole to make them visible through X-ray wrappers. Differential Revision: https://phabricator.services.mozilla.com/D77181
This commit is contained in:
Родитель
fbbed3d9e4
Коммит
87b8f09585
|
@ -3891,14 +3891,20 @@ static void ThrowAggregateError(JSContext* cx,
|
|||
return;
|
||||
}
|
||||
|
||||
// |error| isn't guaranteed to be an AggregateErrorObject in case of OOM.
|
||||
// |error| isn't guaranteed to be an ErrorObject in case of OOM.
|
||||
RootedSavedFrame stack(cx);
|
||||
if (error.isObject() && error.toObject().is<AggregateErrorObject>()) {
|
||||
auto* aggregateError = &error.toObject().as<AggregateErrorObject>();
|
||||
aggregateError->setAggregateErrors(errors.unwrappedArray());
|
||||
if (error.isObject() && error.toObject().is<ErrorObject>()) {
|
||||
Rooted<ErrorObject*> errorObj(cx, &error.toObject().as<ErrorObject>());
|
||||
MOZ_ASSERT(errorObj->type() == JSEXN_AGGREGATEERR);
|
||||
|
||||
RootedValue errorsVal(cx, JS::ObjectValue(*errors.unwrappedArray()));
|
||||
if (!NativeDefineDataProperty(cx, errorObj, cx->names().errors, errorsVal,
|
||||
0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adopt the existing saved frames when present.
|
||||
if (JSObject* errorStack = aggregateError->stack()) {
|
||||
if (JSObject* errorStack = errorObj->stack()) {
|
||||
stack = &errorStack->as<SavedFrame>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -752,6 +752,17 @@ skip script test262/built-ins/Atomics/add/bigint/nonshared-int-views.js
|
|||
skip script test262/built-ins/Atomics/xor/nonshared-int-views.js
|
||||
skip script test262/built-ins/Atomics/exchange/nonshared-int-views.js
|
||||
|
||||
# Not yet updated to match <https://github.com/tc39/proposal-promise-any/pull/64>.
|
||||
skip script test262/built-ins/NativeErrors/AggregateError/prototype/errors/invoked-as-accessor.js
|
||||
skip script test262/built-ins/NativeErrors/AggregateError/prototype/errors/invoked-as-func.js
|
||||
skip script test262/built-ins/NativeErrors/AggregateError/prototype/errors/length.js
|
||||
skip script test262/built-ins/NativeErrors/AggregateError/prototype/errors/name.js
|
||||
skip script test262/built-ins/NativeErrors/AggregateError/prototype/errors/prop-desc.js
|
||||
skip script test262/built-ins/NativeErrors/AggregateError/prototype/errors/return-from-iterable-errors.js
|
||||
skip script test262/built-ins/NativeErrors/AggregateError/prototype/errors/return-new-array-from-list.js
|
||||
skip script test262/built-ins/NativeErrors/AggregateError/prototype/errors/this-has-no-typedarrayname-internal.js
|
||||
skip script test262/built-ins/NativeErrors/AggregateError/prototype/errors/this-is-not-object.js
|
||||
|
||||
|
||||
##############################################
|
||||
# Enable Iterator Helpers tests in the shell #
|
||||
|
|
|
@ -9,14 +9,11 @@ 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.
|
||||
// The .errors data property is an array object.
|
||||
{
|
||||
let err = new AggregateError([]);
|
||||
|
||||
|
@ -24,13 +21,15 @@ assertThrowsInstanceOf(() => AggregateError(), TypeError);
|
|||
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);
|
||||
assertEq(err.errors[0], 123);
|
||||
|
||||
// The property is writable.
|
||||
err.errors = undefined;
|
||||
assertEq(err.errors, undefined);
|
||||
}
|
||||
|
||||
// The errors argument can be any iterable.
|
||||
|
@ -53,29 +52,30 @@ assertThrowsInstanceOf(() => AggregateError(), TypeError);
|
|||
}
|
||||
|
||||
{
|
||||
const {
|
||||
get: getErrors,
|
||||
set: setErrors,
|
||||
} = Object.getOwnPropertyDescriptor(AggregateError.prototype, "errors");
|
||||
assertEq(typeof getErrors, "function");
|
||||
assertEq(typeof setErrors, "undefined");
|
||||
assertEq("errors" in AggregateError.prototype, false);
|
||||
|
||||
// The |this| argument must be an AggregateError instance.
|
||||
assertThrowsInstanceOf(() => getErrors.call(null), TypeError);
|
||||
assertThrowsInstanceOf(() => getErrors.call({}), TypeError);
|
||||
assertThrowsInstanceOf(() => getErrors.call(new Error), TypeError);
|
||||
const {
|
||||
configurable,
|
||||
enumerable,
|
||||
value,
|
||||
writable
|
||||
} = Object.getOwnPropertyDescriptor(new AggregateError([]), "errors");
|
||||
assertEq(configurable, true);
|
||||
assertEq(enumerable, false);
|
||||
assertEq(writable, true);
|
||||
assertEq(value.length, 0);
|
||||
|
||||
const g = newGlobal();
|
||||
|
||||
let obj = {};
|
||||
let errors = getErrors.call(new g.AggregateError([obj]));
|
||||
let errors = new g.AggregateError([obj]).errors;
|
||||
|
||||
assertEq(errors.length, 1);
|
||||
assertEq(errors[0], obj);
|
||||
|
||||
// The prototype is (incorrectly) |g.Array.prototype| in the cross-compartment case.
|
||||
// The prototype is |g.Array.prototype| in the cross-compartment case.
|
||||
let proto = Object.getPrototypeOf(errors);
|
||||
assertEq(proto === Array.prototype || proto === g.Array.prototype, true);
|
||||
assertEq(proto === g.Array.prototype, true);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
|
|
|
@ -139,6 +139,7 @@
|
|||
MACRO(era, era, "era") \
|
||||
MACRO(ErrorToStringWithTrailingNewline, ErrorToStringWithTrailingNewline, \
|
||||
"ErrorToStringWithTrailingNewline") \
|
||||
MACRO(errors, errors, "errors") \
|
||||
MACRO(escape, escape, "escape") \
|
||||
MACRO(eval, eval, "eval") \
|
||||
MACRO(exec, exec, "exec") \
|
||||
|
|
|
@ -103,16 +103,12 @@ static const JSPropertySpec error_properties[] = {
|
|||
JS_PSGS("stack", ErrorObject::getStack, ErrorObject::setStack, 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};
|
||||
|
||||
IMPLEMENT_NATIVE_ERROR_PROPERTIES(InternalError)
|
||||
IMPLEMENT_NATIVE_ERROR_PROPERTIES(AggregateError)
|
||||
IMPLEMENT_NATIVE_ERROR_PROPERTIES(EvalError)
|
||||
IMPLEMENT_NATIVE_ERROR_PROPERTIES(RangeError)
|
||||
IMPLEMENT_NATIVE_ERROR_PROPERTIES(ReferenceError)
|
||||
|
@ -155,19 +151,16 @@ const ClassSpec ErrorObject::classSpecs[JSEXN_ERROR_LIMIT] = {
|
|||
IMPLEMENT_NONGLOBAL_ERROR_SPEC(LinkError),
|
||||
IMPLEMENT_NONGLOBAL_ERROR_SPEC(RuntimeError)};
|
||||
|
||||
#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) \
|
||||
{ \
|
||||
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(name) \
|
||||
IMPLEMENT_ERROR_CLASS_FROM(ErrorObject, name)
|
||||
|
||||
static void exn_finalize(JSFreeOp* fop, JSObject* obj);
|
||||
|
||||
static const JSClassOps ErrorObjectClassOps = {
|
||||
|
@ -186,10 +179,10 @@ 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),
|
||||
IMPLEMENT_ERROR_CLASS(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),
|
||||
|
@ -339,14 +332,18 @@ static bool AggregateError(JSContext* cx, unsigned argc, Value* vp) {
|
|||
}
|
||||
|
||||
// 9.1.13 OrdinaryCreateFromConstructor, step 3.
|
||||
// Step 5.
|
||||
auto* obj = CreateErrorObject(cx, args, 1, JSEXN_AGGREGATEERR, proto);
|
||||
// Step 4.
|
||||
Rooted<ErrorObject*> obj(
|
||||
cx, CreateErrorObject(cx, args, 1, JSEXN_AGGREGATEERR, proto));
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 4.
|
||||
obj->as<AggregateErrorObject>().setAggregateErrors(errorsList);
|
||||
// Step 5.
|
||||
RootedValue errorsVal(cx, JS::ObjectValue(*errorsList));
|
||||
if (!NativeDefineDataProperty(cx, obj, cx->names().errors, errorsVal, 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 6.
|
||||
args.rval().setObject(*obj);
|
||||
|
@ -774,71 +771,3 @@ 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;
|
||||
}
|
||||
|
|
|
@ -118,22 +118,6 @@ 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);
|
||||
};
|
||||
|
||||
JSString* ErrorToSource(JSContext* cx, HandleObject obj);
|
||||
|
||||
} // namespace js
|
||||
|
@ -143,9 +127,4 @@ 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_
|
||||
|
|
|
@ -117,6 +117,7 @@ const char* const XPCJSRuntime::mStrings[] = {
|
|||
"columnNumber", // IDX_COLUMNNUMBER
|
||||
"stack", // IDX_STACK
|
||||
"message", // IDX_MESSAGE
|
||||
"errors", // IDX_ERRORS
|
||||
"lastIndex", // IDX_LASTINDEX
|
||||
"then", // IDX_THEN
|
||||
"isInstance", // IDX_ISINSTANCE
|
||||
|
|
|
@ -394,6 +394,7 @@ class XPCJSContext final : public mozilla::CycleCollectedJSContext,
|
|||
IDX_COLUMNNUMBER,
|
||||
IDX_STACK,
|
||||
IDX_MESSAGE,
|
||||
IDX_ERRORS,
|
||||
IDX_LASTINDEX,
|
||||
IDX_THEN,
|
||||
IDX_ISINSTANCE,
|
||||
|
|
|
@ -252,9 +252,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
|
|||
gPrototypeProperties[c] = ["constructor", "name", "message", "stack"];
|
||||
gConstructorProperties[c] = constructorProps([]);
|
||||
}
|
||||
if (gPrototypeProperties['AggregateError']) {
|
||||
gPrototypeProperties['AggregateError'].push("errors");
|
||||
}
|
||||
// toString and toSource only live on the parent proto (Error.prototype).
|
||||
gPrototypeProperties['Error'].push('toString');
|
||||
gPrototypeProperties['Error'].push('toSource');
|
||||
|
@ -883,9 +880,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
|
|||
is(errors[0], 'error 1', "errors[0] has the correct value");
|
||||
is(errors[1], 'error 2', "errors[1] has the correct value");
|
||||
|
||||
Object.defineProperty(e.wrappedJSObject, 'errors', {value: 42});
|
||||
is(e.wrappedJSObject.errors, 42, "Set tricky expando");
|
||||
is(e.errors.length, 2, "errors accessor works over Xrays");
|
||||
e.wrappedJSObject.errors = 42;
|
||||
is(e.wrappedJSObject.errors, 42, "errors is a plain data property");
|
||||
is(e.errors, 42, "visible over Xrays");
|
||||
}
|
||||
|
||||
// Note - an Exception newed via Xrays is going to have an empty stack given the
|
||||
|
|
|
@ -665,6 +665,11 @@ bool JSXrayTraits::resolveOwnProperty(JSContext* cx, HandleObject wrapper,
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == JSProto_AggregateError &&
|
||||
id == GetJSIDByIndex(cx, XPCJSContext::IDX_ERRORS)) {
|
||||
return getOwnPropertyFromWrapperIfSafe(cx, wrapper, id, desc);
|
||||
}
|
||||
} else if (key == JSProto_RegExp) {
|
||||
if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_LASTINDEX)) {
|
||||
return getOwnPropertyFromWrapperIfSafe(cx, wrapper, id, desc);
|
||||
|
|
Загрузка…
Ссылка в новой задаче