mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Backed out 5 changesets (bug 911216) for apparently tripling the runtime of SM(p) jobs along with introducing a failure in SM(p)
Backed out changeset d70235224525 (bug 911216) Backed out changeset 2f503e373e6f (bug 911216) Backed out changeset 2e98f8b36bc6 (bug 911216) Backed out changeset bd5acdf4a2a1 (bug 911216) Backed out changeset 58716e562690 (bug 911216) MozReview-Commit-ID: 807ajHOZvQn
This commit is contained in:
parent
e6d52431b6
commit
46f04c50ca
@ -77,103 +77,6 @@ UnwrapPromise(JS::Handle<JSObject*> aPromise, ErrorResult& aRv)
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
/* static */ void
|
||||
PromiseDebugging::GetState(GlobalObject& aGlobal, JS::Handle<JSObject*> aPromise,
|
||||
PromiseDebuggingStateHolder& aState,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
JSContext* cx = aGlobal.Context();
|
||||
JS::Rooted<JSObject*> obj(cx, js::CheckedUnwrap(aPromise));
|
||||
if (!obj || !JS::IsPromiseObject(obj)) {
|
||||
aRv.ThrowTypeError<MSG_IS_NOT_PROMISE>(NS_LITERAL_STRING(
|
||||
"Argument of PromiseDebugging.getState"));
|
||||
return;
|
||||
}
|
||||
switch (JS::GetPromiseState(obj)) {
|
||||
case JS::PromiseState::Pending:
|
||||
aState.mState = PromiseDebuggingState::Pending;
|
||||
break;
|
||||
case JS::PromiseState::Fulfilled:
|
||||
aState.mState = PromiseDebuggingState::Fulfilled;
|
||||
aState.mValue = JS::GetPromiseResult(obj);
|
||||
break;
|
||||
case JS::PromiseState::Rejected:
|
||||
aState.mState = PromiseDebuggingState::Rejected;
|
||||
aState.mReason = JS::GetPromiseResult(obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
PromiseDebugging::GetPromiseID(GlobalObject& aGlobal,
|
||||
JS::Handle<JSObject*> aPromise,
|
||||
nsString& aID,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
JSContext* cx = aGlobal.Context();
|
||||
JS::Rooted<JSObject*> obj(cx, js::CheckedUnwrap(aPromise));
|
||||
if (!obj || !JS::IsPromiseObject(obj)) {
|
||||
aRv.ThrowTypeError<MSG_IS_NOT_PROMISE>(NS_LITERAL_STRING(
|
||||
"Argument of PromiseDebugging.getState"));
|
||||
return;
|
||||
}
|
||||
uint64_t promiseID = JS::GetPromiseID(obj);
|
||||
aID = sIDPrefix;
|
||||
aID.AppendInt(promiseID);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
PromiseDebugging::GetAllocationStack(GlobalObject& aGlobal,
|
||||
JS::Handle<JSObject*> aPromise,
|
||||
JS::MutableHandle<JSObject*> aStack,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
JSContext* cx = aGlobal.Context();
|
||||
JS::Rooted<JSObject*> obj(cx, js::CheckedUnwrap(aPromise));
|
||||
if (!obj || !JS::IsPromiseObject(obj)) {
|
||||
aRv.ThrowTypeError<MSG_IS_NOT_PROMISE>(NS_LITERAL_STRING(
|
||||
"Argument of PromiseDebugging.getAllocationStack"));
|
||||
return;
|
||||
}
|
||||
aStack.set(JS::GetPromiseAllocationSite(obj));
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
PromiseDebugging::GetRejectionStack(GlobalObject& aGlobal,
|
||||
JS::Handle<JSObject*> aPromise,
|
||||
JS::MutableHandle<JSObject*> aStack,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
JSContext* cx = aGlobal.Context();
|
||||
JS::Rooted<JSObject*> obj(cx, js::CheckedUnwrap(aPromise));
|
||||
if (!obj || !JS::IsPromiseObject(obj)) {
|
||||
aRv.ThrowTypeError<MSG_IS_NOT_PROMISE>(NS_LITERAL_STRING(
|
||||
"Argument of PromiseDebugging.getRejectionStack"));
|
||||
return;
|
||||
}
|
||||
aStack.set(JS::GetPromiseResolutionSite(obj));
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
PromiseDebugging::GetFullfillmentStack(GlobalObject& aGlobal,
|
||||
JS::Handle<JSObject*> aPromise,
|
||||
JS::MutableHandle<JSObject*> aStack,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
JSContext* cx = aGlobal.Context();
|
||||
JS::Rooted<JSObject*> obj(cx, js::CheckedUnwrap(aPromise));
|
||||
if (!obj || !JS::IsPromiseObject(obj)) {
|
||||
aRv.ThrowTypeError<MSG_IS_NOT_PROMISE>(NS_LITERAL_STRING(
|
||||
"Argument of PromiseDebugging.getFulfillmentStack"));
|
||||
return;
|
||||
}
|
||||
aStack.set(JS::GetPromiseResolutionSite(obj));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* static */ void
|
||||
PromiseDebugging::GetState(GlobalObject&, JS::Handle<JSObject*> aPromise,
|
||||
|
@ -32,13 +32,11 @@ public:
|
||||
static void Init();
|
||||
static void Shutdown();
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
static void GetState(GlobalObject&, JS::Handle<JSObject*> aPromise,
|
||||
PromiseDebuggingStateHolder& aState,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static void GetPromiseID(GlobalObject&, JS::Handle<JSObject*>, nsString&,
|
||||
ErrorResult&);
|
||||
|
||||
static void GetAllocationStack(GlobalObject&, JS::Handle<JSObject*> aPromise,
|
||||
JS::MutableHandle<JSObject*> aStack,
|
||||
ErrorResult& aRv);
|
||||
@ -49,8 +47,6 @@ public:
|
||||
JS::Handle<JSObject*> aPromise,
|
||||
JS::MutableHandle<JSObject*> aStack,
|
||||
ErrorResult& aRv);
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
static void GetDependentPromises(GlobalObject&,
|
||||
JS::Handle<JSObject*> aPromise,
|
||||
nsTArray<RefPtr<Promise>>& aPromises,
|
||||
@ -60,6 +56,9 @@ public:
|
||||
ErrorResult& aRv);
|
||||
static double GetTimeToSettle(GlobalObject&, JS::Handle<JSObject*> aPromise,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static void GetPromiseID(GlobalObject&, JS::Handle<JSObject*>, nsString&,
|
||||
ErrorResult&);
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
// Mechanism for watching uncaught instances of Promise.
|
||||
|
@ -52,6 +52,7 @@ callback interface UncaughtRejectionObserver {
|
||||
|
||||
[ChromeOnly, Exposed=(Window,System)]
|
||||
interface PromiseDebugging {
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
/**
|
||||
* The various functions on this interface all expect to take promises but
|
||||
* don't want the WebIDL behavior of assimilating random passed-in objects
|
||||
@ -67,13 +68,6 @@ interface PromiseDebugging {
|
||||
[Throws]
|
||||
static PromiseDebuggingStateHolder getState(object p);
|
||||
|
||||
/**
|
||||
* Return an identifier for a promise. This identifier is guaranteed
|
||||
* to be unique to the current process.
|
||||
*/
|
||||
[Throws]
|
||||
static DOMString getPromiseID(object p);
|
||||
|
||||
/**
|
||||
* Return the stack to the promise's allocation point. This can
|
||||
* return null if the promise was not created from script.
|
||||
@ -97,7 +91,13 @@ interface PromiseDebugging {
|
||||
[Throws]
|
||||
static object? getFullfillmentStack(object p);
|
||||
|
||||
#ifndef SPIDERMONKEY_PROMISE
|
||||
/**
|
||||
* Return an identifier for a promise. This identifier is guaranteed
|
||||
* to be unique to this instance of Firefox.
|
||||
*/
|
||||
[Throws]
|
||||
static DOMString getPromiseID(object p);
|
||||
|
||||
/**
|
||||
* Get the promises directly depending on a given promise. These are:
|
||||
*
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include "gc/Heap.h"
|
||||
#include "js/Date.h"
|
||||
#include "js/Debug.h"
|
||||
#include "vm/SelfHosting.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
@ -41,37 +40,7 @@ static const JSPropertySpec promise_static_properties[] = {
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
static Value
|
||||
Now()
|
||||
{
|
||||
return JS::TimeValue(JS::TimeClip(static_cast<double>(PRMJ_Now()) / PRMJ_USEC_PER_MSEC));
|
||||
}
|
||||
|
||||
static bool
|
||||
CreateResolvingFunctions(JSContext* cx, HandleValue promise,
|
||||
MutableHandleValue resolveVal,
|
||||
MutableHandleValue rejectVal)
|
||||
{
|
||||
InvokeArgs args(cx);
|
||||
if (!args.init(1))
|
||||
return false;
|
||||
args.setThis(UndefinedValue());
|
||||
args[0].set(promise);
|
||||
|
||||
if (!CallSelfHostedFunction(cx, cx->names().CreateResolvingFunctions, args))
|
||||
return false;
|
||||
|
||||
RootedArrayObject resolvingFunctions(cx, &args.rval().toObject().as<ArrayObject>());
|
||||
resolveVal.set(resolvingFunctions->getDenseElement(0));
|
||||
rejectVal.set(resolvingFunctions->getDenseElement(1));
|
||||
|
||||
MOZ_ASSERT(IsCallable(resolveVal));
|
||||
MOZ_ASSERT(IsCallable(rejectVal));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ES2016, February 12 draft, 25.4.3.1. steps 3-11.
|
||||
// ES6, 25.4.3.1. steps 3-11.
|
||||
PromiseObject*
|
||||
PromiseObject::create(JSContext* cx, HandleObject executor, HandleObject proto /* = nullptr */)
|
||||
{
|
||||
@ -104,36 +73,33 @@ PromiseObject::create(JSContext* cx, HandleObject executor, HandleObject proto /
|
||||
ac.emplace(cx, usedProto);
|
||||
|
||||
promise = &NewObjectWithClassProto(cx, &class_, usedProto)->as<PromiseObject>();
|
||||
|
||||
// Step 4.
|
||||
if (!promise)
|
||||
return nullptr;
|
||||
|
||||
// Step 4.
|
||||
// Step 5.
|
||||
promise->setFixedSlot(PROMISE_STATE_SLOT, Int32Value(PROMISE_STATE_PENDING));
|
||||
|
||||
// Step 5.
|
||||
// Step 6.
|
||||
RootedArrayObject reactions(cx, NewDenseEmptyArray(cx));
|
||||
if (!reactions)
|
||||
return nullptr;
|
||||
promise->setFixedSlot(PROMISE_FULFILL_REACTIONS_SLOT, ObjectValue(*reactions));
|
||||
|
||||
// Step 6.
|
||||
// Step 7.
|
||||
reactions = NewDenseEmptyArray(cx);
|
||||
if (!reactions)
|
||||
return nullptr;
|
||||
promise->setFixedSlot(PROMISE_REJECT_REACTIONS_SLOT, ObjectValue(*reactions));
|
||||
|
||||
// Step 7.
|
||||
promise->setFixedSlot(PROMISE_IS_HANDLED_SLOT,
|
||||
Int32Value(PROMISE_IS_HANDLED_STATE_UNHANDLED));
|
||||
|
||||
// Store an allocation stack so we can later figure out what the
|
||||
// control flow was for some unexpected results. Frightfully expensive,
|
||||
// but oh well.
|
||||
RootedObject stack(cx);
|
||||
if (!JS::CaptureCurrentStack(cx, &stack, 0))
|
||||
return nullptr;
|
||||
promise->setFixedSlot(PROMISE_ALLOCATION_SITE_SLOT, ObjectOrNullValue(stack));
|
||||
promise->setFixedSlot(PROMISE_ALLOCATION_TIME_SLOT, Now());
|
||||
promise->setFixedSlot(PROMISE_ALLOCATION_SITE_SLOT, ObjectValue(*stack));
|
||||
Value now = JS::TimeValue(JS::TimeClip(static_cast<double>(PRMJ_Now()) /
|
||||
PRMJ_USEC_PER_MSEC));
|
||||
promise->setFixedSlot(PROMISE_ALLOCATION_TIME_SLOT, now);
|
||||
}
|
||||
|
||||
RootedValue promiseVal(cx, ObjectValue(*promise));
|
||||
@ -144,10 +110,27 @@ PromiseObject::create(JSContext* cx, HandleObject executor, HandleObject proto /
|
||||
// The resolving functions are created in the compartment active when the
|
||||
// (maybe wrapped) Promise constructor was called. They contain checks and
|
||||
// can unwrap the Promise if required.
|
||||
RootedValue resolveVal(cx);
|
||||
RootedValue rejectVal(cx);
|
||||
if (!CreateResolvingFunctions(cx, promiseVal, &resolveVal, &rejectVal))
|
||||
RootedValue resolvingFunctionsVal(cx);
|
||||
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().CreateResolvingFunctions,
|
||||
&resolvingFunctionsVal))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
InvokeArgs args(cx);
|
||||
if (!args.init(1))
|
||||
return nullptr;
|
||||
args.setCallee(resolvingFunctionsVal);
|
||||
args.setThis(UndefinedValue());
|
||||
args[0].set(promiseVal);
|
||||
|
||||
if (!Invoke(cx, args))
|
||||
return nullptr;
|
||||
|
||||
RootedArrayObject resolvingFunctions(cx, &args.rval().toObject().as<ArrayObject>());
|
||||
RootedValue resolveVal(cx, resolvingFunctions->getDenseElement(0));
|
||||
MOZ_ASSERT(IsCallable(resolveVal));
|
||||
RootedValue rejectVal(cx, resolvingFunctions->getDenseElement(1));
|
||||
MOZ_ASSERT(IsCallable(rejectVal));
|
||||
|
||||
// Need to wrap the resolution functions before storing them on the Promise.
|
||||
if (wrappedProto) {
|
||||
@ -194,7 +177,6 @@ PromiseObject::create(JSContext* cx, HandleObject executor, HandleObject proto /
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Let the Debugger know about this Promise.
|
||||
JS::dbg::onNewPromise(cx, promise);
|
||||
|
||||
// Step 11.
|
||||
@ -206,7 +188,7 @@ namespace {
|
||||
mozilla::Atomic<uint64_t> gIDGenerator(0);
|
||||
} // namespace
|
||||
|
||||
uint64_t
|
||||
double
|
||||
PromiseObject::getID()
|
||||
{
|
||||
Value idVal(getReservedSlot(PROMISE_ID_SLOT));
|
||||
@ -214,7 +196,7 @@ PromiseObject::getID()
|
||||
idVal.setDouble(++gIDGenerator);
|
||||
setReservedSlot(PROMISE_ID_SLOT, idVal);
|
||||
}
|
||||
return uint64_t(idVal.toNumber());
|
||||
return idVal.toNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -407,137 +389,6 @@ PromiseObject::reject(JSContext* cx, HandleValue rejectionValue)
|
||||
return Invoke(cx, args);
|
||||
}
|
||||
|
||||
void PromiseObject::onSettled(JSContext* cx)
|
||||
{
|
||||
Rooted<PromiseObject*> promise(cx, this);
|
||||
RootedObject stack(cx);
|
||||
if (!JS::CaptureCurrentStack(cx, &stack, 0)) {
|
||||
cx->clearPendingException();
|
||||
return;
|
||||
}
|
||||
promise->setFixedSlot(PROMISE_RESOLUTION_SITE_SLOT, ObjectOrNullValue(stack));
|
||||
promise->setFixedSlot(PROMISE_RESOLUTION_TIME_SLOT, Now());
|
||||
|
||||
if (promise->state() == JS::PromiseState::Rejected &&
|
||||
promise->getFixedSlot(PROMISE_IS_HANDLED_SLOT).toInt32() !=
|
||||
PROMISE_IS_HANDLED_STATE_HANDLED)
|
||||
{
|
||||
cx->runtime()->addUnhandledRejectedPromise(cx, promise);
|
||||
}
|
||||
|
||||
JS::dbg::onPromiseSettled(cx, promise);
|
||||
}
|
||||
|
||||
// ES6, 25.4.2.1.
|
||||
bool
|
||||
PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedFunction job(cx, &args.callee().as<JSFunction>());
|
||||
RootedNativeObject jobArgs(cx, &job->getExtendedSlot(0).toObject().as<NativeObject>());
|
||||
|
||||
RootedValue argument(cx, jobArgs->getDenseElement(1));
|
||||
|
||||
// Step 1 (omitted).
|
||||
|
||||
// Steps 2-3.
|
||||
RootedValue handlerVal(cx, jobArgs->getDenseElement(0));
|
||||
RootedValue handlerResult(cx);
|
||||
bool shouldReject = false;
|
||||
|
||||
// Steps 4-7.
|
||||
if (handlerVal.isNumber()) {
|
||||
int32_t handlerNum = int32_t(handlerVal.toNumber());
|
||||
// Step 4.
|
||||
if (handlerNum == PROMISE_HANDLER_IDENTITY) {
|
||||
handlerResult = argument;
|
||||
} else {
|
||||
// Step 5.
|
||||
MOZ_ASSERT(handlerNum == PROMISE_HANDLER_THROWER);
|
||||
shouldReject = true;
|
||||
handlerResult = argument;
|
||||
}
|
||||
} else {
|
||||
// Step 6.
|
||||
InvokeArgs args2(cx);
|
||||
if (!args2.init(1))
|
||||
return false;
|
||||
args2.setThis(UndefinedValue());
|
||||
args2.setCallee(handlerVal);
|
||||
args2[0].set(argument);
|
||||
if (Invoke(cx, args2)) {
|
||||
handlerResult = args2.rval();
|
||||
} else {
|
||||
shouldReject = true;
|
||||
// Not much we can do about uncatchable exceptions, so just bail
|
||||
// for those.
|
||||
if (!cx->isExceptionPending() || !GetAndClearException(cx, &handlerResult))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Steps 7-9.
|
||||
InvokeArgs args2(cx);
|
||||
if (!args2.init(1))
|
||||
return false;
|
||||
args2.setThis(UndefinedValue());
|
||||
args2[0].set(handlerResult);
|
||||
if (shouldReject) {
|
||||
args2.setCallee(jobArgs->getDenseElement(3));
|
||||
} else {
|
||||
args2.setCallee(jobArgs->getDenseElement(2));
|
||||
}
|
||||
bool result = Invoke(cx, args2);
|
||||
|
||||
args.rval().set(args2.rval());
|
||||
return result;
|
||||
}
|
||||
|
||||
// ES6, 25.4.2.2.
|
||||
bool
|
||||
PromiseResolveThenableJob(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedFunction job(cx, &args.callee().as<JSFunction>());
|
||||
RootedNativeObject jobArgs(cx, &job->getExtendedSlot(0).toObject().as<NativeObject>());
|
||||
|
||||
RootedValue promise(cx, jobArgs->getDenseElement(2));
|
||||
RootedValue then(cx, jobArgs->getDenseElement(0));
|
||||
RootedValue thenable(cx, jobArgs->getDenseElement(1));
|
||||
|
||||
// Step 1.
|
||||
RootedValue resolveVal(cx);
|
||||
RootedValue rejectVal(cx);
|
||||
if (!CreateResolvingFunctions(cx, promise, &resolveVal, &rejectVal))
|
||||
return false;
|
||||
|
||||
// Step 2.
|
||||
InvokeArgs args2(cx);
|
||||
if (!args2.init(2))
|
||||
return false;
|
||||
args2.setThis(thenable);
|
||||
args2.setCallee(then);
|
||||
args2[0].set(resolveVal);
|
||||
args2[1].set(rejectVal);
|
||||
|
||||
// In difference to the usual pattern, we return immediately on success.
|
||||
if (Invoke(cx, args2))
|
||||
return true;
|
||||
|
||||
RootedValue thenCallResult(cx);
|
||||
if (!GetAndClearException(cx, &thenCallResult))
|
||||
return false;
|
||||
|
||||
InvokeArgs rejectArgs(cx);
|
||||
if (!rejectArgs.init(1))
|
||||
return false;
|
||||
rejectArgs.setThis(UndefinedValue());
|
||||
rejectArgs.setCallee(rejectVal);
|
||||
rejectArgs[0].set(thenCallResult);
|
||||
|
||||
return Invoke(cx, rejectArgs);
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
||||
static JSObject*
|
||||
|
@ -19,7 +19,7 @@ class AutoSetNewObjectMetadata;
|
||||
class PromiseObject : public NativeObject
|
||||
{
|
||||
public:
|
||||
static const unsigned RESERVED_SLOTS = 12;
|
||||
static const unsigned RESERVED_SLOTS = 11;
|
||||
static const Class class_;
|
||||
static const Class protoClass_;
|
||||
static PromiseObject* create(JSContext* cx, HandleObject executor,
|
||||
@ -42,16 +42,10 @@ class PromiseObject : public NativeObject
|
||||
bool resolve(JSContext* cx, HandleValue resolutionValue);
|
||||
bool reject(JSContext* cx, HandleValue rejectionValue);
|
||||
|
||||
void onSettled(JSContext* cx);
|
||||
|
||||
double allocationTime() { return getFixedSlot(PROMISE_ALLOCATION_TIME_SLOT).toNumber(); }
|
||||
double resolutionTime() { return getFixedSlot(PROMISE_RESOLUTION_TIME_SLOT).toNumber(); }
|
||||
JSObject* allocationSite() {
|
||||
return getFixedSlot(PROMISE_ALLOCATION_SITE_SLOT).toObjectOrNull();
|
||||
}
|
||||
JSObject* resolutionSite() {
|
||||
return getFixedSlot(PROMISE_RESOLUTION_SITE_SLOT).toObjectOrNull();
|
||||
}
|
||||
JSObject* allocationSite() { return &getFixedSlot(PROMISE_ALLOCATION_SITE_SLOT).toObject(); }
|
||||
JSObject* resolutionSite() { return &getFixedSlot(PROMISE_RESOLUTION_SITE_SLOT).toObject(); }
|
||||
double lifetime() {
|
||||
double now = JS::TimeClip(static_cast<double>(PRMJ_Now()) / PRMJ_USEC_PER_MSEC).toDouble();
|
||||
return now - allocationTime();
|
||||
@ -61,23 +55,9 @@ class PromiseObject : public NativeObject
|
||||
return resolutionTime() - allocationTime();
|
||||
}
|
||||
bool dependentPromises(JSContext* cx, AutoValueVector& values);
|
||||
uint64_t getID();
|
||||
bool markedAsUncaught() {
|
||||
return getFixedSlot(PROMISE_IS_HANDLED_SLOT).toInt32() != PROMISE_IS_HANDLED_STATE_HANDLED;
|
||||
}
|
||||
void markAsReported() {
|
||||
MOZ_ASSERT(getFixedSlot(PROMISE_IS_HANDLED_SLOT).toInt32() ==
|
||||
PROMISE_IS_HANDLED_STATE_UNHANDLED);
|
||||
setFixedSlot(PROMISE_IS_HANDLED_SLOT, Int32Value(PROMISE_IS_HANDLED_STATE_REPORTED));
|
||||
}
|
||||
double getID();
|
||||
};
|
||||
|
||||
// ES6, 25.4.2.1.
|
||||
bool PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
// ES6, 25.4.2.2.
|
||||
bool PromiseResolveThenableJob(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* builtin_Promise_h */
|
||||
|
@ -124,7 +124,6 @@ function FulfillUnwrappedPromise(value) {
|
||||
}
|
||||
|
||||
// Commoned-out implementation of 25.4.1.4. and 25.4.1.7.
|
||||
// ES2016 February 12 draft.
|
||||
function ResolvePromise(promise, valueOrReason, reactionsSlot, state) {
|
||||
// Step 1.
|
||||
assert(GetPromiseState(promise) === PROMISE_STATE_PENDING,
|
||||
@ -152,11 +151,12 @@ function ResolvePromise(promise, valueOrReason, reactionsSlot, state) {
|
||||
UnsafeSetReservedSlot(promise, PROMISE_REJECT_FUNCTION_SLOT, null);
|
||||
|
||||
// Now that everything else is done, do the things the debugger needs.
|
||||
// Step 7 of RejectPromise implemented in the debugger intrinsic.
|
||||
let site = _dbg_captureCurrentStack(0);
|
||||
UnsafeSetReservedSlot(promise, PROMISE_RESOLUTION_SITE_SLOT, site);
|
||||
UnsafeSetReservedSlot(promise, PROMISE_RESOLUTION_TIME_SLOT, std_Date_now());
|
||||
_dbg_onPromiseSettled(promise);
|
||||
|
||||
// Step 7 of FulfillPromise.
|
||||
// Step 8 of RejectPromise.
|
||||
// Step 7.
|
||||
return TriggerPromiseReactions(reactions, valueOrReason);
|
||||
}
|
||||
|
||||
@ -209,7 +209,7 @@ function NewPromiseCapability(C) {
|
||||
|
||||
// ES6, 25.4.1.6. is implemented as an intrinsic in SelfHosting.cpp.
|
||||
|
||||
// ES2016, February 12 draft, 25.4.1.7.
|
||||
// ES6, 25.4.1.7.
|
||||
function RejectPromise(promise, reason) {
|
||||
return ResolvePromise(promise, reason, PROMISE_REJECT_REACTIONS_SLOT, PROMISE_STATE_REJECTED);
|
||||
}
|
||||
@ -222,26 +222,66 @@ function TriggerPromiseReactions(reactions, argument) {
|
||||
// Step 2 (implicit).
|
||||
}
|
||||
|
||||
// ES2016, February 12 draft 25.4.1.9, implemented in SelfHosting.cpp.
|
||||
|
||||
// ES6, 25.4.2.1.
|
||||
function EnqueuePromiseReactionJob(reaction, argument) {
|
||||
let capabilities = reaction.capabilities;
|
||||
_EnqueuePromiseReactionJob([reaction.handler,
|
||||
argument,
|
||||
capabilities.resolve,
|
||||
capabilities.reject
|
||||
],
|
||||
capabilities.promise);
|
||||
_EnqueuePromiseJob(function PromiseReactionJob() {
|
||||
// Step 1.
|
||||
assert(IsPromiseReaction(reaction), "Invalid promise reaction record");
|
||||
|
||||
// Step 2.
|
||||
let promiseCapability = reaction.capabilities;
|
||||
|
||||
// Step 3.
|
||||
let handler = reaction.handler;
|
||||
let handlerResult = argument;
|
||||
let shouldReject = false;
|
||||
|
||||
// Steps 4-6.
|
||||
if (handler === PROMISE_HANDLER_IDENTITY) {
|
||||
// handlerResult = argument; (implicit)
|
||||
} else if (handler === PROMISE_HANDLER_THROWER) {
|
||||
// handlerResult = argument; (implicit)
|
||||
shouldReject = true;
|
||||
} else {
|
||||
try {
|
||||
handlerResult = callContentFunction(handler, undefined, argument);
|
||||
} catch (e) {
|
||||
handlerResult = e;
|
||||
shouldReject = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 7.
|
||||
if (shouldReject) {
|
||||
// Step 7.a.
|
||||
callContentFunction(promiseCapability.reject, undefined, handlerResult);
|
||||
|
||||
// Step 7.b.
|
||||
return;
|
||||
}
|
||||
|
||||
// Steps 8-9.
|
||||
return callContentFunction(promiseCapability.resolve, undefined, handlerResult);
|
||||
});
|
||||
}
|
||||
|
||||
// ES6, 25.4.2.2.
|
||||
function EnqueuePromiseResolveThenableJob(promiseToResolve, thenable, then) {
|
||||
_EnqueuePromiseResolveThenableJob([then,
|
||||
thenable,
|
||||
promiseToResolve
|
||||
],
|
||||
promiseToResolve);
|
||||
_EnqueuePromiseJob(function PromiseResolveThenableJob() {
|
||||
// Step 1.
|
||||
let {0: resolve, 1: reject} = CreateResolvingFunctions(promiseToResolve);
|
||||
|
||||
// Steps 2-3.
|
||||
try {
|
||||
// Step 2.
|
||||
callContentFunction(then, thenable, resolve, reject);
|
||||
} catch (thenCallResult) {
|
||||
// Steps 3.a-b.
|
||||
callFunction(reject, undefined, thenCallResult);
|
||||
}
|
||||
|
||||
// Step 4 (implicit, no need to return anything).
|
||||
});
|
||||
}
|
||||
|
||||
// ES6, 25.4.3.1. (Implemented in C++).
|
||||
@ -861,7 +901,7 @@ function UnwrappedPerformPromiseThen(fulfilledHandler, rejectedHandler, promise,
|
||||
resultCapability);
|
||||
}
|
||||
|
||||
// ES2016, March 1, 2016 draft, 25.4.5.3.1.
|
||||
// ES6, 25.4.5.3.1.
|
||||
function PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability) {
|
||||
// Step 1.
|
||||
assert(IsPromise(promise), "Can't call PerformPromiseThen on non-Promise objects");
|
||||
@ -915,28 +955,15 @@ function PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability)
|
||||
}
|
||||
|
||||
// Step 9.
|
||||
else {
|
||||
else if (state === PROMISE_STATE_REJECTED) {
|
||||
// Step 9.a.
|
||||
assert(state === PROMISE_STATE_REJECTED, "Invalid Promise state " + state);
|
||||
|
||||
// Step 9.b.
|
||||
let reason = UnsafeGetReservedSlot(promise, PROMISE_RESULT_SLOT);
|
||||
|
||||
// Step 9.c.
|
||||
if (UnsafeGetInt32FromReservedSlot(promise, PROMISE_IS_HANDLED_SLOT) !==
|
||||
PROMISE_IS_HANDLED_STATE_HANDLED)
|
||||
{
|
||||
HostPromiseRejectionTracker(promise, PROMISE_REJECTION_TRACKER_OPERATION_HANDLE);
|
||||
}
|
||||
|
||||
// Step 9.d.
|
||||
// Step 9.b.
|
||||
EnqueuePromiseReactionJob(rejectReaction, reason);
|
||||
}
|
||||
|
||||
// Step 10.
|
||||
UnsafeSetReservedSlot(promise, PROMISE_IS_HANDLED_SLOT, PROMISE_IS_HANDLED_STATE_HANDLED);
|
||||
|
||||
// Step 11.
|
||||
return resultCapability.promise;
|
||||
}
|
||||
|
||||
|
@ -68,22 +68,14 @@
|
||||
#define PROMISE_ALLOCATION_TIME_SLOT 8
|
||||
#define PROMISE_RESOLUTION_TIME_SLOT 9
|
||||
#define PROMISE_ID_SLOT 10
|
||||
#define PROMISE_IS_HANDLED_SLOT 11
|
||||
|
||||
#define PROMISE_STATE_PENDING 0
|
||||
#define PROMISE_STATE_FULFILLED 1
|
||||
#define PROMISE_STATE_REJECTED 2
|
||||
|
||||
#define PROMISE_IS_HANDLED_STATE_HANDLED 0
|
||||
#define PROMISE_IS_HANDLED_STATE_UNHANDLED 1
|
||||
#define PROMISE_IS_HANDLED_STATE_REPORTED 2
|
||||
|
||||
#define PROMISE_HANDLER_IDENTITY 0
|
||||
#define PROMISE_HANDLER_THROWER 1
|
||||
|
||||
#define PROMISE_REJECTION_TRACKER_OPERATION_REJECT false
|
||||
#define PROMISE_REJECTION_TRACKER_OPERATION_HANDLE true
|
||||
|
||||
// NB: keep these in sync with the copy in jsfriendapi.h.
|
||||
#define JSITER_OWNONLY 0x8 /* iterate over obj's own properties only */
|
||||
#define JSITER_HIDDEN 0x10 /* also enumerate non-enumerable properties */
|
||||
|
@ -1,36 +0,0 @@
|
||||
if (typeof Promise === "undefined")
|
||||
quit(0);
|
||||
|
||||
let g = newGlobal();
|
||||
let dbg = new Debugger();
|
||||
let gw = dbg.addDebuggee(g);
|
||||
|
||||
g.promise = Promise.resolve(42);
|
||||
|
||||
let promiseDO = gw.getOwnPropertyDescriptor('promise').value;
|
||||
|
||||
assertEq(promiseDO.isPromise, true);
|
||||
|
||||
let state = promiseDO.promiseState;
|
||||
assertEq(state.state, "fulfilled");
|
||||
assertEq(state.value, 42);
|
||||
assertEq("reason" in state, true);
|
||||
assertEq(state.reason, undefined);
|
||||
|
||||
let allocationSite = promiseDO.promiseAllocationSite;
|
||||
// Depending on whether async stacks are activated, this can be null, which
|
||||
// has typeof null.
|
||||
assertEq(typeof allocationSite === "object", true);
|
||||
|
||||
let resolutionSite = promiseDO.promiseResolutionSite;
|
||||
// Depending on whether async stacks are activated, this can be null, which
|
||||
// has typeof null.
|
||||
assertEq(typeof resolutionSite === "object", true);
|
||||
|
||||
assertEq(promiseDO.promiseID, 1);
|
||||
|
||||
assertEq(typeof promiseDO.promiseDependentPromises, "object");
|
||||
assertEq(promiseDO.promiseDependentPromises.length, 0);
|
||||
|
||||
assertEq(typeof promiseDO.promiseLifetime, "number");
|
||||
assertEq(typeof promiseDO.promiseTimeToResolution, "number");
|
@ -4645,14 +4645,6 @@ JS::SetEnqueuePromiseJobCallback(JSRuntime* rt, JSEnqueuePromiseJobCallback call
|
||||
rt->enqueuePromiseJobCallbackData = data;
|
||||
}
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS::SetPromiseRejectionTrackerCallback(JSRuntime* rt, JSPromiseRejectionTrackerCallback callback,
|
||||
void* data /* = nullptr */)
|
||||
{
|
||||
rt->promiseRejectionTrackerCallback = callback;
|
||||
rt->promiseRejectionTrackerCallbackData = data;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject*)
|
||||
JS::NewPromiseObject(JSContext* cx, HandleObject executor, HandleObject proto /* = nullptr */)
|
||||
{
|
||||
@ -4694,7 +4686,7 @@ JS::GetPromiseState(JS::HandleObject obj)
|
||||
return promise->as<PromiseObject>().state();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(uint64_t)
|
||||
JS_PUBLIC_API(double)
|
||||
JS::GetPromiseID(JS::HandleObject promise)
|
||||
{
|
||||
return promise->as<PromiseObject>().getID();
|
||||
|
@ -603,20 +603,7 @@ typedef bool
|
||||
(* JSInterruptCallback)(JSContext* cx);
|
||||
|
||||
typedef bool
|
||||
(* JSEnqueuePromiseJobCallback)(JSContext* cx, JS::HandleObject job,
|
||||
JS::HandleObject allocationSite, void* data);
|
||||
|
||||
enum class PromiseRejectionHandlingState {
|
||||
Unhandled,
|
||||
Handled
|
||||
};
|
||||
|
||||
typedef void
|
||||
(* JSPromiseRejectionTrackerCallback)(JSContext* cx, JS::HandleObject promise,
|
||||
PromiseRejectionHandlingState state, void* data);
|
||||
|
||||
typedef void
|
||||
(* JSProcessPromiseCallback)(JSContext* cx, JS::HandleObject promise);
|
||||
(* JSEnqueuePromiseJobCallback)(JSContext* cx, JS::HandleObject job, void* data);
|
||||
|
||||
typedef void
|
||||
(* JSErrorReporter)(JSContext* cx, const char* message, JSErrorReport* report);
|
||||
@ -4306,23 +4293,13 @@ namespace JS {
|
||||
*
|
||||
* SpiderMonkey doesn't schedule Promise resolution jobs itself; instead,
|
||||
* using this function the embedding can provide a callback to do that
|
||||
* scheduling. The provided `callback` is invoked with the promise job,
|
||||
* the corresponding Promise's allocation stack, and the `data` pointer
|
||||
* passed here as arguments.
|
||||
* scheduling. The provided `callback` is invoked with the promise job
|
||||
* and the `data` pointer passed here as arguments.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
SetEnqueuePromiseJobCallback(JSRuntime* rt, JSEnqueuePromiseJobCallback callback,
|
||||
void* data = nullptr);
|
||||
|
||||
/**
|
||||
* Sets the callback that's invoked whenever a Promise is rejected without
|
||||
* a rejection handler, and when a Promise that was previously rejected
|
||||
* without a handler gets a handler attached.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void)
|
||||
SetPromiseRejectionTrackerCallback(JSRuntime* rt, JSPromiseRejectionTrackerCallback callback,
|
||||
void* data = nullptr);
|
||||
|
||||
/**
|
||||
* Returns a new instance of the Promise builtin class in the current
|
||||
* compartment, with the right slot layout. If a `proto` is passed, that gets
|
||||
@ -4367,7 +4344,7 @@ GetPromiseState(JS::HandleObject promise);
|
||||
/**
|
||||
* Returns the given Promise's process-unique ID.
|
||||
*/
|
||||
JS_PUBLIC_API(uint64_t)
|
||||
JS_PUBLIC_API(double)
|
||||
GetPromiseID(JS::HandleObject promise);
|
||||
|
||||
/**
|
||||
|
@ -139,7 +139,9 @@ static const double MAX_TIMEOUT_INTERVAL = 1800.0;
|
||||
# define SHARED_MEMORY_DEFAULT 0
|
||||
#endif
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
using JobQueue = GCVector<JSObject*, 0, SystemAllocPolicy>;
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
// Per-runtime shell state.
|
||||
struct ShellRuntime
|
||||
@ -153,8 +155,9 @@ struct ShellRuntime
|
||||
JS::PersistentRootedValue interruptFunc;
|
||||
bool lastWarningEnabled;
|
||||
JS::PersistentRootedValue lastWarning;
|
||||
JS::PersistentRootedValue promiseRejectionTrackerCallback;
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
JS::PersistentRooted<JobQueue> jobQueue;
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
/*
|
||||
* Watchdog thread state.
|
||||
@ -302,7 +305,6 @@ ShellRuntime::ShellRuntime(JSRuntime* rt)
|
||||
interruptFunc(rt, NullValue()),
|
||||
lastWarningEnabled(false),
|
||||
lastWarning(rt, NullValue()),
|
||||
promiseRejectionTrackerCallback(rt, NullValue()),
|
||||
watchdogLock(nullptr),
|
||||
watchdogWakeup(nullptr),
|
||||
watchdogThread(nullptr),
|
||||
@ -622,9 +624,9 @@ RunModule(JSContext* cx, const char* filename, FILE* file, bool compileOnly)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
static bool
|
||||
ShellEnqueuePromiseJobCallback(JSContext* cx, JS::HandleObject job, JS::HandleObject allocationSite,
|
||||
void* data)
|
||||
ShellEnqueuePromiseJobCallback(JSContext* cx, JS::HandleObject job, void* data)
|
||||
{
|
||||
ShellRuntime* sr = GetShellRuntime(cx);
|
||||
MOZ_ASSERT(job);
|
||||
@ -664,51 +666,7 @@ DrainJobQueue(JSContext* cx, unsigned argc, Value* vp)
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
ForwardingPromiseRejectionTrackerCallback(JSContext* cx, JS::HandleObject promise,
|
||||
PromiseRejectionHandlingState state, void* data)
|
||||
{
|
||||
RootedValue callback(cx, GetShellRuntime(cx)->promiseRejectionTrackerCallback);
|
||||
if (callback.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
InvokeArgs args2(cx);
|
||||
if (!args2.init(2)) {
|
||||
JS_ClearPendingException(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
args2.setCallee(callback);
|
||||
args2.setThis(UndefinedValue());
|
||||
|
||||
args2[0].setObject(*promise);
|
||||
args2[1].setInt32(static_cast<int32_t>(state));
|
||||
|
||||
if (!Invoke(cx, args2))
|
||||
JS_ClearPendingException(cx);
|
||||
}
|
||||
|
||||
static bool
|
||||
SetPromiseRejectionTrackerCallback(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (!IsCallable(args.get(0))) {
|
||||
JS_ReportError(cx,
|
||||
"setPromiseRejectionTrackerCallback expects a function as its sole "
|
||||
"argument");
|
||||
return false;
|
||||
}
|
||||
|
||||
GetShellRuntime(cx)->promiseRejectionTrackerCallback = args[0];
|
||||
JS::SetPromiseRejectionTrackerCallback(cx->runtime(),
|
||||
ForwardingPromiseRejectionTrackerCallback);
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
static bool
|
||||
EvalAndPrint(JSContext* cx, const char* bytes, size_t length,
|
||||
@ -807,7 +765,9 @@ ReadEvalPrintLoop(JSContext* cx, FILE* in, bool compileOnly)
|
||||
stderr);
|
||||
}
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
DrainJobQueue(cx);
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
} while (!hitEOF && !sr->quitting);
|
||||
|
||||
@ -960,6 +920,7 @@ CreateMappedArrayBuffer(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
static bool
|
||||
AddPromiseReactions(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
@ -998,6 +959,7 @@ AddPromiseReactions(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
||||
return JS::AddPromiseReactions(cx, promise, onResolve, onReject);
|
||||
}
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
static bool
|
||||
Options(JSContext* cx, unsigned argc, Value* vp)
|
||||
@ -2960,8 +2922,10 @@ WorkerMain(void* arg)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
sr->jobQueue.init(cx, JobQueue(SystemAllocPolicy()));
|
||||
JS::SetEnqueuePromiseJobCallback(rt, ShellEnqueuePromiseJobCallback);
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
JS::SetLargeAllocationFailureCallback(rt, my_LargeAllocFailCallback, (void*)cx);
|
||||
|
||||
@ -2989,8 +2953,10 @@ WorkerMain(void* arg)
|
||||
|
||||
JS::SetLargeAllocationFailureCallback(rt, nullptr, nullptr);
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
JS::SetEnqueuePromiseJobCallback(rt, nullptr);
|
||||
sr->jobQueue.reset();
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
DestroyContext(cx, false);
|
||||
|
||||
@ -5573,9 +5539,11 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
|
||||
"createMappedArrayBuffer(filename, [offset, [size]])",
|
||||
" Create an array buffer that mmaps the given file."),
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
JS_FN_HELP("addPromiseReactions", AddPromiseReactions, 3, 0,
|
||||
"addPromiseReactions(promise, onResolve, onReject)",
|
||||
" Calls the JS::AddPromiseReactions JSAPI function with the given arguments."),
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
JS_FN_HELP("getMaxArgs", GetMaxArgs, 0, 0,
|
||||
"getMaxArgs()",
|
||||
@ -5650,15 +5618,12 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
|
||||
"string 'eval:FILENAME' if the code was invoked by 'eval' or something\n"
|
||||
"similar.\n"),
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
JS_FN_HELP("drainJobQueue", DrainJobQueue, 0, 0,
|
||||
"drainJobQueue()",
|
||||
"Take jobs from the shell's job queue in FIFO order and run them until the\n"
|
||||
"queue is empty.\n"),
|
||||
|
||||
JS_FN_HELP("setPromiseRejectionTrackerCallback", SetPromiseRejectionTrackerCallback, 1, 0,
|
||||
"setPromiseRejectionTrackerCallback()",
|
||||
"Sets the callback to be invoked whenever a Promise rejection is unhandled\n"
|
||||
"or a previously-unhandled rejection becomes handled."),
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
JS_FS_HELP_END
|
||||
};
|
||||
@ -6768,7 +6733,9 @@ ProcessArgs(JSContext* cx, OptionParser* op)
|
||||
return sr->exitCode;
|
||||
}
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
DrainJobQueue(cx);
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
if (op->getBoolOption('i'))
|
||||
Process(cx, nullptr, true);
|
||||
@ -7451,8 +7418,10 @@ main(int argc, char** argv, char** envp)
|
||||
if (!cx)
|
||||
return 1;
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
sr->jobQueue.init(cx, JobQueue(SystemAllocPolicy()));
|
||||
JS::SetEnqueuePromiseJobCallback(rt, ShellEnqueuePromiseJobCallback);
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
JS_SetGCParameter(rt, JSGC_MODE, JSGC_MODE_INCREMENTAL);
|
||||
|
||||
@ -7480,8 +7449,10 @@ main(int argc, char** argv, char** envp)
|
||||
|
||||
JS::SetLargeAllocationFailureCallback(rt, nullptr, nullptr);
|
||||
|
||||
#ifdef SPIDERMONKEY_PROMISE
|
||||
JS::SetEnqueuePromiseJobCallback(rt, nullptr);
|
||||
sr->jobQueue.reset();
|
||||
#endif // SPIDERMONKEY_PROMISE
|
||||
|
||||
DestroyContext(cx, true);
|
||||
|
||||
|
@ -1,36 +0,0 @@
|
||||
// |reftest| skip-if(!xulRuntime.shell) -- needs setPromiseRejectionTrackerCallback
|
||||
|
||||
if (!this.Promise) {
|
||||
this.reportCompare && reportCompare(true,true);
|
||||
quit(0);
|
||||
}
|
||||
|
||||
const UNHANDLED = 0;
|
||||
const HANDLED = 1;
|
||||
|
||||
let rejections = new Map();
|
||||
function rejectionTracker(promise, state) {
|
||||
rejections.set(promise, state);
|
||||
}
|
||||
setPromiseRejectionTrackerCallback(rejectionTracker);
|
||||
|
||||
// Unhandled rejections are tracked.
|
||||
let reject;
|
||||
let p = new Promise((res_, rej_) => (reject = rej_));
|
||||
assertEq(rejections.has(p), false);
|
||||
reject('reason');
|
||||
assertEq(rejections.get(p), UNHANDLED);
|
||||
// Later handling updates the tracking.
|
||||
p.then(_=>_, _=>_);
|
||||
assertEq(rejections.get(p), HANDLED);
|
||||
|
||||
rejections.clear();
|
||||
|
||||
// Handled rejections aren't tracked at all.
|
||||
p = new Promise((res_, rej_) => (reject = rej_));
|
||||
assertEq(rejections.has(p), false);
|
||||
p.then(_=>_, _=>_);
|
||||
reject('reason');
|
||||
assertEq(rejections.has(p), false);
|
||||
|
||||
this.reportCompare && reportCompare(true,true);
|
@ -7546,7 +7546,6 @@ DebuggerObject_checkThis(JSContext* cx, const CallArgs& args, const char* fnname
|
||||
|
||||
#define THIS_DEBUGOBJECT_PROMISE(cx, argc, vp, fnname, args, obj) \
|
||||
THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, fnname, args, obj); \
|
||||
obj = CheckedUnwrap(obj); \
|
||||
if (!obj->is<PromiseObject>()) { \
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, \
|
||||
"Debugger", "Promise", obj->getClass()->name); \
|
||||
@ -7556,7 +7555,6 @@ DebuggerObject_checkThis(JSContext* cx, const CallArgs& args, const char* fnname
|
||||
|
||||
#define THIS_DEBUGOBJECT_OWNER_PROMISE(cx, argc, vp, fnname, args, dbg, obj) \
|
||||
THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, fnname, args, dbg, obj); \
|
||||
obj = CheckedUnwrap(obj); \
|
||||
if (!obj->is<PromiseObject>()) { \
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, \
|
||||
"Debugger", "Promise", obj->getClass()->name); \
|
||||
@ -7857,7 +7855,6 @@ DebuggerObject_getIsPromise(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get isPromise", args, refobj);
|
||||
|
||||
refobj = CheckedUnwrap(refobj);
|
||||
args.rval().setBoolean(refobj->is<PromiseObject>());
|
||||
return true;
|
||||
}
|
||||
@ -7963,7 +7960,7 @@ DebuggerObject_getPromiseID(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
THIS_DEBUGOBJECT_PROMISE(cx, argc, vp, "get promiseID", args, refobj);
|
||||
|
||||
args.rval().setNumber(double(promise->getID()));
|
||||
args.rval().setNumber(promise->getID());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,6 @@
|
||||
#include "jswrapper.h"
|
||||
|
||||
#include "asmjs/WasmSignalHandlers.h"
|
||||
#include "builtin/Promise.h"
|
||||
#include "jit/arm/Simulator-arm.h"
|
||||
#include "jit/arm64/vixl/Simulator-vixl.h"
|
||||
#include "jit/JitCompartment.h"
|
||||
@ -154,8 +153,6 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
|
||||
interruptCallback(nullptr),
|
||||
enqueuePromiseJobCallback(nullptr),
|
||||
enqueuePromiseJobCallbackData(nullptr),
|
||||
promiseRejectionTrackerCallback(nullptr),
|
||||
promiseRejectionTrackerCallbackData(nullptr),
|
||||
#ifdef DEBUG
|
||||
exclusiveAccessOwner(nullptr),
|
||||
mainThreadHasExclusiveAccess(false),
|
||||
@ -767,40 +764,13 @@ FreeOp::~FreeOp()
|
||||
}
|
||||
|
||||
bool
|
||||
JSRuntime::enqueuePromiseJob(JSContext* cx, HandleFunction job, HandleObject promise)
|
||||
JSRuntime::enqueuePromiseJob(JSContext* cx, HandleFunction job)
|
||||
{
|
||||
MOZ_ASSERT(cx->runtime()->enqueuePromiseJobCallback,
|
||||
"Must set a callback using JS_SetEnqeueuPromiseJobCallback before using Promises");
|
||||
|
||||
void* data = cx->runtime()->enqueuePromiseJobCallbackData;
|
||||
RootedObject allocationSite(cx);
|
||||
if (promise)
|
||||
allocationSite = JS::GetPromiseAllocationSite(promise);
|
||||
return cx->runtime()->enqueuePromiseJobCallback(cx, job, allocationSite, data);
|
||||
}
|
||||
|
||||
void
|
||||
JSRuntime::addUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise)
|
||||
{
|
||||
MOZ_ASSERT(promise->is<PromiseObject>());
|
||||
if (!cx->runtime()->promiseRejectionTrackerCallback)
|
||||
return;
|
||||
|
||||
void* data = cx->runtime()->promiseRejectionTrackerCallbackData;
|
||||
cx->runtime()->promiseRejectionTrackerCallback(cx, promise,
|
||||
PromiseRejectionHandlingState::Unhandled, data);
|
||||
}
|
||||
|
||||
void
|
||||
JSRuntime::removeUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise)
|
||||
{
|
||||
MOZ_ASSERT(promise->is<PromiseObject>());
|
||||
if (!cx->runtime()->promiseRejectionTrackerCallback)
|
||||
return;
|
||||
|
||||
void* data = cx->runtime()->promiseRejectionTrackerCallbackData;
|
||||
cx->runtime()->promiseRejectionTrackerCallback(cx, promise,
|
||||
PromiseRejectionHandlingState::Handled, data);
|
||||
return cx->runtime()->enqueuePromiseJobCallback(cx, job, data);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -597,8 +597,6 @@ class PerThreadData : public PerThreadDataFriendFields
|
||||
class AutoLockForExclusiveAccess;
|
||||
} // namespace js
|
||||
|
||||
using PromiseList = js::GCVector<JSObject*, 0, js::SystemAllocPolicy>;
|
||||
|
||||
struct JSRuntime : public JS::shadow::Runtime,
|
||||
public js::MallocProvider<JSRuntime>
|
||||
{
|
||||
@ -908,9 +906,6 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
JSEnqueuePromiseJobCallback enqueuePromiseJobCallback;
|
||||
void* enqueuePromiseJobCallbackData;
|
||||
|
||||
JSPromiseRejectionTrackerCallback promiseRejectionTrackerCallback;
|
||||
void* promiseRejectionTrackerCallbackData;
|
||||
|
||||
#ifdef DEBUG
|
||||
void assertCanLock(js::RuntimeLock which);
|
||||
#else
|
||||
@ -1019,9 +1014,7 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
return interpreterStack_;
|
||||
}
|
||||
|
||||
bool enqueuePromiseJob(JSContext* cx, js::HandleFunction job, js::HandleObject promise);
|
||||
void addUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise);
|
||||
void removeUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise);
|
||||
bool enqueuePromiseJob(JSContext* cx, js::HandleFunction job);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Self-hosting support
|
||||
|
@ -1559,84 +1559,16 @@ js::ReportIncompatibleSelfHostedMethod(JSContext* cx, const CallArgs& args)
|
||||
|
||||
// ES6, 25.4.1.6.
|
||||
static bool
|
||||
intrinsic_EnqueuePromiseReactionJob(JSContext* cx, unsigned argc, Value* vp)
|
||||
intrinsic_EnqueuePromiseJob(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
MOZ_ASSERT(args[0].toObject().as<NativeObject>().getDenseInitializedLength() == 4);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
MOZ_ASSERT(args[0].isObject());
|
||||
MOZ_ASSERT(args[0].toObject().is<JSFunction>());
|
||||
|
||||
// When using JS::AddPromiseReactions, no actual promise is created, so we
|
||||
// might not have one here.
|
||||
RootedObject promise(cx);
|
||||
if (args[1].isObject())
|
||||
promise = UncheckedUnwrap(&args[1].toObject());
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT_IF(promise, promise->is<PromiseObject>());
|
||||
RootedNativeObject jobArgs(cx, &args[0].toObject().as<NativeObject>());
|
||||
MOZ_ASSERT((jobArgs->getDenseElement(0).isNumber() &&
|
||||
(jobArgs->getDenseElement(0).toNumber() == PROMISE_HANDLER_IDENTITY ||
|
||||
jobArgs->getDenseElement(0).toNumber() == PROMISE_HANDLER_THROWER)) ||
|
||||
jobArgs->getDenseElement(0).toObject().isCallable());
|
||||
MOZ_ASSERT(jobArgs->getDenseElement(2).toObject().isCallable());
|
||||
MOZ_ASSERT(jobArgs->getDenseElement(3).toObject().isCallable());
|
||||
#endif
|
||||
|
||||
RootedAtom funName(cx, cx->names().empty);
|
||||
RootedFunction job(cx, NewNativeFunction(cx, PromiseReactionJob, 0, funName,
|
||||
gc::AllocKind::FUNCTION_EXTENDED));
|
||||
if (!job)
|
||||
RootedFunction job(cx, &args[0].toObject().as<JSFunction>());
|
||||
if (!cx->runtime()->enqueuePromiseJob(cx, job))
|
||||
return false;
|
||||
|
||||
job->setExtendedSlot(0, args[0]);
|
||||
if (!cx->runtime()->enqueuePromiseJob(cx, job, promise))
|
||||
return false;
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
// ES6, 25.4.1.6.
|
||||
static bool
|
||||
intrinsic_EnqueuePromiseResolveThenableJob(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
MOZ_ASSERT(args[0].toObject().as<NativeObject>().getDenseInitializedLength() == 3);
|
||||
MOZ_ASSERT(UncheckedUnwrap(&args[1].toObject())->is<PromiseObject>());
|
||||
RootedNativeObject jobArgs(cx, &args[0].toObject().as<NativeObject>());
|
||||
MOZ_ASSERT(jobArgs->getDenseElement(0).toObject().isCallable());
|
||||
MOZ_ASSERT(jobArgs->getDenseElement(1).isObject());
|
||||
MOZ_ASSERT(UncheckedUnwrap(&jobArgs->getDenseElement(2).toObject())->is<PromiseObject>());
|
||||
#endif
|
||||
|
||||
RootedAtom funName(cx, cx->names().empty);
|
||||
RootedFunction job(cx, NewNativeFunction(cx, PromiseResolveThenableJob, 0, funName,
|
||||
gc::AllocKind::FUNCTION_EXTENDED));
|
||||
if (!job)
|
||||
return false;
|
||||
|
||||
job->setExtendedSlot(0, args[0]);
|
||||
RootedObject promise(cx, CheckedUnwrap(&args[1].toObject()));
|
||||
if (!cx->runtime()->enqueuePromiseJob(cx, job, promise))
|
||||
return false;
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
// ES2016, February 12 draft, 25.4.1.9.
|
||||
static bool
|
||||
intrinsic_HostPromiseRejectionTracker(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
MOZ_ASSERT(args[0].toObject().is<PromiseObject>());
|
||||
|
||||
Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>());
|
||||
mozilla::DebugOnly<bool> isHandled = args[1].toBoolean();
|
||||
MOZ_ASSERT(isHandled, "HostPromiseRejectionTracker intrinsic currently only marks as handled");
|
||||
cx->runtime()->removeUnhandledRejectedPromise(cx, promise);
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
@ -2002,12 +1934,36 @@ intrinsic_onPromiseSettled(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>());
|
||||
promise->onSettled(cx);
|
||||
RootedObject promise(cx, &args[0].toObject());
|
||||
JS::dbg::onPromiseSettled(cx, promise);
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intrinsic used to tell the debugger about settled promises.
|
||||
*
|
||||
* This is invoked both when resolving and rejecting promises, after the
|
||||
* resulting state has been set on the promise, and it's up to the debugger
|
||||
* to act on this signal in whichever way it wants.
|
||||
*/
|
||||
static bool
|
||||
intrinsic_captureCurrentStack(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() < 2);
|
||||
unsigned maxFrameCount = 0;
|
||||
if (args.length() == 1)
|
||||
maxFrameCount = args[0].toInt32();
|
||||
|
||||
RootedObject stack(cx);
|
||||
if (!JS::CaptureCurrentStack(cx, &stack, maxFrameCount))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*stack);
|
||||
return true;
|
||||
}
|
||||
|
||||
// The self-hosting global isn't initialized with the normal set of builtins.
|
||||
// Instead, individual C++-implemented functions that're required by
|
||||
// self-hosted code are defined as global functions. Accessing these
|
||||
@ -2243,9 +2199,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||
|
||||
JS_FN("IsPromise", intrinsic_IsInstanceOfBuiltin<PromiseObject>, 1,0),
|
||||
JS_FN("IsWrappedPromise", intrinsic_IsWrappedPromiseObject, 1, 0),
|
||||
JS_FN("_EnqueuePromiseReactionJob", intrinsic_EnqueuePromiseReactionJob, 2, 0),
|
||||
JS_FN("_EnqueuePromiseResolveThenableJob", intrinsic_EnqueuePromiseResolveThenableJob, 2, 0),
|
||||
JS_FN("HostPromiseRejectionTracker", intrinsic_HostPromiseRejectionTracker,2, 0),
|
||||
JS_FN("_EnqueuePromiseJob", intrinsic_EnqueuePromiseJob, 1, 0),
|
||||
JS_FN("_GetOriginalPromiseConstructor", intrinsic_OriginalPromiseConstructor, 0, 0),
|
||||
JS_FN("RejectUnwrappedPromise", intrinsic_RejectUnwrappedPromise, 2, 0),
|
||||
JS_FN("CallPromiseMethodIfWrapped",
|
||||
@ -2338,6 +2292,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||
JS_FN("ModuleNamespaceExports", intrinsic_ModuleNamespaceExports, 1, 0),
|
||||
|
||||
JS_FN("_dbg_onPromiseSettled", intrinsic_onPromiseSettled, 1, 0),
|
||||
JS_FN("_dbg_captureCurrentStack", intrinsic_captureCurrentStack, 1, 0),
|
||||
|
||||
JS_FS_END
|
||||
};
|
||||
|
@ -918,8 +918,8 @@ CycleCollectedJSRuntime::ContextCallback(JSContext* aContext,
|
||||
class PromiseJobRunnable final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
PromiseJobRunnable(JS::HandleObject aCallback, JS::HandleObject aAllocationSite)
|
||||
: mCallback(new PromiseJobCallback(aCallback, aAllocationSite, nullptr))
|
||||
PromiseJobRunnable(JSContext* aCx, JS::HandleObject aCallback)
|
||||
: mCallback(new PromiseJobCallback(aCx, aCallback, nullptr))
|
||||
{
|
||||
}
|
||||
|
||||
@ -946,14 +946,13 @@ private:
|
||||
bool
|
||||
CycleCollectedJSRuntime::EnqueuePromiseJobCallback(JSContext* aCx,
|
||||
JS::HandleObject aJob,
|
||||
JS::HandleObject aAllocationSite,
|
||||
void* aData)
|
||||
{
|
||||
CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
|
||||
MOZ_ASSERT(JS_GetRuntime(aCx) == self->Runtime());
|
||||
MOZ_ASSERT(Get() == self);
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable = new PromiseJobRunnable(aJob, aAllocationSite);
|
||||
nsCOMPtr<nsIRunnable> runnable = new PromiseJobRunnable(aCx, aJob);
|
||||
self->DispatchToMicroTask(runnable);
|
||||
return true;
|
||||
}
|
||||
|
@ -219,7 +219,6 @@ private:
|
||||
void* aData);
|
||||
static bool EnqueuePromiseJobCallback(JSContext* aCx,
|
||||
JS::HandleObject aJob,
|
||||
JS::HandleObject aAllocationSite,
|
||||
void* aData);
|
||||
|
||||
virtual void TraceNativeBlackRoots(JSTracer* aTracer) { };
|
||||
|
Loading…
Reference in New Issue
Block a user