Bug 1289318 - Part 4: Only allocate the Promise reactions array once the first reaction record is added. r?efaust
Saves 96 bytes on reaction-less promises. It also saves 32 bytes on promises that have up to two reactions: empty arrays are initialized with an allocated length of 8, whereas providing an initial element initializes to 2.
MozReview-Commit-ID: 3PtT7LDwL3k
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -110,21 +110,17 @@ PromiseObject::create(JSContext* cx, Han
promise = NewObjectWithClassProto<PromiseObject>(cx, usedProto);
if (!promise)
return nullptr;
// Step 4.
promise->setFixedSlot(PROMISE_STATE_SLOT, Int32Value(PROMISE_STATE_PENDING));
// Steps 5-6.
- // We only have a single list of reaction records.
- RootedArrayObject reactions(cx, NewDenseEmptyArray(cx));
- if (!reactions)
- return nullptr;
- promise->setFixedSlot(PROMISE_REACTIONS_SLOT, ObjectValue(*reactions));
+ // Omitted, we allocate our single list of reaction records lazily.
// 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.
--- a/js/src/builtin/Promise.js
+++ b/js/src/builtin/Promise.js
@@ -142,17 +142,17 @@ function ResolvePromise(promise, valueOr
assert(state >= PROMISE_STATE_FULFILLED && state <= PROMISE_STATE_REJECTED,
`Invalid Promise resolution state <${state}>`);
// Step 2.
// We only have one list of reactions for both resolution types. So
// instead of getting the right list of reactions, we determine the
// resolution type to retrieve the right information from the
// reaction records.
- var reactions = UnsafeGetObjectFromReservedSlot(promise, PROMISE_REACTIONS_SLOT);
+ var reactions = UnsafeGetReservedSlot(promise, PROMISE_REACTIONS_SLOT);
let jobType = state === PROMISE_STATE_FULFILLED
? PROMISE_JOB_TYPE_FULFILL
: PROMISE_JOB_TYPE_REJECT;
// Step 3.
UnsafeSetReservedSlot(promise, PROMISE_RESULT_SLOT, valueOrReason);
// Steps 4-5.
@@ -166,17 +166,18 @@ function ResolvePromise(promise, valueOr
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.
_dbg_onPromiseSettled(promise);
// Step 7 of FulfillPromise.
// Step 8 of RejectPromise.
- return TriggerPromiseReactions(reactions, jobType, valueOrReason);
+ if (reactions)
+ TriggerPromiseReactions(reactions, jobType, valueOrReason);
}
// Used to verify that an object is a PromiseCapability record.
var PromiseCapabilityRecordProto = {__proto__: null};
// ES6, 25.4.1.5.
// Creates PromiseCapability records, see 25.4.1.1.
function NewPromiseCapability(C) {
@@ -642,20 +643,21 @@ function BlockOnPromise(promise, blocked
function AddDependentPromise(dependentPromise) {
assert(IsPromise(this), "AddDependentPromise expects an unwrapped Promise as the receiver");
let reaction = new PromiseReactionRecord(dependentPromise, NullFunction, NullFunction,
NullFunction, NullFunction, null);
let reactions = UnsafeGetReservedSlot(this, PROMISE_REACTIONS_SLOT);
- // The reactions slot might've been reset because the Promise was resolved.
+ // The reactions list might not have been allocated yet or been reset
+ // because the Promise was resolved.
if (!reactions) {
- assert(GetPromiseState(this) !== PROMISE_STATE_PENDING,
- "Pending promises must have reactions lists.");
+ if (GetPromiseState(this) === PROMISE_STATE_PENDING)
+ UnsafeSetReservedSlot(promise, PROMISE_REACTIONS_SLOT, [reaction]);
return;
}
_DefineDataProperty(reactions, reactions.length, reaction);
}
// ES6, 25.4.4.4.
function Promise_static_reject(r) {
// Step 1.
@@ -895,18 +897,21 @@ function PerformPromiseThen(promise, onF
onRejected,
incumbentGlobal);
// Step 7.
let state = GetPromiseState(promise);
if (state === PROMISE_STATE_PENDING) {
// Steps 7.a,b.
// We only have a single list for fulfill and reject reactions.
- let reactions = UnsafeGetObjectFromReservedSlot(promise, PROMISE_REACTIONS_SLOT);
- _DefineDataProperty(reactions, reactions.length, reaction);
+ let reactions = UnsafeGetReservedSlot(promise, PROMISE_REACTIONS_SLOT);
+ if (!reactions)
+ UnsafeSetReservedSlot(promise, PROMISE_REACTIONS_SLOT, [reaction]);
+ else
+ _DefineDataProperty(reactions, reactions.length, reaction);
}
// Step 8.
else if (state === PROMISE_STATE_FULFILLED) {
// Step 8.a.
let value = UnsafeGetReservedSlot(promise, PROMISE_RESULT_SLOT);
// Step 8.b.