Merge remote-tracking branch 'origin/tracking' into custom

This commit is contained in:
roytam1 2023-02-02 09:32:40 +08:00
commit ea9d00f005
22 changed files with 2119 additions and 767 deletions

File diff suppressed because it is too large Load diff

View file

@ -181,6 +181,142 @@ AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal, CompletionKind com
bool
AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind completionKind);
class MOZ_NON_TEMPORARY_CLASS PromiseLookup final
{
/*
* A PromiseLookup holds the following:
*
* Promise's shape (promiseConstructorShape_)
* To ensure that Promise has not been modified.
*
* Promise.prototype's shape (promiseProtoShape_)
* To ensure that Promise.prototype has not been modified.
*
* Promise's shape for the @@species getter. (promiseSpeciesShape_)
* To quickly retrieve the @@species getter for Promise.
*
* Promise's slot number for resolve (promiseResolveSlot_)
* To quickly retrieve the Promise.resolve function.
*
* Promise.prototype's slot number for constructor (promiseProtoConstructorSlot_)
* To quickly retrieve the Promise.prototype.constructor property.
*
* Promise.prototype's slot number for then (promiseProtoThenSlot_)
* To quickly retrieve the Promise.prototype.then function.
*
* MOZ_INIT_OUTSIDE_CTOR fields below are set in |initialize()|. The
* constructor only initializes a |state_| field, that defines whether the
* other fields are accessible.
*/
// Shape of matching Promise object.
MOZ_INIT_OUTSIDE_CTOR Shape* promiseConstructorShape_;
#ifdef DEBUG
// Accessor Shape containing the @@species property.
// See isPromiseStateStillSane() for why this field is debug-only.
MOZ_INIT_OUTSIDE_CTOR Shape* promiseSpeciesShape_;
#endif
// Shape of matching Promise.prototype object.
MOZ_INIT_OUTSIDE_CTOR Shape* promiseProtoShape_;
// Slots Promise.resolve, Promise.prototype.constructor, and
// Promise.prototype.then.
MOZ_INIT_OUTSIDE_CTOR uint32_t promiseResolveSlot_;
MOZ_INIT_OUTSIDE_CTOR uint32_t promiseProtoConstructorSlot_;
MOZ_INIT_OUTSIDE_CTOR uint32_t promiseProtoThenSlot_;
enum class State : uint8_t {
// Flags marking the lazy initialization of the above fields.
Uninitialized,
Initialized,
// The disabled flag is set when we don't want to try optimizing
// anymore because core objects were changed.
Disabled
};
State state_ = State::Uninitialized;
// Initialize the internal fields.
//
// The cache is successfully initialized iff
// 1. Promise and Promise.prototype classes are initialized.
// 2. Promise.prototype.constructor is equal to Promise.
// 3. Promise.prototype.then is the original `then` function.
// 4. Promise[@@species] is the original @@species getter.
// 5. Promise.resolve is the original `resolve` function.
void initialize(JSContext* cx);
// Reset the cache.
void reset();
// Check if the global promise-related objects have not been messed with
// in a way that would disable this cache.
bool isPromiseStateStillSane(JSContext* cx);
// Flags to control whether or not ensureInitialized() is allowed to
// reinitialize the cache when the Promise state is no longer sane.
enum class Reinitialize : bool {
Allowed,
Disallowed
};
// Return true if the lookup cache is properly initialized for usage.
bool ensureInitialized(JSContext* cx, Reinitialize reinitialize);
// Return true if the prototype of the given Promise object is
// Promise.prototype and the object doesn't shadow properties from
// Promise.prototype.
bool hasDefaultProtoAndNoShadowedProperties(JSContext* cx, PromiseObject* promise);
// Return true if the given Promise object uses the default @@species,
// "constructor", and "then" properties.
bool isDefaultInstance(JSContext* cx, PromiseObject* promise, Reinitialize reinitialize);
// Return the built-in Promise constructor or null if not yet initialized.
static JSFunction* getPromiseConstructor(JSContext* cx);
// Return the built-in Promise prototype or null if not yet initialized.
static NativeObject* getPromisePrototype(JSContext* cx);
// Return true if the slot contains the given native.
static bool isDataPropertyNative(JSContext* cx, NativeObject* obj, uint32_t slot,
JSNative native);
// Return true if the accessor shape contains the given native.
static bool isAccessorPropertyNative(JSContext* cx, Shape* shape, JSNative native);
public:
/** Construct a |PromiseSpeciesLookup| in the uninitialized state. */
PromiseLookup() {
reset();
}
// Return true if the Promise constructor and Promise.prototype still use
// the default built-in functions.
bool isDefaultPromiseState(JSContext* cx);
// Return true if the given Promise object uses the default @@species,
// "constructor", and "then" properties.
bool isDefaultInstance(JSContext* cx, PromiseObject* promise) {
return isDefaultInstance(cx, promise, Reinitialize::Allowed);
}
// Return true if the given Promise object uses the default @@species,
// "constructor", and "then" properties.
bool isDefaultInstanceWhenPromiseStateIsSane(JSContext* cx, PromiseObject* promise) {
return isDefaultInstance(cx, promise, Reinitialize::Disallowed);
}
// Purge the cache and all info associated with it.
void purge() {
if (state_ == State::Initialized)
reset();
}
};
/**
* A PromiseTask represents a task that can be dispatched to a helper thread
* (via StartPromiseTask), executed (by implementing PromiseTask::execute()),

View file

@ -224,6 +224,15 @@ function GetTypeError(msg) {
assert(false, "the catch block should've returned from this function.");
}
function GetAggregateError(msg) {
try {
FUN_APPLY(ThrowAggregateError, undefined, arguments);
} catch (e) {
return e;
}
assert(false, "the catch block should've returned from this function.");
}
function GetInternalError(msg) {
try {
FUN_APPLY(ThrowInternalError, undefined, arguments);

View file

@ -0,0 +1,5 @@
// |jit-test| skip-if: !('oomTest' in this)
oomTest(() => {
new AggregateError([]);
});

View file

@ -0,0 +1,58 @@
// |jit-test| skip-if: !Promise.any
function newPromiseCapability() {
var resolve, reject, promise = new Promise(function(r1, r2) {
resolve = r1;
reject = r2;
});
return {promise, resolve, reject};
}
function neverCalled() {
// Quit with non-zero exit code to ensure a test suite error is shown,
// even when this function is called within promise handlers which normally
// swallow any exceptions.
quit(1);
}
var {promise, resolve} = newPromiseCapability();
var getterCount = 0;
class P extends Promise {
constructor(executor) {
var {promise, resolve, reject} = newPromiseCapability();
executor(function(v) {
// Resolve the promise.
resolve(v);
// But then return an object from the resolve function. This object
// must be treated as the resolution value for the otherwise
// skipped promise which gets created when Promise.prototype.then is
// called in PerformPromiseRace.
return {
get then() {
getterCount++;
}
};
}, neverCalled);
return promise;
}
// Default to the standard Promise.resolve function, so we don't create
// another instance of this class when resolving the passed promise objects
// in Promise.race.
static resolve(v) {
return Promise.resolve(v);
}
}
P.any([promise]);
resolve(0);
drainJobQueue();
assertEq(getterCount, 1);

View file

@ -597,6 +597,7 @@ MSG_DEF(JSMSG_PROMISE_CAPABILITY_HAS_SOMETHING_ALREADY, 0, JSEXN_TYPEERR, "GetCa
MSG_DEF(JSMSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the resolve function.")
MSG_DEF(JSMSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the reject function.")
MSG_DEF(JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON,0, JSEXN_INTERNALERR, "Promise rejection value is a non-unwrappable cross-compartment wrapper.")
MSG_DEF(JSMSG_PROMISE_ANY_REJECTION, 0, JSEXN_AGGREGATEERR, "No Promise in Promise.any was resolved")
// Iterator
MSG_DEF(JSMSG_RETURN_NOT_CALLABLE, 0, JSEXN_TYPEERR, "property 'return' of iterator is not callable")

View file

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

View file

@ -947,6 +947,7 @@ void
JSCompartment::purge()
{
dtoaCache.purge();
promiseLookup.purge();
lastCachedNativeIterator = nullptr;
}

View file

@ -881,6 +881,10 @@ struct JSCompartment
// Aggregated output used to collect JSScript hit counts when code coverage
// is enabled.
js::coverage::LCovCompartment lcovOutput;
public:
// Property lookup table for promises
js::PromiseLookup promiseLookup;
};
inline bool

View file

@ -45,162 +45,6 @@ using namespace js::gc;
using mozilla::ArrayLength;
using mozilla::PodArrayZero;
static void
exn_finalize(FreeOp* fop, JSObject* obj);
static bool
exn_toSource(JSContext* cx, unsigned argc, Value* vp);
#define IMPLEMENT_ERROR_PROTO_CLASS(name) \
{ \
js_Object_str, \
JSCLASS_HAS_CACHED_PROTO(JSProto_##name), \
JS_NULL_CLASS_OPS, \
&ErrorObject::classSpecs[JSProto_##name - JSProto_Error] \
}
const Class
ErrorObject::protoClasses[JSEXN_ERROR_LIMIT] = {
IMPLEMENT_ERROR_PROTO_CLASS(Error),
IMPLEMENT_ERROR_PROTO_CLASS(InternalError),
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),
IMPLEMENT_ERROR_PROTO_CLASS(CompileError),
IMPLEMENT_ERROR_PROTO_CLASS(RuntimeError)
};
static const JSFunctionSpec error_methods[] = {
#if JS_HAS_TOSOURCE
JS_FN(js_toSource_str, exn_toSource, 0, 0),
#endif
JS_SELF_HOSTED_FN(js_toString_str, "ErrorToString", 0,0),
JS_FS_END
};
static const JSPropertySpec error_properties[] = {
JS_STRING_PS("message", "", 0),
JS_STRING_PS("name", "Error", 0),
// 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 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(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_NONGLOBAL_ERROR_SPEC(name) \
{ \
ErrorObject::createConstructor, \
ErrorObject::createProto, \
nullptr, \
nullptr, \
nullptr, \
other_error_properties[JSProto_##name - JSProto_Error - 1], \
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(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(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 ] \
}
static const ClassOps ErrorObjectClassOps = {
nullptr, /* addProperty */
nullptr, /* delProperty */
nullptr, /* getProperty */
nullptr, /* setProperty */
nullptr, /* enumerate */
nullptr, /* resolve */
nullptr, /* mayResolve */
exn_finalize,
nullptr, /* call */
nullptr, /* hasInstance */
nullptr, /* construct */
nullptr, /* trace */
};
const Class
ErrorObject::classes[JSEXN_ERROR_LIMIT] = {
IMPLEMENT_ERROR_CLASS(Error),
IMPLEMENT_ERROR_CLASS(InternalError),
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(RuntimeError)
};
size_t
ExtraMallocSize(JSErrorReport* report)
{
@ -361,8 +205,9 @@ struct SuppressErrorsGuard
}
};
static bool
CaptureStack(JSContext* cx, MutableHandleObject stack)
bool
js::CaptureStack(JSContext* cx, MutableHandleObject stack)
{
// Cut off the stack if it gets too deep (most commonly for infinite recursion
// errors).
@ -388,14 +233,6 @@ js::ComputeStackString(JSContext* cx)
return str.get();
}
static void
exn_finalize(FreeOp* fop, JSObject* obj)
{
MOZ_ASSERT(fop->maybeOffMainThread());
if (JSErrorReport* report = obj->as<ErrorObject>().getErrorReport())
fop->delete_(report);
}
JSErrorReport*
js::ErrorFromException(JSContext* cx, HandleObject objArg)
{
@ -429,199 +266,6 @@ ExceptionStackOrNull(HandleObject objArg)
return obj->as<ErrorObject>().stack();
}
bool
Error(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// ES6 19.5.1.1 mandates the .prototype lookup happens before the toString
RootedObject proto(cx);
if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
return false;
/* Compute the error message, if any. */
RootedString message(cx, nullptr);
if (args.hasDefined(0)) {
message = ToString<CanGC>(cx, args[0]);
if (!message)
return false;
}
/* Find the scripted caller, but only ones we're allowed to know about. */
NonBuiltinFrameIter iter(cx, cx->compartment()->principals());
/* Set the 'fileName' property. */
RootedString fileName(cx);
if (args.length() > 1) {
fileName = ToString<CanGC>(cx, args[1]);
} else {
fileName = cx->runtime()->emptyString;
if (!iter.done()) {
if (const char* cfilename = iter.filename())
fileName = JS_NewStringCopyZ(cx, cfilename);
}
}
if (!fileName)
return false;
/* Set the 'lineNumber' property. */
uint32_t lineNumber, columnNumber = 0;
if (args.length() > 2) {
if (!ToUint32(cx, args[2], &lineNumber))
return false;
} else {
lineNumber = iter.done() ? 0 : iter.computeLine(&columnNumber);
// XXX: Make the column 1-based as in other browsers, instead of 0-based
// which is how SpiderMonkey stores it internally. This will be
// unnecessary once bug 1144340 is fixed.
++columnNumber;
}
RootedObject stack(cx);
if (!CaptureStack(cx, &stack))
return false;
/*
* 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());
RootedObject obj(cx, ErrorObject::create(cx, exnType, stack, fileName,
lineNumber, columnNumber, nullptr, message, proto));
if (!obj)
return false;
args.rval().setObject(*obj);
return true;
}
#if JS_HAS_TOSOURCE
/*
* Return a string that may eval to something similar to the original object.
*/
static bool
exn_toSource(JSContext* cx, unsigned argc, Value* vp)
{
JS_CHECK_RECURSION(cx, return false);
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
RootedValue nameVal(cx);
RootedString name(cx);
if (!GetProperty(cx, obj, obj, cx->names().name, &nameVal) ||
!(name = ToString<CanGC>(cx, nameVal)))
{
return false;
}
RootedValue messageVal(cx);
RootedString message(cx);
if (!GetProperty(cx, obj, obj, cx->names().message, &messageVal) ||
!(message = ValueToSource(cx, messageVal)))
{
return false;
}
RootedValue filenameVal(cx);
RootedString filename(cx);
if (!GetProperty(cx, obj, obj, cx->names().fileName, &filenameVal) ||
!(filename = ValueToSource(cx, filenameVal)))
{
return false;
}
RootedValue linenoVal(cx);
uint32_t lineno;
if (!GetProperty(cx, obj, obj, cx->names().lineNumber, &linenoVal) ||
!ToUint32(cx, linenoVal, &lineno))
{
return false;
}
StringBuffer sb(cx);
if (!sb.append("(new ") || !sb.append(name) || !sb.append("("))
return false;
if (!sb.append(message))
return false;
if (!filename->empty()) {
if (!sb.append(", ") || !sb.append(filename))
return false;
}
if (lineno != 0) {
/* We have a line, but no filename, add empty string */
if (filename->empty() && !sb.append(", \"\""))
return false;
JSString* linenumber = ToString<CanGC>(cx, linenoVal);
if (!linenumber)
return false;
if (!sb.append(", ") || !sb.append(linenumber))
return false;
}
if (!sb.append("))"))
return false;
JSString* str = sb.finishString();
if (!str)
return false;
args.rval().setString(str);
return true;
}
#endif
/* 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]);
}
RootedObject protoProto(cx, GlobalObject::getOrCreateErrorPrototype(cx, cx->global()));
if (!protoProto)
return nullptr;
return GlobalObject::createBlankPrototypeInheriting(cx, cx->global(),
&ErrorObject::protoClasses[type],
protoProto);
}
/* static */ JSObject*
ErrorObject::createConstructor(JSContext* cx, JSProtoKey key)
{
JSExnType type = ExnTypeFromProtoKey(key);
RootedObject ctor(cx);
if (type == JSEXN_ERR) {
ctor = GenericCreateConstructor<Error, 1, gc::AllocKind::FUNCTION_EXTENDED>(cx, key);
} else {
RootedFunction proto(cx, GlobalObject::getOrCreateErrorConstructor(cx, cx->global()));
if (!proto)
return nullptr;
ctor = NewFunctionWithProto(cx, Error, 1, JSFunction::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;
}
JS_FRIEND_API(JSFlatString*)
js::GetErrorTypeName(JSContext* cx, int16_t exnType)
{
@ -1112,3 +756,11 @@ js::GetTypeError(JSContext* cx, unsigned errorNumber, MutableHandleValue error)
args[0].set(Int32Value(errorNumber));
return CallSelfHostedFunction(cx, "GetTypeError", NullHandleValue, args, error);
}
bool
js::GetAggregateError(JSContext* cx, unsigned errorNumber, MutableHandleValue error)
{
FixedInvokeArgs<1> args(cx);
args[0].set(Int32Value(errorNumber));
return CallSelfHostedFunction(cx, "GetAggregateError", NullHandleValue, args, error);
}

View file

@ -23,6 +23,9 @@ CopyErrorNote(JSContext* cx, JSErrorNotes::Note* note);
JSErrorReport*
CopyErrorReport(JSContext* cx, JSErrorReport* report);
bool
CaptureStack(JSContext* cx, MutableHandleObject stack);
JSString*
ComputeStackString(JSContext* cx);
@ -59,6 +62,7 @@ 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 &&
@ -134,6 +138,8 @@ bool
GetInternalError(JSContext* cx, unsigned errorNumber, MutableHandleValue error);
bool
GetTypeError(JSContext* cx, unsigned errorNumber, MutableHandleValue error);
bool
GetAggregateError(JSContext* cx, unsigned errorNumber, MutableHandleValue error);
} // namespace js

View file

@ -69,6 +69,7 @@
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)) \

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, "");
// The |errors| argument is mandatory.
assertThrowsInstanceOf(() => new AggregateError(), TypeError);
assertThrowsInstanceOf(() => AggregateError(), TypeError);
// The .errors data property is an array object.
{
let err = new AggregateError([]);
let {errors} = err;
assertEq(Array.isArray(errors), true);
assertEq(errors.length, 0);
// 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.
{
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");
}
{
assertEq("errors" in AggregateError.prototype, false);
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 = new g.AggregateError([obj]).errors;
assertEq(errors.length, 1);
assertEq(errors[0], obj);
// The prototype is |g.Array.prototype| in the cross-compartment case.
let proto = Object.getPrototypeOf(errors);
assertEq(proto === g.Array.prototype, true);
}
if (typeof reportCompare === "function")
reportCompare(0, 0);

View file

@ -0,0 +1,69 @@
// |reftest| skip-if(!Promise.any)
function toMessage(stack) {
// Provide the stack string in the error message for debugging.
return `[stack: ${stack.replace(/\n/g, "\\n")}]`;
}
// Test when AggregateError isn't created from a Promise Job.
{
let p = Promise.any([]); // line 10
p.then(v => {
reportCompare(0, 1, "expected error");
}, e => {
assertEq(e.name, "AggregateError");
var {stack} = e;
assertEq(/^@.+any-stack.js:10/m.test(stack), true, toMessage(stack));
});
}
// Same as above, but now with surrounding function context.
function testNoJobQueue() {
let p = Promise.any([]); // line 24
p.then(v => {
reportCompare(0, 1, "expected error");
}, e => {
assertEq(e.name, "AggregateError");
var {stack} = e;
assertEq(/^testNoJobQueue@.+any-stack.js:24/m.test(stack), true, toMessage(stack));
});
}
testNoJobQueue();
// Test when AggregateError is created from a Promise Job.
{
let rejected = Promise.reject(0);
let p = Promise.any([rejected]); // line 40
p.then(v => {
reportCompare(0, 1, "expected error");
}, e => {
assertEq(e.name, "AggregateError");
var {stack} = e;
assertEq(/^Promise.any\*@.+any-stack.js:40/m.test(stack), true, toMessage(stack));
});
}
// Same as above, but now with surrounding function context.
function testFromJobQueue() {
let rejected = Promise.reject(0);
let p = Promise.any([rejected]); // line 55
p.then(v => {
reportCompare(0, 1, "expected error");
}, e => {
assertEq(e.name, "AggregateError");
var {stack} = e;
assertEq(/^Promise.any\*testFromJobQueue@.+any-stack.js:55/m.test(stack), true, toMessage(stack));
});
}
testFromJobQueue();
if (typeof reportCompare === "function")
reportCompare(0, 0);

View file

@ -0,0 +1,78 @@
// |reftest| skip-if(!Promise.any)
// Smoke test for `Promise.any`, test262 should cover the function in
// more detail.
function expectedError() {
reportCompare(true, false, "expected error");
}
// Empty elements.
Promise.any([]).then(expectedError, e => {
assertEq(e instanceof AggregateError, true);
assertEq(e.errors.length, 0);
});
// Single element.
Promise.any([Promise.resolve(0)]).then(v => {
assertEq(v, 0);
});
Promise.any([Promise.reject(1)]).then(expectedError, e => {
assertEq(e instanceof AggregateError, true);
assertEq(e.errors.length, 1);
assertEq(e.errors[0], 1);
});
// Multiple elements.
Promise.any([Promise.resolve(1), Promise.resolve(2)]).then(v => {
assertEq(v, 1);
});
Promise.any([Promise.resolve(3), Promise.reject(4)]).then(v => {
assertEq(v, 3);
});
Promise.any([Promise.reject(5), Promise.resolve(6)]).then(v => {
assertEq(v, 6);
});
Promise.any([Promise.reject(7), Promise.reject(8)]).then(expectedError, e => {
assertEq(e instanceof AggregateError, true);
assertEq(e.errors.length, 2);
assertEq(e.errors[0], 7);
assertEq(e.errors[1], 8);
});
// Cross-Realm tests.
//
// Note: When |g| is a cross-compartment global, Promise.any creates the errors
// array and the AggregateError in |g|'s Realm. This doesn't follow the spec, but
// the code in js/src/builtin/Promise.cpp claims this is useful when the Promise
// compartment is less-privileged. This means for this test we can't use
// assertDeepEq below, because the result array/error may have the wrong prototype.
let g = newGlobal();
if (typeof isSameCompartment !== "function") {
var isSameCompartment = SpecialPowers.Cu.getJSTestingFunctions().isSameCompartment;
}
// Test wrapping when no `Promise.any Reject Element Function` is called.
Promise.any.call(g.Promise, []).then(expectedError, e => {
assertEq(e.name, "AggregateError");
assertEq(isSameCompartment(e, g), true);
assertEq(isSameCompartment(e.errors, g), true);
assertEq(e.errors.length, 0);
});
// Test wrapping in `Promise.any Reject Element Function`.
Promise.any.call(g.Promise, [Promise.reject("err")]).then(expectedError, e => {
assertEq(e.name, "AggregateError");
assertEq(isSameCompartment(e, g), true);
assertEq(isSameCompartment(e.errors, g), true);
assertEq(e.errors.length, 1);
assertEq(e.errors[0], "err");
});
if (typeof reportCompare === "function")
reportCompare(0, 0);

View file

@ -116,6 +116,7 @@
macro(enumerate, enumerate, "enumerate") \
macro(era, era, "era") \
macro(ErrorToStringWithTrailingNewline, ErrorToStringWithTrailingNewline, "ErrorToStringWithTrailingNewline") \
macro(errors, errors, "errors") \
macro(escape, escape, "escape") \
macro(eval, eval, "eval") \
macro(exec, exec, "exec") \

View file

@ -6,23 +6,406 @@
#include "vm/ErrorObject-inl.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Range.h"
#include "jsapi.h"
#include "jsarray.h"
#include "jsexn.h"
#include "js/CallArgs.h"
#include "js/CharacterEncoding.h"
#include "vm/StringBuffer.h"
#include "vm/GlobalObject.h"
#include "vm/String.h"
#include "jsobjinlines.h"
#include "vm/ArrayObject-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/SavedStacks-inl.h"
#include "vm/Shape-inl.h"
using namespace js;
#define IMPLEMENT_ERROR_PROTO_CLASS(name) \
{ \
js_Object_str, \
JSCLASS_HAS_CACHED_PROTO(JSProto_##name), \
JS_NULL_CLASS_OPS, \
&ErrorObject::classSpecs[JSProto_##name - JSProto_Error] \
}
const Class
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),
IMPLEMENT_ERROR_PROTO_CLASS(CompileError),
IMPLEMENT_ERROR_PROTO_CLASS(RuntimeError)
};
static bool
exn_toSource(JSContext* cx, unsigned argc, Value* vp);
static const JSFunctionSpec error_methods[] = {
#if JS_HAS_TOSOURCE
JS_FN(js_toSource_str, exn_toSource, 0, 0),
#endif
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[] = {
COMMON_ERROR_PROPERTIES(Error),
// Only Error.prototype has .stack!
JS_PSGS("stack", ErrorObject::getStack, ErrorObject::setStack, 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)
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, \
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(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 ] \
}
static void
exn_finalize(FreeOp* fop, JSObject* obj);
static const ClassOps ErrorObjectClassOps = {
nullptr, /* addProperty */
nullptr, /* delProperty */
nullptr, /* getProperty */
nullptr, /* setProperty */
nullptr, /* enumerate */
nullptr, /* resolve */
nullptr, /* mayResolve */
exn_finalize,
nullptr, /* call */
nullptr, /* hasInstance */
nullptr, /* construct */
nullptr, /* trace */
};
const Class
ErrorObject::classes[JSEXN_ERROR_LIMIT] = {
IMPLEMENT_ERROR_CLASS(Error),
IMPLEMENT_ERROR_CLASS(InternalError),
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(RuntimeError)
};
static void
exn_finalize(FreeOp* fop, JSObject* obj)
{
MOZ_ASSERT(fop->maybeOffMainThread());
if (JSErrorReport* report = obj->as<ErrorObject>().getErrorReport())
fop->delete_(report);
}
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(messageArg)) {
message = ToString<CanGC>(cx, args[messageArg]);
if (!message)
return nullptr;
}
/* Find the scripted caller, but only ones we're allowed to know about. */
NonBuiltinFrameIter iter(cx, cx->compartment()->principals());
/* Set the 'fileName' property. */
RootedString fileName(cx);
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 (!fileName)
return nullptr;
/* Set the 'lineNumber' property. */
uint32_t lineNumber, columnNumber = 0;
if (args.length() > messageArg + 2) {
if (!ToUint32(cx, args[messageArg + 2], &lineNumber))
return nullptr;
} else {
lineNumber = iter.done() ? 0 : iter.computeLine(&columnNumber);
// XXX: Make the column 1-based as in other browsers, instead of 0-based
// which is how SpiderMonkey stores it internally. This will be
// unnecessary once bug 1144340 is fixed.
++columnNumber;
}
RootedObject stack(cx);
if (!CaptureStack(cx, &stack))
return nullptr;
return ErrorObject::create(cx, exnType, stack, fileName, 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");
// ES6 19.5.1.1 mandates the .prototype lookup happens before the toString
RootedObject proto(cx);
if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
return false;
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));
if (!array) {
return nullptr;
}
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 (!GetPrototypeFromCallableConstructor(cx, args, &proto)) {
return false;
}
// TypeError anyway, but this gives a better error message.
if (!args.requireAtLeast(cx, "AggregateError", 1)) {
return false;
}
// 9.1.13 OrdinaryCreateFromConstructor, step 3.
// Step 3.
Rooted<ErrorObject*> obj(
cx, CreateErrorObject(cx, args, 1, JSEXN_AGGREGATEERR, proto));
if (!obj) {
return false;
}
// Step 4.
RootedArrayObject errorsList(cx, IterableToArray(cx, args.get(0)));
if (!errorsList) {
return false;
}
// 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);
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]);
}
RootedObject protoProto(cx, GlobalObject::getOrCreateErrorPrototype(cx, cx->global()));
if (!protoProto)
return nullptr;
return GlobalObject::createBlankPrototypeInheriting(cx, cx->global(),
&ErrorObject::protoClasses[type],
protoProto);
}
/* static */ JSObject*
ErrorObject::createConstructor(JSContext* cx, JSProtoKey key)
{
JSExnType type = ExnTypeFromProtoKey(key);
RootedObject ctor(cx);
if (type == JSEXN_ERR) {
ctor = GenericCreateConstructor<Error, 1, gc::AllocKind::FUNCTION_EXTENDED>(cx, key);
} else {
RootedFunction proto(cx, GlobalObject::getOrCreateErrorConstructor(cx, cx->global()));
if (!proto)
return nullptr;
Native native;
unsigned nargs;
if (type == JSEXN_AGGREGATEERR) {
native = AggregateError;
nargs = 2;
} else {
native = Error;
nargs = 1;
}
ctor =
NewFunctionWithProto(cx, native, nargs, JSFunction::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;
}
/* static */ Shape*
js::ErrorObject::assignInitialShape(ExclusiveContext* cx, Handle<ErrorObject*> obj)
{
@ -280,3 +663,81 @@ js::ErrorObject::setStack_impl(JSContext* cx, const CallArgs& args)
return DefineProperty(cx, thisObj, cx->names().stack, val);
}
/*
* Return a string that may eval to something similar to the original object.
*/
static bool
exn_toSource(JSContext* cx, unsigned argc, Value* vp)
{
JS_CHECK_RECURSION(cx, return false);
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
RootedValue nameVal(cx);
RootedString name(cx);
if (!GetProperty(cx, obj, obj, cx->names().name, &nameVal) ||
!(name = ToString<CanGC>(cx, nameVal)))
{
return false;
}
RootedValue messageVal(cx);
RootedString message(cx);
if (!GetProperty(cx, obj, obj, cx->names().message, &messageVal) ||
!(message = ValueToSource(cx, messageVal)))
{
return false;
}
RootedValue filenameVal(cx);
RootedString filename(cx);
if (!GetProperty(cx, obj, obj, cx->names().fileName, &filenameVal) ||
!(filename = ValueToSource(cx, filenameVal)))
{
return false;
}
RootedValue linenoVal(cx);
uint32_t lineno;
if (!GetProperty(cx, obj, obj, cx->names().lineNumber, &linenoVal) ||
!ToUint32(cx, linenoVal, &lineno))
{
return false;
}
StringBuffer sb(cx);
if (!sb.append("(new ") || !sb.append(name) || !sb.append("("))
return false;
if (!sb.append(message))
return false;
if (!filename->empty()) {
if (!sb.append(", ") || !sb.append(filename))
return false;
}
if (lineno != 0) {
/* We have a line, but no filename, add empty string */
if (filename->empty() && !sb.append(", \"\""))
return false;
JSString* linenumber = ToString<CanGC>(cx, linenoVal);
if (!linenumber)
return false;
if (!sb.append(", ") || !sb.append(linenumber))
return false;
}
if (!sb.append("))"))
return false;
JSString* str = sb.finishString();
if (!str)
return false;
args.rval().setString(str);
return true;
}

View file

@ -13,12 +13,7 @@
#include "vm/Shape.h"
namespace js {
/*
* Initialize the exception constructor/prototype hierarchy.
*/
extern JSObject*
InitExceptionClasses(JSContext* cx, HandleObject obj);
class ArrayObject;
class ErrorObject : public NativeObject
{
@ -28,10 +23,6 @@ class ErrorObject : public NativeObject
static JSObject*
createConstructor(JSContext* cx, JSProtoKey key);
/* For access to createProto. */
friend JSObject*
js::InitExceptionClasses(JSContext* cx, HandleObject global);
static bool
init(JSContext* cx, Handle<ErrorObject*> obj, JSExnType type,
ScopedJSFreePtr<JSErrorReport>* errorReport, HandleString fileName, HandleObject stack,

View file

@ -356,6 +356,16 @@ intrinsic_ThrowSyntaxError(JSContext* cx, unsigned argc, Value* vp)
return false;
}
static bool
intrinsic_ThrowAggregateError(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() >= 1);
ThrowErrorWithType(cx, JSEXN_AGGREGATEERR, args);
return false;
}
static bool
intrinsic_ThrowInternalError(JSContext* cx, unsigned argc, Value* vp)
{
@ -2223,6 +2233,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("ThrowRangeError", intrinsic_ThrowRangeError, 4,0),
JS_FN("ThrowTypeError", intrinsic_ThrowTypeError, 4,0),
JS_FN("ThrowSyntaxError", intrinsic_ThrowSyntaxError, 4,0),
JS_FN("ThrowAggregateError", intrinsic_ThrowAggregateError, 4,0),
JS_FN("ThrowInternalError", intrinsic_ThrowInternalError, 4,0),
JS_FN("GetErrorMessage", intrinsic_GetErrorMessage, 1,0),
JS_FN("CreateModuleSyntaxError", intrinsic_CreateModuleSyntaxError, 4,0),

View file

@ -108,6 +108,7 @@ const char* const XPCJSContext::mStrings[] = {
"columnNumber", // IDX_COLUMNNUMBER
"stack", // IDX_STACK
"message", // IDX_MESSAGE
"errors", // IDX_ERRORS
"lastIndex" // IDX_LASTINDEX
};

View file

@ -495,6 +495,7 @@ public:
IDX_COLUMNNUMBER ,
IDX_STACK ,
IDX_MESSAGE ,
IDX_ERRORS ,
IDX_LASTINDEX ,
IDX_TOTAL_COUNT // just a count of the above
};

View file

@ -41,11 +41,11 @@ using namespace XrayUtils;
#define Between(x, a, b) (a <= x && x <= b)
static_assert(JSProto_URIError - JSProto_Error == 7, "New prototype added in error object range");
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 jsprototypes.h ordering here");
MOZ_FOR_EACH(AssertErrorObjectKeyInBounds, (),
(JSProto_Error, JSProto_InternalError, JSProto_EvalError, JSProto_RangeError,
(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");
@ -608,6 +608,11 @@ JSXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper,
FillPropertyDescriptor(desc, nullptr, 0, UndefinedValue());
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);