Bug 1289318 - Part 8: Combine Promise state and rejection handling info into a single flags field. r=efaust

The rejection handling state will be required even without devtools being open once projection rejection tracking events[1] are implemented, so it should always be tracked on the Promise itself. The other debugging state will be moved into a debug-only object referenced via a slot on Promise.

MozReview-Commit-ID: LM10qruLDxz
This commit is contained in:
Till Schneidereit 2016-08-06 18:26:22 +02:00
Родитель e2d055ae6b
Коммит 36380475eb
5 изменённых файлов: 45 добавлений и 33 удалений

Просмотреть файл

@ -160,7 +160,11 @@ ResolvePromise(JSContext* cx, Handle<PromiseObject*> promise, HandleValue valueO
promise->setFixedSlot(PROMISE_REACTIONS_OR_RESULT_SLOT, valueOrReason);
// Step 6.
promise->setFixedSlot(PROMISE_STATE_SLOT, Int32Value(int32_t(state)));
int32_t flags = promise->getFixedSlot(PROMISE_FLAGS_SLOT).toInt32();
flags |= PROMISE_FLAG_RESOLVED;
if (state == JS::PromiseState::Fulfilled)
flags |= PROMISE_FLAG_FULFILLED;
promise->setFixedSlot(PROMISE_FLAGS_SLOT, Int32Value(flags));
// Also null out the resolve/reject functions so they can be GC'd.
promise->setFixedSlot(PROMISE_RESOLVE_FUNCTION_SLOT, UndefinedValue());
@ -435,14 +439,13 @@ PromiseObject::create(JSContext* cx, HandleObject executor, HandleObject proto /
return nullptr;
// Step 4.
promise->setFixedSlot(PROMISE_STATE_SLOT, Int32Value(PROMISE_STATE_PENDING));
promise->setFixedSlot(PROMISE_FLAGS_SLOT, Int32Value(0));
// Steps 5-6.
// Omitted, we allocate our single list of reaction records lazily.
// Step 7.
promise->setFixedSlot(PROMISE_IS_HANDLED_SLOT,
Int32Value(PROMISE_IS_HANDLED_STATE_UNHANDLED));
// Implicit, the handled flag is unset by default.
// Store an allocation stack so we can later figure out what the
// control flow was for some unexpected results. Frightfully expensive,
@ -685,7 +688,7 @@ PromiseConstructor(JSContext* cx, unsigned argc, Value* vp)
bool
PromiseObject::resolve(JSContext* cx, HandleValue resolutionValue)
{
if (this->getFixedSlot(PROMISE_STATE_SLOT).toInt32() != unsigned(JS::PromiseState::Pending))
if (state() != JS::PromiseState::Pending)
return true;
RootedValue funVal(cx, this->getReservedSlot(PROMISE_RESOLVE_FUNCTION_SLOT));
@ -703,7 +706,7 @@ PromiseObject::resolve(JSContext* cx, HandleValue resolutionValue)
bool
PromiseObject::reject(JSContext* cx, HandleValue rejectionValue)
{
if (this->getFixedSlot(PROMISE_STATE_SLOT).toInt32() != unsigned(JS::PromiseState::Pending))
if (state() != JS::PromiseState::Pending)
return true;
RootedValue resolveVal(cx, this->getReservedSlot(PROMISE_RESOLVE_FUNCTION_SLOT));
@ -733,12 +736,8 @@ PromiseObject::onSettled(JSContext* cx)
promise->setFixedSlot(PROMISE_RESOLUTION_SITE_SLOT, ObjectOrNullValue(stack));
promise->setFixedSlot(PROMISE_RESOLUTION_TIME_SLOT, DoubleValue(MillisecondsSinceStartup()));
if (promise->state() == JS::PromiseState::Rejected &&
promise->getFixedSlot(PROMISE_IS_HANDLED_SLOT).toInt32() !=
PROMISE_IS_HANDLED_STATE_HANDLED)
{
if (promise->state() == JS::PromiseState::Rejected && promise->isUnhandled())
cx->runtime()->addUnhandledRejectedPromise(cx, promise);
}
JS::dbg::onPromiseSettled(cx, promise);
}

Просмотреть файл

@ -17,16 +17,21 @@ class AutoSetNewObjectMetadata;
class PromiseObject : public NativeObject
{
public:
static const unsigned RESERVED_SLOTS = 12;
static const unsigned RESERVED_SLOTS = 8;
static const Class class_;
static const Class protoClass_;
static PromiseObject* create(JSContext* cx, HandleObject executor,
HandleObject proto = nullptr);
JS::PromiseState state() {
int32_t state = getFixedSlot(PROMISE_STATE_SLOT).toInt32();
MOZ_ASSERT(state >= 0 && state <= int32_t(JS::PromiseState::Rejected));
return JS::PromiseState(state);
int32_t flags = getFixedSlot(PROMISE_FLAGS_SLOT).toInt32();
if (!(flags & PROMISE_FLAG_RESOLVED)) {
MOZ_ASSERT(!(flags & PROMISE_FLAG_FULFILLED));
return JS::PromiseState::Pending;
}
if (flags & PROMISE_FLAG_FULFILLED)
return JS::PromiseState::Fulfilled;
return JS::PromiseState::Rejected;
}
Value value() {
MOZ_ASSERT(state() == JS::PromiseState::Fulfilled);
@ -57,13 +62,14 @@ class PromiseObject : public NativeObject
}
MOZ_MUST_USE bool dependentPromises(JSContext* cx, MutableHandle<GCVector<Value>> values);
uint64_t getID();
bool markedAsUncaught() {
return getFixedSlot(PROMISE_IS_HANDLED_SLOT).toInt32() != PROMISE_IS_HANDLED_STATE_HANDLED;
bool isUnhandled() {
MOZ_ASSERT(state() == JS::PromiseState::Rejected);
return !(getFixedSlot(PROMISE_FLAGS_SLOT).toInt32() & PROMISE_FLAG_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));
MOZ_ASSERT(isUnhandled());
int32_t flags = getFixedSlot(PROMISE_FLAGS_SLOT).toInt32();
setFixedSlot(PROMISE_FLAGS_SLOT, Int32Value(flags | PROMISE_FLAG_REPORTED));
}
};

Просмотреть файл

@ -736,6 +736,7 @@ function PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability)
// Step 7.
let state = GetPromiseState(promise);
let flags = UnsafeGetInt32FromReservedSlot(promise, PROMISE_FLAGS_SLOT);
if (state === PROMISE_STATE_PENDING) {
// Steps 7.a,b.
// We only have a single list for fulfill and reject reactions.
@ -764,18 +765,15 @@ function PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability)
let reason = UnsafeGetReservedSlot(promise, PROMISE_REACTIONS_OR_RESULT_SLOT);
// Step 9.c.
if (UnsafeGetInt32FromReservedSlot(promise, PROMISE_IS_HANDLED_SLOT) !==
PROMISE_IS_HANDLED_STATE_HANDLED)
{
if (!(flags & PROMISE_FLAG_HANDLED))
HostPromiseRejectionTracker(promise, PROMISE_REJECTION_TRACKER_OPERATION_HANDLE);
}
// Step 9.d.
EnqueuePromiseReactionJob(reaction, PROMISE_JOB_TYPE_REJECT, reason);
}
// Step 10.
UnsafeSetReservedSlot(promise, PROMISE_IS_HANDLED_SLOT, PROMISE_IS_HANDLED_STATE_HANDLED);
UnsafeSetReservedSlot(promise, PROMISE_FLAGS_SLOT, flags | PROMISE_FLAG_HANDLED);
// Step 11.
return resultCapability.promise;
@ -791,5 +789,12 @@ function IsPromiseCapability(capability) {
}
function GetPromiseState(promise) {
return UnsafeGetInt32FromReservedSlot(promise, PROMISE_STATE_SLOT);
let flags = UnsafeGetInt32FromReservedSlot(promise, PROMISE_FLAGS_SLOT);
if (!(flags & PROMISE_FLAG_RESOLVED)) {
assert(!(flags & PROMISE_STATE_FULFILLED), "Fulfilled promises are resolved, too");
return PROMISE_STATE_PENDING;
}
if (flags & PROMISE_FLAG_FULFILLED)
return PROMISE_STATE_FULFILLED;
return PROMISE_STATE_REJECTED;
}

Просмотреть файл

@ -57,7 +57,7 @@
#define ITEM_KIND_VALUE 1
#define ITEM_KIND_KEY_AND_VALUE 2
#define PROMISE_STATE_SLOT 0
#define PROMISE_FLAGS_SLOT 0
#define PROMISE_REACTIONS_OR_RESULT_SLOT 1
#define PROMISE_RESOLVE_FUNCTION_SLOT 2
#define PROMISE_ALLOCATION_SITE_SLOT 3
@ -65,15 +65,15 @@
#define PROMISE_ALLOCATION_TIME_SLOT 5
#define PROMISE_RESOLUTION_TIME_SLOT 6
#define PROMISE_ID_SLOT 7
#define PROMISE_IS_HANDLED_SLOT 8
#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_FLAG_RESOLVED 0x1
#define PROMISE_FLAG_FULFILLED 0x2
#define PROMISE_FLAG_HANDLED 0x4
#define PROMISE_FLAG_REPORTED 0x8
#define PROMISE_HANDLER_IDENTITY 0
#define PROMISE_HANDLER_THROWER 1

Просмотреть файл

@ -1444,8 +1444,10 @@ SettlePromiseNow(JSContext* cx, unsigned argc, Value* vp)
}
RootedNativeObject promise(cx, &args[0].toObject().as<NativeObject>());
promise->setReservedSlot(PROMISE_STATE_SLOT, Int32Value(PROMISE_STATE_FULFILLED));
promise->setReservedSlot(PROMISE_REACTIONS_OR_RESULT_SLOT, UndefinedValue());
int32_t flags = promise->getFixedSlot(PROMISE_FLAGS_SLOT).toInt32();
promise->setFixedSlot(PROMISE_FLAGS_SLOT,
Int32Value(flags | PROMISE_FLAG_RESOLVED | PROMISE_FLAG_FULFILLED));
promise->setFixedSlot(PROMISE_REACTIONS_OR_RESULT_SLOT, UndefinedValue());
JS::dbg::onPromiseSettled(cx, promise);
return true;