mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-04 06:48:38 +09:00
Merge remote-tracking branch 'origin/tracking' into custom
This commit is contained in:
commit
ea9d00f005
22 changed files with 2119 additions and 767 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -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()),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
5
js/src/jit-test/tests/auto-regress/bug1652148.js
Normal file
5
js/src/jit-test/tests/auto-regress/bug1652148.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// |jit-test| skip-if: !('oomTest' in this)
|
||||
|
||||
oomTest(() => {
|
||||
new AggregateError([]);
|
||||
});
|
||||
|
|
@ -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);
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -642,6 +642,7 @@ typedef enum JSExnType {
|
|||
JSEXN_ERR,
|
||||
JSEXN_FIRST = JSEXN_ERR,
|
||||
JSEXN_INTERNALERR,
|
||||
JSEXN_AGGREGATEERR,
|
||||
JSEXN_EVALERR,
|
||||
JSEXN_RANGEERR,
|
||||
JSEXN_REFERENCEERR,
|
||||
|
|
|
|||
|
|
@ -947,6 +947,7 @@ void
|
|||
JSCompartment::purge()
|
||||
{
|
||||
dtoaCache.purge();
|
||||
promiseLookup.purge();
|
||||
lastCachedNativeIterator = nullptr;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
370
js/src/jsexn.cpp
370
js/src/jsexn.cpp
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)) \
|
||||
|
|
|
|||
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, "");
|
||||
|
||||
// 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);
|
||||
69
js/src/tests/non262/Promise/any-stack.js
Normal file
69
js/src/tests/non262/Promise/any-stack.js
Normal 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);
|
||||
78
js/src/tests/non262/Promise/any.js
Normal file
78
js/src/tests/non262/Promise/any.js
Normal 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);
|
||||
|
|
@ -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") \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ const char* const XPCJSContext::mStrings[] = {
|
|||
"columnNumber", // IDX_COLUMNNUMBER
|
||||
"stack", // IDX_STACK
|
||||
"message", // IDX_MESSAGE
|
||||
"errors", // IDX_ERRORS
|
||||
"lastIndex" // IDX_LASTINDEX
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -495,6 +495,7 @@ public:
|
|||
IDX_COLUMNNUMBER ,
|
||||
IDX_STACK ,
|
||||
IDX_MESSAGE ,
|
||||
IDX_ERRORS ,
|
||||
IDX_LASTINDEX ,
|
||||
IDX_TOTAL_COUNT // just a count of the above
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue