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:
Wes Kocher 2016-04-04 15:41:17 -07:00
parent e6d52431b6
commit 46f04c50ca
18 changed files with 181 additions and 648 deletions

View File

@ -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,

View File

@ -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.

View File

@ -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:
*

View File

@ -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*

View File

@ -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 */

View File

@ -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;
}

View File

@ -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 */

View File

@ -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");

View File

@ -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();

View File

@ -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);
/**

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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
};

View File

@ -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;
}

View File

@ -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) { };