--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -58,41 +58,76 @@ enum ReactionJobSlots {
ReactionJobSlot_ReactionRecord = 0,
};
enum ThenableJobSlots {
ThenableJobSlot_Handler = 0,
ThenableJobSlot_JobData,
};
-enum ThenableJobDataSlots {
- ThenableJobDataSlot_Promise = 0,
- ThenableJobDataSlot_Thenable,
- ThenableJobDataSlotsCount,
+enum ThenableJobDataIndices {
+ ThenableJobDataIndex_Promise = 0,
+ ThenableJobDataIndex_Thenable,
+ ThenableJobDataLength,
};
enum PromiseAllDataHolderSlots {
PromiseAllDataHolderSlot_Promise = 0,
PromiseAllDataHolderSlot_RemainingElements,
PromiseAllDataHolderSlot_ValuesArray,
PromiseAllDataHolderSlot_ResolveFunction,
PromiseAllDataHolderSlots,
};
class PromiseAllDataHolder : public NativeObject
{
public:
static const Class class_;
+ JSObject* promiseObj() { return &getFixedSlot(PromiseAllDataHolderSlot_Promise).toObject(); }
+ JSObject* resolveObj() {
+ return getFixedSlot(PromiseAllDataHolderSlot_ResolveFunction).toObjectOrNull();
+ }
+ Value valuesArray() { return getFixedSlot(PromiseAllDataHolderSlot_ValuesArray); }
+ int32_t remainingCount() {
+ return getFixedSlot(PromiseAllDataHolderSlot_RemainingElements).toInt32();
+ }
+ int32_t increaseRemainingCount() {
+ int32_t remainingCount = getFixedSlot(PromiseAllDataHolderSlot_RemainingElements).toInt32();
+ remainingCount++;
+ setFixedSlot(PromiseAllDataHolderSlot_RemainingElements, Int32Value(remainingCount));
+ return remainingCount;
+ }
+ int32_t decreaseRemainingCount() {
+ int32_t remainingCount = getFixedSlot(PromiseAllDataHolderSlot_RemainingElements).toInt32();
+ remainingCount--;
+ setFixedSlot(PromiseAllDataHolderSlot_RemainingElements, Int32Value(remainingCount));
+ return remainingCount;
+ }
};
const Class PromiseAllDataHolder::class_ = {
"PromiseAllDataHolder",
JSCLASS_HAS_RESERVED_SLOTS(PromiseAllDataHolderSlots)
};
+static PromiseAllDataHolder*
+NewPromiseAllDataHolder(JSContext* cx, HandleObject resultPromise, HandleObject valuesArray,
+ HandleObject resolve)
+{
+ Rooted<PromiseAllDataHolder*> dataHolder(cx, NewObjectWithClassProto<PromiseAllDataHolder>(cx));
+ if (!dataHolder)
+ return nullptr;
+
+ dataHolder->setFixedSlot(PromiseAllDataHolderSlot_Promise, ObjectValue(*resultPromise));
+ dataHolder->setFixedSlot(PromiseAllDataHolderSlot_RemainingElements, Int32Value(1));
+ dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ValuesArray, ObjectValue(*valuesArray));
+ dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ResolveFunction, ObjectOrNullValue(resolve));
+ return dataHolder;
+}
+
static MOZ_MUST_USE bool RunResolutionFunction(JSContext *cx, HandleObject resolutionFun,
HandleValue result, ResolutionMode mode,
HandleObject promiseObj);
// ES2016, 25.4.1.1.1, Steps 1.a-b.
// Extracting all of this internal spec algorithm into a helper function would
// be tedious, so the check in step 1 and the entirety of step 2 aren't
// included.
@@ -103,80 +138,88 @@ AbruptRejectPromise(JSContext *cx, CallA
RootedValue reason(cx);
if (!GetAndClearException(cx, &reason))
return false;
if (!RunResolutionFunction(cx, reject, reason, RejectMode, promiseObj))
return false;
// Step 1.b.
- args.rval().set(ObjectValue(*promiseObj));
+ args.rval().setObject(*promiseObj);
return true;
}
-// ES2016, 25.4.1.2.
-class PromiseReactionRecord : public NativeObject
-{
- public:
- static const Class class_;
-};
-
enum ReactionRecordSlots {
ReactionRecordSlot_Promise = 0,
ReactionRecordSlot_OnFulfilled,
ReactionRecordSlot_OnRejected,
ReactionRecordSlot_Resolve,
ReactionRecordSlot_Reject,
ReactionRecordSlot_IncumbentGlobalObject,
ReactionRecordSlot_Flags,
ReactionRecordSlot_HandlerArg,
ReactionRecordSlots,
};
#define REACTION_FLAG_RESOLVED 0x1
#define REACTION_FLAG_FULFILLED 0x2
#define REACTION_FLAG_IGNORE_DEFAULT_RESOLUTION 0x4
-static int32_t
-ReactionFlags(PromiseReactionRecord* reaction)
-{
- return reaction->getFixedSlot(ReactionRecordSlot_Flags).toInt32();
-}
-
-static void
-SetReactionTargetState(PromiseReactionRecord* reaction, JS::PromiseState state)
+// ES2016, 25.4.1.2.
+class PromiseReactionRecord : public NativeObject
{
- int32_t flags = ReactionFlags(reaction);
- MOZ_ASSERT(!(flags & REACTION_FLAG_RESOLVED));
- MOZ_ASSERT(state != JS::PromiseState::Pending, "Can't revert a reaction to pending.");
- flags |= REACTION_FLAG_RESOLVED;
- if (state == JS::PromiseState::Fulfilled)
- flags |= REACTION_FLAG_FULFILLED;
- reaction->setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
-}
-
-static JS::PromiseState
-ReactionTargetState(PromiseReactionRecord* reaction)
-{
- int32_t flags = ReactionFlags(reaction);
- if (!(flags & REACTION_FLAG_RESOLVED))
- return JS::PromiseState::Pending;
- return flags & REACTION_FLAG_FULFILLED ?
- JS::PromiseState::Fulfilled :
- JS::PromiseState::Rejected;
-}
+ public:
+ static const Class class_;
+
+ JSObject* promise() { return getFixedSlot(ReactionRecordSlot_Promise).toObjectOrNull(); }
+ int32_t flags() { return getFixedSlot(ReactionRecordSlot_Flags).toInt32(); }
+ JS::PromiseState targetState() {
+ int32_t flags = this->flags();
+ if (!(flags & REACTION_FLAG_RESOLVED))
+ return JS::PromiseState::Pending;
+ return flags & REACTION_FLAG_FULFILLED
+ ? JS::PromiseState::Fulfilled
+ : JS::PromiseState::Rejected;
+ }
+ void setTargetState(JS::PromiseState state) {
+ int32_t flags = this->flags();
+ MOZ_ASSERT(!(flags & REACTION_FLAG_RESOLVED));
+ MOZ_ASSERT(state != JS::PromiseState::Pending, "Can't revert a reaction to pending.");
+ flags |= REACTION_FLAG_RESOLVED;
+ if (state == JS::PromiseState::Fulfilled)
+ flags |= REACTION_FLAG_FULFILLED;
+ setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
+ }
+ Value handler() {
+ MOZ_ASSERT(targetState() != JS::PromiseState::Pending);
+ uint32_t slot = targetState() == JS::PromiseState::Fulfilled
+ ? ReactionRecordSlot_OnFulfilled
+ : ReactionRecordSlot_OnRejected;
+ return getFixedSlot(slot);
+ }
+ Value handlerArg() {
+ MOZ_ASSERT(targetState() != JS::PromiseState::Pending);
+ return getFixedSlot(ReactionRecordSlot_HandlerArg);
+ }
+ void setHandlerArg(Value arg) {
+ MOZ_ASSERT(targetState() == JS::PromiseState::Pending);
+ setFixedSlot(ReactionRecordSlot_HandlerArg, arg);
+ }
+ JSObject* incumbentGlobalObject() {
+ return getFixedSlot(ReactionRecordSlot_IncumbentGlobalObject).toObjectOrNull();
+ }
+};
const Class PromiseReactionRecord::class_ = {
"PromiseReactionRecord",
- JSCLASS_HAS_RESERVED_SLOTS(ReactionRecordSlots),
- JS_NULL_CLASS_OPS
+ JSCLASS_HAS_RESERVED_SLOTS(ReactionRecordSlots)
};
static void
-SetPromiseFlags(PromiseObject& promise, int32_t flag)
+AddPromiseFlags(PromiseObject& promise, int32_t flag)
{
int32_t flags = promise.getFixedSlot(PromiseSlot_Flags).toInt32();
promise.setFixedSlot(PromiseSlot_Flags, Int32Value(flags | flag));
}
static bool
PromiseHasAnyFlag(PromiseObject& promise, int32_t flag)
{
@@ -375,19 +418,19 @@ ResolvePromiseFunction(JSContext* cx, un
return status;
}
static bool PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp);
/**
* Tells the embedding to enqueue a Promise reaction job, based on
* three parameters:
- * reaction - The reaction record.
- * handlerArg - The first and only argument to pass to the handler invoked by
- * the job. This is stored on the reaction record.
+ * reactionObj - The reaction record.
+ * handlerArg_ - The first and only argument to pass to the handler invoked by
+ * the job. This will be stored on the reaction record.
* targetState - The PromiseState this reaction job targets. This decides
* whether the onFulfilled or onRejected handler is called.
*/
MOZ_MUST_USE static bool
EnqueuePromiseReactionJob(JSContext* cx, HandleObject reactionObj,
HandleValue handlerArg_, JS::PromiseState targetState)
{
// The reaction might have been stored on a Promise from another
@@ -405,28 +448,25 @@ EnqueuePromiseReactionJob(JSContext* cx,
reaction = &unwrappedReactionObj->as<PromiseReactionRecord>();
if (!cx->compartment()->wrap(cx, &handlerArg))
return false;
} else {
reaction = &reactionObj->as<PromiseReactionRecord>();
}
// Must not enqueue a reaction job more than once.
- MOZ_ASSERT(ReactionTargetState(reaction) == JS::PromiseState::Pending);
-
- SetReactionTargetState(reaction, targetState);
+ MOZ_ASSERT(reaction->targetState() == JS::PromiseState::Pending);
+
assertSameCompartment(cx, handlerArg);
- reaction->setFixedSlot(ReactionRecordSlot_HandlerArg, handlerArg);
+ reaction->setHandlerArg(handlerArg);
RootedValue reactionVal(cx, ObjectValue(*reaction));
- uint32_t handlerSlot = targetState == JS::PromiseState::Fulfilled
- ? ReactionRecordSlot_OnFulfilled
- : ReactionRecordSlot_OnRejected;
- RootedValue handler(cx, reaction->getFixedSlot(handlerSlot));
+ reaction->setTargetState(targetState);
+ RootedValue handler(cx, reaction->handler());
// If we have a handler callback, we enter that handler's compartment so
// that the promise reaction job function is created in that compartment.
// That guarantees that the embedding ends up with the right entry global.
// This is relevant for some html APIs like fetch that derive information
// from said global.
mozilla::Maybe<AutoCompartment> ac2;
if (handler.isObject()) {
@@ -462,30 +502,28 @@ EnqueuePromiseReactionJob(JSContext* cx,
// Promise[@@species] (or invokes Promise#then on a Promise subclass
// instance with a non-default @@species value on the constructor) with a
// function that returns objects that're not Promise (subclass) instances.
// In that case, we just pretend we didn't have an object in the first
// place.
// If after all this we do have an object, wrap it in case we entered the
// handler's compartment above, because we should pass objects from a
// single compartment to the enqueuePromiseJob callback.
- RootedObject promise(cx, reaction->getFixedSlot(ReactionRecordSlot_Promise).toObjectOrNull());
+ RootedObject promise(cx, reaction->promise());
if (promise && promise->is<PromiseObject>()) {
if (!cx->compartment()->wrap(cx, &promise))
return false;
}
// Using objectFromIncumbentGlobal, we can derive the incumbent global by
// unwrapping and then getting the global. This is very convoluted, but
// much better than having to store the original global as a private value
// because we couldn't wrap it to store it as a normal JS value.
RootedObject global(cx);
- JSObject* obj = reaction->getFixedSlot(ReactionRecordSlot_IncumbentGlobalObject)
- .toObjectOrNull();
- RootedObject objectFromIncumbentGlobal(cx, obj);
+ RootedObject objectFromIncumbentGlobal(cx, reaction->incumbentGlobalObject());
if (objectFromIncumbentGlobal) {
objectFromIncumbentGlobal = CheckedUnwrap(objectFromIncumbentGlobal);
MOZ_ASSERT(objectFromIncumbentGlobal);
global = &objectFromIncumbentGlobal->global();
}
// Note: the global we pass here might be from a different compartment
// than job and promise. While it's somewhat unusual to pass objects
@@ -509,17 +547,17 @@ ResolvePromise(JSContext* cx, Handle<Pro
// 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.
RootedValue reactionsVal(cx, promise->getFixedSlot(PromiseSlot_ReactionsOrResult));
- // Step 3-5.
+ // Steps 3-5.
// The same slot is used for the reactions list and the result, so setting
// the result also removes the reactions list.
promise->setFixedSlot(PromiseSlot_ReactionsOrResult, valueOrReason);
// Step 6.
int32_t flags = promise->getFixedSlot(PromiseSlot_Flags).toInt32();
flags |= PROMISE_FLAG_RESOLVED;
if (state == JS::PromiseState::Fulfilled)
@@ -569,16 +607,21 @@ FulfillMaybeWrappedPromise(JSContext *cx
static bool GetCapabilitiesExecutor(JSContext* cx, unsigned argc, Value* vp);
static bool PromiseConstructor(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE PromiseObject* CreatePromiseObjectInternal(JSContext* cx,
HandleObject proto = nullptr,
bool protoIsWrapped = false,
bool informDebugger = true);
+enum GetCapabilitiesExecutorSlots {
+ GetCapabilitiesExecutorSlots_Resolve,
+ GetCapabilitiesExecutorSlots_Reject
+};
+
// ES2016, 25.4.1.5.
static MOZ_MUST_USE bool
NewPromiseCapability(JSContext* cx, HandleObject C, MutableHandleObject promise,
MutableHandleObject resolve, MutableHandleObject reject,
bool canOmitResolutionFunctions)
{
RootedValue cVal(cx, ObjectValue(*C));
@@ -596,17 +639,17 @@ NewPromiseCapability(JSContext* cx, Hand
// This can't be used in Promise.all and Promise.race because we have to
// pass the reject (and resolve, in the race case) function to thenables
// in the list passed to all/race, which (potentially) means exposing them
// to content.
if (canOmitResolutionFunctions && IsNativeFunction(cVal, PromiseConstructor)) {
promise.set(CreatePromiseObjectInternal(cx));
if (!promise)
return false;
- SetPromiseFlags(promise->as<PromiseObject>(), PROMISE_FLAG_DEFAULT_RESOLVE_FUNCTION |
+ AddPromiseFlags(promise->as<PromiseObject>(), PROMISE_FLAG_DEFAULT_RESOLVE_FUNCTION |
PROMISE_FLAG_DEFAULT_REJECT_FUNCTION);
return true;
}
// Step 3 (omitted).
// Step 4.
@@ -620,25 +663,25 @@ NewPromiseCapability(JSContext* cx, Hand
// Step 6.
FixedConstructArgs<1> cargs(cx);
cargs[0].setObject(*executor);
if (!Construct(cx, cVal, cargs, cVal, promise))
return false;
// Step 7.
- RootedValue resolveVal(cx, executor->getExtendedSlot(0));
+ RootedValue resolveVal(cx, executor->getExtendedSlot(GetCapabilitiesExecutorSlots_Resolve));
if (!IsCallable(resolveVal)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE);
return false;
}
// Step 8.
- RootedValue rejectVal(cx, executor->getExtendedSlot(1));
+ RootedValue rejectVal(cx, executor->getExtendedSlot(GetCapabilitiesExecutorSlots_Reject));
if (!IsCallable(rejectVal)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE);
return false;
}
// Step 9 (well, the equivalent for all of promiseCapabilities' fields.)
resolve.set(&resolveVal.toObject());
@@ -653,27 +696,29 @@ static bool
GetCapabilitiesExecutor(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedFunction F(cx, &args.callee().as<JSFunction>());
// Steps 1-2 (implicit).
// Steps 3-4.
- if (!F->getExtendedSlot(0).isUndefined() || !F->getExtendedSlot(1).isUndefined()) {
+ if (!F->getExtendedSlot(GetCapabilitiesExecutorSlots_Resolve).isUndefined() ||
+ !F->getExtendedSlot(GetCapabilitiesExecutorSlots_Reject).isUndefined())
+ {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_PROMISE_CAPABILITY_HAS_SOMETHING_ALREADY);
return false;
}
// Step 5.
- F->setExtendedSlot(0, args.get(0));
+ F->setExtendedSlot(GetCapabilitiesExecutorSlots_Resolve, args.get(0));
// Step 6.
- F->setExtendedSlot(1, args.get(1));
+ F->setExtendedSlot(GetCapabilitiesExecutorSlots_Reject, args.get(1));
// Step 7.
args.rval().setUndefined();
return true;
}
// ES2016, 25.4.1.7.
static MOZ_MUST_USE bool
@@ -738,23 +783,23 @@ TriggerPromiseReactions(JSContext* cx, H
reaction = &reactionsList->getDenseElement(i).toObject();
if (!EnqueuePromiseReactionJob(cx, reaction, valueOrReason, state))
return false;
}
return true;
}
-// ES6, 25.4.2.1.
+// ES2016, 25.4.2.1.
/**
* Callback triggering the fulfill/reject reaction for a resolved Promise,
* to be invoked by the embedding during its processing of the Promise job
* queue.
*
- * See http://www.ecma-international.org/ecma-262/6.0/index.html#sec-jobs-and-job-queues
+ * See http://www.ecma-international.org/ecma-262/7.0/index.html#sec-jobs-and-job-queues
*
* A PromiseReactionJob is set as the native function of an extended
* JSFunction object, with all information required for the job's
* execution stored in in a reaction record in its first extended slot.
*/
static bool
PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp)
{
@@ -772,34 +817,31 @@ PromiseReactionJob(JSContext* cx, unsign
// back, we check if the reaction is a wrapper and if so, unwrap it and
// enter its compartment.
mozilla::Maybe<AutoCompartment> ac;
if (IsWrapper(reactionObj)) {
reactionObj = UncheckedUnwrap(reactionObj);
ac.emplace(cx, reactionObj);
}
+ // Steps 1-2.
Rooted<PromiseReactionRecord*> reaction(cx, &reactionObj->as<PromiseReactionRecord>());
- JS:: PromiseState targetState = ReactionTargetState(reaction);
- uint32_t handlerSlot = JS::PromiseState(targetState) == JS::PromiseState::Fulfilled
- ? ReactionRecordSlot_OnFulfilled
- : ReactionRecordSlot_OnRejected;
- RootedValue handlerVal(cx, reaction->getFixedSlot(handlerSlot));
- RootedValue argument(cx, reaction->getFixedSlot(ReactionRecordSlot_HandlerArg));
-
- // Step 1 (omitted).
-
- // Steps 2-3.
+ // Step 3.
+ RootedValue handlerVal(cx, reaction->handler());
+
+ RootedValue argument(cx, reaction->handlerArg());
+
RootedValue handlerResult(cx);
ResolutionMode resolutionMode = ResolveMode;
- // Steps 4-7.
+ // Steps 4-6.
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);
resolutionMode = RejectMode;
handlerResult = argument;
@@ -817,31 +859,30 @@ PromiseReactionJob(JSContext* cx, unsign
}
}
// Steps 7-9.
size_t hookSlot = resolutionMode == RejectMode
? ReactionRecordSlot_Reject
: ReactionRecordSlot_Resolve;
RootedObject callee(cx, reaction->getFixedSlot(hookSlot).toObjectOrNull());
- RootedObject promiseObj(cx, reaction->getFixedSlot(ReactionRecordSlot_Promise)
- .toObjectOrNull());
+ RootedObject promiseObj(cx, reaction->promise());
if (!RunResolutionFunction(cx, callee, handlerResult, resolutionMode, promiseObj))
return false;
args.rval().setUndefined();
return true;
}
-// ES6, 25.4.2.2.
+// ES2016, 25.4.2.2.
/**
* Callback for resolving a thenable, to be invoked by the embedding during
* its processing of the Promise job queue.
*
- * See http://www.ecma-international.org/ecma-262/6.0/index.html#sec-jobs-and-job-queues
+ * See http://www.ecma-international.org/ecma-262/7.0/index.html#sec-jobs-and-job-queues
*
* A PromiseResolveThenableJob is set as the native function of an extended
* JSFunction object, with all information required for the job's
* execution stored in the function's extended slots.
*
* Usage of the function's extended slots is as follows:
* ThenableJobSlot_Handler: The handler to use as the Promise reaction.
* This can be PROMISE_HANDLER_IDENTITY,
@@ -865,18 +906,18 @@ PromiseResolveThenableJob(JSContext* cx,
CallArgs args = CallArgsFromVp(argc, vp);
RootedFunction job(cx, &args.callee().as<JSFunction>());
RootedValue then(cx, job->getExtendedSlot(ThenableJobSlot_Handler));
MOZ_ASSERT(!IsWrapper(&then.toObject()));
RootedNativeObject jobArgs(cx, &job->getExtendedSlot(ThenableJobSlot_JobData)
.toObject().as<NativeObject>());
- RootedValue promise(cx, jobArgs->getDenseElement(ThenableJobDataSlot_Promise));
- RootedValue thenable(cx, jobArgs->getDenseElement(ThenableJobDataSlot_Thenable));
+ RootedValue promise(cx, jobArgs->getDenseElement(ThenableJobDataIndex_Promise));
+ RootedValue thenable(cx, jobArgs->getDenseElement(ThenableJobDataIndex_Thenable));
// Step 1.
RootedValue resolveVal(cx);
RootedValue rejectVal(cx);
if (!CreateResolvingFunctions(cx, promise, &resolveVal, &rejectVal))
return false;
// Step 2.
@@ -897,19 +938,19 @@ PromiseResolveThenableJob(JSContext* cx,
rejectArgs[0].set(rval);
return Call(cx, rejectVal, UndefinedHandleValue, rejectArgs, &rval);
}
/**
* Tells the embedding to enqueue a Promise resolve thenable job, based on
* three parameters:
- * promiseToResolve - The promise to resolve, obviously.
- * thenable - The thenable to resolve the Promise with.
- * then - The `then` function to invoke with the `thenable` as the receiver.
+ * promiseToResolve_ - The promise to resolve, obviously.
+ * thenable_ - The thenable to resolve the Promise with.
+ * thenVal - The `then` function to invoke with the `thenable` as the receiver.
*/
static MOZ_MUST_USE bool
EnqueuePromiseResolveThenableJob(JSContext* cx, HandleValue promiseToResolve_,
HandleValue thenable_, HandleValue thenVal)
{
// Need to re-root these to enable wrapping them below.
RootedValue promiseToResolve(cx, promiseToResolve_);
RootedValue thenable(cx, thenable_);
@@ -929,36 +970,36 @@ EnqueuePromiseResolveThenableJob(JSConte
return false;
// Store the `then` function on the callback.
job->setExtendedSlot(ThenableJobSlot_Handler, ObjectValue(*then));
// Create a dense array to hold the data needed for the reaction job to
// work.
// See the doc comment for PromiseResolveThenableJob for the layout.
- RootedArrayObject data(cx, NewDenseFullyAllocatedArray(cx, ThenableJobDataSlotsCount));
+ RootedArrayObject data(cx, NewDenseFullyAllocatedArray(cx, ThenableJobDataLength));
if (!data ||
- data->ensureDenseElements(cx, 0, ThenableJobDataSlotsCount) != DenseElementResult::Success)
+ data->ensureDenseElements(cx, 0, ThenableJobDataLength) != DenseElementResult::Success)
{
return false;
}
// Wrap and set the `promiseToResolve` argument.
if (!cx->compartment()->wrap(cx, &promiseToResolve))
return false;
- data->setDenseElement(ThenableJobDataSlot_Promise, promiseToResolve);
+ data->setDenseElement(ThenableJobDataIndex_Promise, promiseToResolve);
// At this point the promise is guaranteed to be wrapped into the job's
// compartment.
RootedObject promise(cx, &promiseToResolve.toObject());
// Wrap and set the `thenable` argument.
MOZ_ASSERT(thenable.isObject());
if (!cx->compartment()->wrap(cx, &thenable))
return false;
- data->setDenseElement(ThenableJobDataSlot_Thenable, thenable);
+ data->setDenseElement(ThenableJobDataIndex_Thenable, thenable);
// Store the data array on the reaction job.
job->setExtendedSlot(ThenableJobSlot_JobData, ObjectValue(*data));
RootedObject incumbentGlobal(cx, cx->runtime()->getIncumbentGlobal(cx));
return cx->runtime()->enqueuePromiseJob(cx, job, promise, incumbentGlobal);
}
@@ -973,23 +1014,23 @@ AddPromiseReaction(JSContext* cx, Handle
static MOZ_MUST_USE bool BlockOnPromise(JSContext* cx, HandleObject promise,
HandleObject blockedPromise,
HandleValue onFulfilled, HandleValue onRejected);
static JSFunction*
GetResolveFunctionFromReject(JSFunction* reject)
{
+ MOZ_ASSERT(reject->maybeNative() == RejectPromiseFunction);
Value resolveFunVal = reject->getExtendedSlot(RejectFunctionSlot_ResolveFunction);
if (IsNativeFunction(resolveFunVal, ResolvePromiseFunction))
return &resolveFunVal.toObject().as<JSFunction>();
PromiseAllDataHolder* resolveFunObj = &resolveFunVal.toObject().as<PromiseAllDataHolder>();
- return &resolveFunObj->getFixedSlot(PromiseAllDataHolderSlot_ResolveFunction)
- .toObject().as<JSFunction>();
+ return &resolveFunObj->resolveObj()->as<JSFunction>();
}
static JSFunction*
GetResolveFunctionFromPromise(PromiseObject* promise)
{
Value rejectFunVal = promise->getFixedSlot(PromiseSlot_RejectFunction);
if (rejectFunVal.isUndefined())
return nullptr;
@@ -1008,29 +1049,32 @@ GetResolveFunctionFromPromise(PromiseObj
return nullptr;
return GetResolveFunctionFromReject(rejectFun);
}
static void
ClearResolutionFunctionSlots(JSFunction* resolutionFun)
{
- JSFunction* otherFun;
+ JSFunction* resolve;
+ JSFunction* reject;
if (resolutionFun->maybeNative() == ResolvePromiseFunction) {
- otherFun = &resolutionFun->getExtendedSlot(ResolveFunctionSlot_RejectFunction)
- .toObject().as<JSFunction>();
+ resolve = resolutionFun;
+ reject = &resolutionFun->getExtendedSlot(ResolveFunctionSlot_RejectFunction)
+ .toObject().as<JSFunction>();
} else {
- otherFun = GetResolveFunctionFromReject(resolutionFun);
+ resolve = GetResolveFunctionFromReject(resolutionFun);
+ reject = resolutionFun;
}
- resolutionFun->setExtendedSlot(0, UndefinedValue());
- resolutionFun->setExtendedSlot(1, UndefinedValue());
-
- otherFun->setExtendedSlot(0, UndefinedValue());
- otherFun->setExtendedSlot(1, UndefinedValue());
+ resolve->setExtendedSlot(ResolveFunctionSlot_Promise, UndefinedValue());
+ resolve->setExtendedSlot(ResolveFunctionSlot_RejectFunction, UndefinedValue());
+
+ reject->setExtendedSlot(RejectFunctionSlot_Promise, UndefinedValue());
+ reject->setExtendedSlot(RejectFunctionSlot_ResolveFunction, UndefinedValue());
}
// ES2016, 25.4.3.1. steps 3-7.
static MOZ_MUST_USE PromiseObject*
CreatePromiseObjectInternal(JSContext* cx, HandleObject proto /* = nullptr */,
bool protoIsWrapped /* = false */, bool informDebugger /* = true */)
{
// Step 3.
@@ -1070,17 +1114,17 @@ CreatePromiseObjectInternal(JSContext* c
// Let the Debugger know about this Promise.
if (informDebugger)
JS::dbg::onNewPromise(cx, promise);
return promise;
}
-// ES6, 25.4.3.1.
+// ES2016, 25.4.3.1.
static bool
PromiseConstructor(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// Step 1.
if (!ThrowIfNotConstructing(cx, args, "Promise"))
return false;
@@ -1249,17 +1293,17 @@ static MOZ_MUST_USE bool PerformPromiseA
// ES2016, 25.4.4.1.
static bool
Promise_static_all(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedValue iterable(cx, args.get(0));
- // Step 2.
+ // Step 2 (reordered).
RootedValue CVal(cx, args.thisv());
if (!CVal.isObject()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
"Receiver of Promise.all call");
return false;
}
// Step 1.
@@ -1293,17 +1337,17 @@ Promise_static_all(JSContext* cx, unsign
// Step 8.a.
// TODO: implement iterator closing.
// Step 8.b.
return AbruptRejectPromise(cx, args, resultPromise, reject);
}
// Step 9.
- args.rval().set(ObjectValue(*resultPromise));
+ args.rval().setObject(*resultPromise);
return true;
}
static MOZ_MUST_USE bool PerformPromiseThen(JSContext* cx, Handle<PromiseObject*> promise,
HandleValue onFulfilled_, HandleValue onRejected_,
HandleObject resultPromise,
HandleObject resolve, HandleObject reject);
@@ -1333,17 +1377,17 @@ js::GetWaitForAllPromise(JSContext* cx,
RootedObject resolve(cx);
RootedObject reject(cx);
if (!NewPromiseCapability(cx, C, &resultPromise, &resolve, &reject, false))
return nullptr;
// Steps 4-6 (omitted).
// Step 7.
- // Implemented as an inlined, simplied version of PerformPromiseAll.
+ // Implemented as an inlined, simplied version of ES2016 25.4.4.1.1, PerformPromiseAll.
{
uint32_t promiseCount = promises.length();
// Sub-steps 1-2 (omitted).
// Sub-step 3.
RootedNativeObject valuesArray(cx, NewDenseFullyAllocatedArray(cx, promiseCount));
if (!valuesArray ||
valuesArray->ensureDenseElements(cx, 0, promiseCount) != DenseElementResult::Success)
@@ -1351,84 +1395,79 @@ js::GetWaitForAllPromise(JSContext* cx,
return nullptr;
}
// Sub-step 4.
// Create our data holder that holds all the things shared across
// every step of the iterator. In particular, this holds the
// remainingElementsCount (as an integer reserved slot), the array of
// values, and the resolve function from our PromiseCapability.
- RootedNativeObject dataHolder(cx, NewObjectWithClassProto<PromiseAllDataHolder>(cx));
+ Rooted<PromiseAllDataHolder*> dataHolder(cx, NewPromiseAllDataHolder(cx, resultPromise,
+ valuesArray, resolve));
if (!dataHolder)
return nullptr;
RootedValue dataHolderVal(cx, ObjectValue(*dataHolder));
- int32_t remainingCount = 0;
- dataHolder->setFixedSlot(PromiseAllDataHolderSlot_Promise, ObjectValue(*resultPromise));
- dataHolder->setFixedSlot(PromiseAllDataHolderSlot_RemainingElements,
- Int32Value(remainingCount));
- dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ValuesArray, ObjectValue(*valuesArray));
- dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ResolveFunction,
- ObjectOrNullValue(resolve));
-
// Sub-step 5 (inline in loop-header below).
// Sub-step 6.
for (uint32_t index = 0; index < promiseCount; index++) {
// Steps a-c (omitted).
- // Step d (implemented after the loop.
+ // Step d (implemented after the loop).
// Steps e-g (omitted).
// Step h.
valuesArray->setDenseElement(index, UndefinedHandleValue);
- // Steps i-j, vastly simplified.
+ // Step i, vastly simplified.
RootedObject nextPromiseObj(cx, promises[index]);
- // Step k.
+ // Step j.
RootedFunction resolveFunc(cx, NewNativeFunction(cx, PromiseAllResolveElementFunction,
1, nullptr,
gc::AllocKind::FUNCTION_EXTENDED,
GenericObject));
if (!resolveFunc)
return nullptr;
- // Steps l-p.
+ // Steps k-o.
resolveFunc->setExtendedSlot(PromiseAllResolveElementFunctionSlot_Data, dataHolderVal);
resolveFunc->setExtendedSlot(PromiseAllResolveElementFunctionSlot_ElementIndex,
Int32Value(index));
- // Step q.
- remainingCount++;
- dataHolder->setFixedSlot(PromiseAllDataHolderSlot_RemainingElements,
- Int32Value(remainingCount));
-
- // Steps r-s, very roughly.
+ // Step p.
+ dataHolder->increaseRemainingCount();
+
+ // Step q, very roughly.
RootedValue resolveFunVal(cx, ObjectValue(*resolveFunc));
- RootedValue rejectFunVal(cx, ObjectOrNullValue(reject));
+ RootedValue rejectFunVal(cx, ObjectValue(*reject));
Rooted<PromiseObject*> nextPromise(cx);
// GetWaitForAllPromise is used internally only and must not
// trigger content-observable effects when registering a reaction.
// It's also meant to work on wrapped Promises, potentially from
// compartments with principals inaccessible from the current
// compartment. To make that work, it unwraps promises with
// UncheckedUnwrap,
nextPromise = &UncheckedUnwrap(nextPromiseObj)->as<PromiseObject>();
if (!PerformPromiseThen(cx, nextPromise, resolveFunVal, rejectFunVal,
resultPromise, nullptr, nullptr))
{
return nullptr;
}
- // Step t (inline in loop-header).
+ // Step r (inline in loop-header).
}
- // Sub-step 6.d.
+ // Sub-step d.i (implicit).
+ // Sub-step d.ii.
+ int32_t remainingCount = dataHolder->decreaseRemainingCount();
+
+ // Sub-step d.iii-iv.
if (remainingCount == 0) {
RootedValue valuesArrayVal(cx, ObjectValue(*valuesArray));
if (!ResolvePromiseInternal(cx, resultPromise, valuesArrayVal))
return nullptr;
}
}
// Step 8 (omitted).
@@ -1467,17 +1506,17 @@ RunResolutionFunction(JSContext *cx, Han
}
if (!PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_REJECT_FUNCTION))
return true;
return RejectMaybeWrappedPromise(cx, promiseObj, result);
}
-// ES2016, 25.4.4.1.1
+// ES2016, 25.4.4.1.1.
static MOZ_MUST_USE bool
PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C,
HandleObject promiseObj, HandleObject resolve, HandleObject reject)
{
RootedObject unwrappedPromiseObj(cx);
if (IsWrapper(promiseObj)) {
unwrappedPromiseObj = CheckedUnwrap(promiseObj);
MOZ_ASSERT(unwrappedPromiseObj);
@@ -1523,28 +1562,22 @@ PerformPromiseAll(JSContext *cx, JS::For
if (!cx->compartment()->wrap(cx, &valuesArrayVal))
return false;
// Step 4.
// Create our data holder that holds all the things shared across
// every step of the iterator. In particular, this holds the
// remainingElementsCount (as an integer reserved slot), the array of
// values, and the resolve function from our PromiseCapability.
- RootedNativeObject dataHolder(cx, NewObjectWithClassProto<PromiseAllDataHolder>(cx));
+ Rooted<PromiseAllDataHolder*> dataHolder(cx, NewPromiseAllDataHolder(cx, promiseObj,
+ valuesArray, resolve));
if (!dataHolder)
return false;
RootedValue dataHolderVal(cx, ObjectValue(*dataHolder));
- int32_t remainingCount = 1;
- dataHolder->setFixedSlot(PromiseAllDataHolderSlot_Promise, ObjectValue(*promiseObj));
- dataHolder->setFixedSlot(PromiseAllDataHolderSlot_RemainingElements,
- Int32Value(remainingCount));
- dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ValuesArray, valuesArrayVal);
- dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ResolveFunction, ObjectOrNullValue(resolve));
-
// Step 5.
uint32_t index = 0;
// Step 6.
RootedValue nextValue(cx);
RootedId indexId(cx);
RootedValue rejectFunVal(cx, ObjectOrNullValue(reject));
@@ -1553,21 +1586,17 @@ PerformPromiseAll(JSContext *cx, JS::For
// Steps a, b, c, e, f, g.
if (!iterator.next(&nextValue, &done))
return false;
// Step d.
if (done) {
// Step d.i (implicit).
// Step d.ii.
- remainingCount = dataHolder->getFixedSlot(PromiseAllDataHolderSlot_RemainingElements)
- .toInt32();
- remainingCount--;
- dataHolder->setFixedSlot(PromiseAllDataHolderSlot_RemainingElements,
- Int32Value(remainingCount));
+ int32_t remainingCount = dataHolder->decreaseRemainingCount();
// Steps d.iii-iv.
if (remainingCount == 0) {
if (resolve) {
return RunResolutionFunction(cx, resolve, valuesArrayVal, ResolveMode,
promiseObj);
}
return ResolvePromiseInternal(cx, promiseObj, valuesArrayVal);
@@ -1611,21 +1640,17 @@ PerformPromiseAll(JSContext *cx, JS::For
// Steps k,m,n.
resolveFunc->setExtendedSlot(PromiseAllResolveElementFunctionSlot_Data, dataHolderVal);
// Step l.
resolveFunc->setExtendedSlot(PromiseAllResolveElementFunctionSlot_ElementIndex,
Int32Value(index));
// Steps o-p.
- remainingCount = dataHolder->getFixedSlot(PromiseAllDataHolderSlot_RemainingElements)
- .toInt32();
- remainingCount++;
- dataHolder->setFixedSlot(PromiseAllDataHolderSlot_RemainingElements,
- Int32Value(remainingCount));
+ dataHolder->increaseRemainingCount();
// Step q.
RootedObject nextPromiseObj(cx, &nextPromise.toObject());
RootedValue resolveFunVal(cx, ObjectValue(*resolveFunc));
if (!BlockOnPromise(cx, nextPromiseObj, promiseObj, resolveFunVal, rejectFunVal))
return false;
// Step r.
@@ -1660,56 +1685,49 @@ PromiseAllResolveElementFunction(JSConte
// Step 3.
resolve->setExtendedSlot(PromiseAllResolveElementFunctionSlot_Data, UndefinedValue());
// Step 4.
int32_t index = resolve->getExtendedSlot(PromiseAllResolveElementFunctionSlot_ElementIndex)
.toInt32();
// Step 5.
- RootedValue valuesVal(cx, data->getFixedSlot(PromiseAllDataHolderSlot_ValuesArray));
+ RootedValue valuesVal(cx, data->valuesArray());
RootedObject valuesObj(cx, &valuesVal.toObject());
bool valuesListIsWrapped = false;
if (IsWrapper(valuesObj)) {
valuesListIsWrapped = true;
// See comment for PerformPromiseAll, step 3 for why we unwrap here.
valuesObj = UncheckedUnwrap(valuesObj);
}
RootedNativeObject values(cx, &valuesObj->as<NativeObject>());
- // Step 6 (moved unter step 10).
-
- // Step 7.
- int32_t remainingElementsCount = data->getFixedSlot(PromiseAllDataHolderSlot_RemainingElements)
- .toInt32();
+ // Step 6 (moved under step 10).
+ // Step 7 (moved to step 9).
// Step 8.
// The index is guaranteed to be initialized to `undefined`.
if (valuesListIsWrapped) {
AutoCompartment ac(cx, values);
if (!cx->compartment()->wrap(cx, &xVal))
return false;
}
values->setDenseElement(index, xVal);
- // Step 9.
- remainingElementsCount--;
- data->setFixedSlot(PromiseAllDataHolderSlot_RemainingElements,
- Int32Value(remainingElementsCount));
+ // Steps 7,9.
+ uint32_t remainingCount = data->decreaseRemainingCount();
// Step 10.
- if (remainingElementsCount == 0) {
+ if (remainingCount == 0) {
// Step 10.a. (Omitted, happened in PerformPromiseAll.)
// Step 10.b.
// Step 6 (Adapted to work with PromiseAllDataHolder's layout).
- RootedObject resolveAllFun(cx, data->getFixedSlot(PromiseAllDataHolderSlot_ResolveFunction)
- .toObjectOrNull());
- RootedObject promiseObj(cx, data->getFixedSlot(PromiseAllDataHolderSlot_Promise)
- .toObjectOrNull());
+ RootedObject resolveAllFun(cx, data->resolveObj());
+ RootedObject promiseObj(cx, data->promiseObj());
if (!resolveAllFun) {
if (!FulfillMaybeWrappedPromise(cx, promiseObj, valuesVal))
return false;
} else {
if (!RunResolutionFunction(cx, resolveAllFun, valuesVal, ResolveMode, promiseObj))
return false;
}
}
@@ -1725,17 +1743,17 @@ static MOZ_MUST_USE bool PerformPromiseR
// ES2016, 25.4.4.3.
static bool
Promise_static_race(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedValue iterable(cx, args.get(0));
- // Step 2.
+ // Step 2 (reordered).
RootedValue CVal(cx, args.thisv());
if (!CVal.isObject()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
"Receiver of Promise.race call");
return false;
}
// Step 1.
@@ -1769,32 +1787,28 @@ Promise_static_race(JSContext* cx, unsig
// Step 8.a.
// TODO: implement iterator closing.
// Step 8.b.
return AbruptRejectPromise(cx, args, resultPromise, reject);
}
// Step 9.
- args.rval().set(ObjectValue(*resultPromise));
+ args.rval().setObject(*resultPromise);
return true;
}
-// ES2016, 25.4.4.3.1
+// ES2016, 25.4.4.3.1.
static MOZ_MUST_USE bool
PerformPromiseRace(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C,
HandleObject promiseObj, HandleObject resolve, HandleObject reject)
{
- // Step 1.
MOZ_ASSERT(C->isConstructor());
RootedValue CVal(cx, ObjectValue(*C));
- // Step 2 (omitted).
-
- // Step 3.
RootedValue nextValue(cx);
RootedValue resolveFunVal(cx, ObjectOrNullValue(resolve));
RootedValue rejectFunVal(cx, ObjectOrNullValue(reject));
bool done;
while (true) {
// Steps a-c, e-g.
if (!iterator.next(&nextValue, &done))
@@ -2003,29 +2017,30 @@ js::OriginalPromiseThen(JSContext* cx, H
HandleValue onRejected)
{
RootedObject promiseObj(cx, promise);
if (promise->compartment() != cx->compartment()) {
if (!cx->compartment()->wrap(cx, &promiseObj))
return nullptr;
}
+ // Step 3.
RootedValue ctorVal(cx);
if (!SpeciesConstructor(cx, promiseObj, JSProto_Promise, &ctorVal))
return nullptr;
RootedObject C(cx, &ctorVal.toObject());
- // Steps 5-6.
+ // Step 4.
RootedObject resultPromise(cx);
RootedObject resolve(cx);
RootedObject reject(cx);
if (!NewPromiseCapability(cx, C, &resultPromise, &resolve, &reject, true))
return nullptr;
- // Step 7.
+ // Step 5.
if (!PerformPromiseThen(cx, promise, onFulfilled, onRejected, resultPromise, resolve, reject))
return nullptr;
return resultPromise;
}
// ES2016, 25.4.5.3.
static bool
@@ -2155,62 +2170,63 @@ PerformPromiseThen(JSContext* cx, Handle
static MOZ_MUST_USE bool
BlockOnPromise(JSContext* cx, HandleObject promiseObj, HandleObject blockedPromise_,
HandleValue onFulfilled, HandleValue onRejected)
{
RootedValue thenVal(cx);
if (!GetProperty(cx, promiseObj, promiseObj, cx->names().then, &thenVal))
return false;
- // By default, the blocked promise is added as an extra entry to the
- // rejected promises list.
- bool addToDependent = true;
if (promiseObj->is<PromiseObject>() && IsNativeFunction(thenVal, Promise_then)) {
// |promise| is an unwrapped Promise, and |then| is the original
// |Promise.prototype.then|, inline it here.
- // 25.4.5.3., steps 3-4.
+ // 25.4.5.3., step 3.
RootedObject PromiseCtor(cx);
if (!GetBuiltinConstructor(cx, JSProto_Promise, &PromiseCtor))
return false;
RootedValue PromiseCtorVal(cx, ObjectValue(*PromiseCtor));
RootedValue CVal(cx);
if (!SpeciesConstructor(cx, promiseObj, PromiseCtorVal, &CVal))
return false;
RootedObject C(cx, &CVal.toObject());
RootedObject resultPromise(cx, blockedPromise_);
RootedObject resolveFun(cx);
RootedObject rejectFun(cx);
+ // By default, the blocked promise is added as an extra entry to the
+ // rejected promises list.
+ bool addToDependent = true;
+
if (C == PromiseCtor) {
addToDependent = false;
} else {
- // 25.4.5.3., steps 5-6.
+ // 25.4.5.3., step 4.
if (!NewPromiseCapability(cx, C, &resultPromise, &resolveFun, &rejectFun, true))
return false;
}
- // 25.4.5.3., step 7.
+ // 25.4.5.3., step 5.
Rooted<PromiseObject*> promise(cx, &promiseObj->as<PromiseObject>());
if (!PerformPromiseThen(cx, promise, onFulfilled, onRejected, resultPromise,
resolveFun, rejectFun))
{
return false;
}
+
+ if (!addToDependent)
+ return true;
} else {
// Optimization failed, do the normal call.
RootedValue promiseVal(cx, ObjectValue(*promiseObj));
RootedValue rval(cx);
if (!Call(cx, thenVal, promiseVal, onFulfilled, onRejected, &rval))
return false;
}
- if (!addToDependent)
- return true;
-
// The object created by the |promise.then| call or the inlined version
// of it above is visible to content (either because |promise.then| was
// overridden by content and could leak it, or because a constructor
// other than the original value of |Promise| was used to create it).
// To have both that object and |blockedPromise| show up as dependent
// promises in the debugger, add a dummy reaction to the list of reject
// reactions that contains |blockedPromise|, but otherwise does nothing.
RootedObject unwrappedPromiseObj(cx, promiseObj);
@@ -2251,17 +2267,17 @@ AddPromiseReaction(JSContext* cx, Handle
// is properly wrapped.
mozilla::Maybe<AutoCompartment> ac;
if (promise->compartment() != cx->compartment()) {
ac.emplace(cx, promise);
if (!cx->compartment()->wrap(cx, &reactionVal))
return false;
}
- // Steps 7.a,b.
+ // 25.4.5.3.1 steps 7.a,b.
RootedValue reactionsVal(cx, promise->getFixedSlot(PromiseSlot_ReactionsOrResult));
RootedNativeObject reactions(cx);
if (reactionsVal.isUndefined()) {
// If no reactions existed so far, just store the reaction record directly.
promise->setFixedSlot(PromiseSlot_ReactionsOrResult, reactionVal);
return true;
}
@@ -2275,21 +2291,25 @@ AddPromiseReaction(JSContext* cx, Handle
reactionsObj = UncheckedUnwrap(reactionsObj);
MOZ_ASSERT(reactionsObj->is<PromiseReactionRecord>());
}
if (reactionsObj->is<PromiseReactionRecord>()) {
// If a single reaction existed so far, create a list and store the
// old and the new reaction in it.
reactions = NewDenseFullyAllocatedArray(cx, 2);
- if (!reactions || reactions->ensureDenseElements(cx, 0, 2) != DenseElementResult::Success)
+ if (!reactions)
return false;
- promise->setFixedSlot(PromiseSlot_ReactionsOrResult, ObjectValue(*reactions));
+ if (reactions->ensureDenseElements(cx, 0, 2) != DenseElementResult::Success)
+ return false;
+
reactions->setDenseElement(0, reactionsVal);
reactions->setDenseElement(1, reactionVal);
+
+ promise->setFixedSlot(PromiseSlot_ReactionsOrResult, ObjectValue(*reactions));
} else {
// Otherwise, just store the new reaction.
reactions = &reactionsObj->as<NativeObject>();
uint32_t len = reactions->getDenseInitializedLength();
if (reactions->ensureDenseElements(cx, 0, len + 1) != DenseElementResult::Success)
return false;
reactions->setDenseElement(len, reactionVal);
}
@@ -2323,20 +2343,20 @@ double
PromiseObject::lifetime()
{
return MillisecondsSinceStartup() - allocationTime();
}
uint64_t
PromiseObject::getID()
{
- Value idVal(getReservedSlot(PromiseSlot_Id));
+ Value idVal(getFixedSlot(PromiseSlot_Id));
if (idVal.isUndefined()) {
idVal.setDouble(++gIDGenerator);
- setReservedSlot(PromiseSlot_Id, idVal);
+ setFixedSlot(PromiseSlot_Id, idVal);
}
return uint64_t(idVal.toNumber());
}
/**
* Returns all promises that directly depend on this one. That means those
* created by calling `then` on this promise, or the promise returned by
* `Promise.all(iterable)` or `Promise.race(iterable)`, with this promise
@@ -2348,51 +2368,59 @@ PromiseObject::getID()
* and extract the dependent promises from all reaction records.
*/
bool
PromiseObject::dependentPromises(JSContext* cx, MutableHandle<GCVector<Value>> values)
{
if (state() != JS::PromiseState::Pending)
return true;
- RootedValue reactionsVal(cx, getReservedSlot(PromiseSlot_ReactionsOrResult));
+ RootedValue reactionsVal(cx, getFixedSlot(PromiseSlot_ReactionsOrResult));
// If no reactions are pending, we don't have list and are done.
if (reactionsVal.isNullOrUndefined())
return true;
RootedNativeObject reactions(cx, &reactionsVal.toObject().as<NativeObject>());
// If only a single reaction is pending, it's stored directly.
if (reactions->is<PromiseReactionRecord>()) {
+ // Not all reactions have a Promise on them.
+ RootedObject promiseObj(cx, reactions->as<PromiseReactionRecord>().promise());
+ if (!promiseObj)
+ return true;
+
if (!values.growBy(1))
return false;
- RootedValue promiseVal(cx, reactions->getFixedSlot(ReactionRecordSlot_Promise));
- Rooted<PromiseObject*> promise(cx, &promiseVal.toObject().as<PromiseObject>());
- values[0].set(reactions->getFixedSlot(ReactionRecordSlot_Promise));
+
+ values[0].setObject(*promiseObj);
return true;
}
uint32_t len = reactions->getDenseInitializedLength();
MOZ_ASSERT(len >= 2);
- if (!values.growBy(len))
- return false;
-
+ size_t valuesIndex = 0;
Rooted<PromiseReactionRecord*> reaction(cx);
for (size_t i = 0; i < len; i++) {
reaction = &reactions->getDenseElement(i).toObject().as<PromiseReactionRecord>();
- values[i].set(reaction->getFixedSlot(ReactionRecordSlot_Promise));
+
+ // Not all reactions have a Promise on them.
+ RootedObject promiseObj(cx, reaction->promise());
+ if (!promiseObj)
+ continue;
+ if (!values.growBy(1))
+ return false;
+
+ values[valuesIndex++].setObject(*promiseObj);
}
return true;
}
-namespace js {
-
bool
PromiseObject::resolve(JSContext* cx, HandleValue resolutionValue)
{
if (state() != JS::PromiseState::Pending)
return true;
RootedObject resolveFun(cx, GetResolveFunctionFromPromise(this));
RootedValue funVal(cx, ObjectValue(*resolveFun));
@@ -2411,17 +2439,17 @@ PromiseObject::resolve(JSContext* cx, Ha
}
bool
PromiseObject::reject(JSContext* cx, HandleValue rejectionValue)
{
if (state() != JS::PromiseState::Pending)
return true;
- RootedValue funVal(cx, this->getReservedSlot(PromiseSlot_RejectFunction));
+ RootedValue funVal(cx, this->getFixedSlot(PromiseSlot_RejectFunction));
MOZ_ASSERT(IsCallable(funVal));
FixedInvokeArgs<1> args(cx);
args[0].set(rejectionValue);
RootedValue dummy(cx);
return Call(cx, funVal, UndefinedHandleValue, args, &dummy);
}
@@ -2442,19 +2470,19 @@ PromiseObject::onSettled(JSContext* cx)
if (promise->state() == JS::PromiseState::Rejected && promise->isUnhandled())
cx->runtime()->addUnhandledRejectedPromise(cx, promise);
JS::dbg::onPromiseSettled(cx, promise);
}
MOZ_MUST_USE bool
-EnqueuePromiseReactions(JSContext* cx, Handle<PromiseObject*> promise,
- HandleObject dependentPromise,
- HandleValue onFulfilled, HandleValue onRejected)
+js::EnqueuePromiseReactions(JSContext* cx, Handle<PromiseObject*> promise,
+ HandleObject dependentPromise,
+ HandleValue onFulfilled, HandleValue onRejected)
{
MOZ_ASSERT_IF(dependentPromise, dependentPromise->is<PromiseObject>());
return PerformPromiseThen(cx, promise, onFulfilled, onRejected, dependentPromise,
nullptr, nullptr);
}
PromiseTask::PromiseTask(JSContext* cx, Handle<PromiseObject*> promise)
: runtime_(cx),
@@ -2477,24 +2505,22 @@ PromiseTask::finish(JSContext* cx)
AutoCompartment ac(cx, promise_);
if (!finishPromise(cx, promise_))
cx->clearPendingException();
}
js_delete(this);
}
void
-js::PromiseTask::cancel(JSContext* cx)
+PromiseTask::cancel(JSContext* cx)
{
MOZ_ASSERT(cx == runtime_);
js_delete(this);
}
-} // namespace js
-
static JSObject*
CreatePromisePrototype(JSContext* cx, JSProtoKey key)
{
return cx->global()->createBlankPrototype(cx, &PromiseObject::protoClass_);
}
static const JSFunctionSpec promise_methods[] = {
JS_SELF_HOSTED_FN("catch", "Promise_catch", 1, 0),