Issue #2089 - Improve Runtime perf of Promises

- reduce typecasting
- reduce rooting already rooted values
- directly handle unwrapped promises

Based-on: m-c 1475678/{1-7}
This commit is contained in:
Martok 2023-01-23 00:19:20 +01:00 • committed by roytam1
commit 1fd51e14f3
3 changed files with 214 additions and 187 deletions

View file

@ -162,7 +162,7 @@ static PromiseAllDataHolder*
NewPromiseAllDataHolder(JSContext* cx, HandleObject resultPromise, HandleValue valuesArray, NewPromiseAllDataHolder(JSContext* cx, HandleObject resultPromise, HandleValue valuesArray,
HandleObject resolve) HandleObject resolve)
{ {
Rooted<PromiseAllDataHolder*> dataHolder(cx, NewObjectWithClassProto<PromiseAllDataHolder>(cx)); PromiseAllDataHolder* dataHolder = NewObjectWithClassProto<PromiseAllDataHolder>(cx);
if (!dataHolder) if (!dataHolder)
return nullptr; return nullptr;
@ -436,19 +436,19 @@ enum ReactionRecordSlots {
// ES2016, 25.4.1.2. // ES2016, 25.4.1.2.
class PromiseReactionRecord : public NativeObject class PromiseReactionRecord : public NativeObject
{ {
static constexpr size_t REACTION_FLAG_RESOLVED = 0x1; static constexpr uint32_t REACTION_FLAG_RESOLVED = 0x1;
static constexpr size_t REACTION_FLAG_FULFILLED = 0x2; static constexpr uint32_t REACTION_FLAG_FULFILLED = 0x2;
static constexpr size_t REACTION_FLAG_DEFAULT_RESOLVING_HANDLER = 0x4; static constexpr uint32_t REACTION_FLAG_DEFAULT_RESOLVING_HANDLER = 0x4;
static constexpr size_t REACTION_FLAG_ASYNC_FUNCTION = 0x8; static constexpr uint32_t REACTION_FLAG_ASYNC_FUNCTION = 0x8;
static constexpr size_t REACTION_FLAG_ASYNC_GENERATOR = 0x10; static constexpr uint32_t REACTION_FLAG_ASYNC_GENERATOR = 0x10;
static constexpr size_t REACTION_FLAG_DEBUGGER_DUMMY = 0x20; static constexpr uint32_t REACTION_FLAG_DEBUGGER_DUMMY = 0x20;
void setFlagOnInitialState(size_t flag) { void setFlagOnInitialState(uint32_t flag) {
int32_t flags = this->flags(); int32_t flags = this->flags();
MOZ_ASSERT(flags == 0, "Can't modify with non-default flags"); MOZ_ASSERT(flags == 0, "Can't modify with non-default flags");
flags |= flag; flags |= flag;
setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags)); setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
} }
public: public:
static const Class class_; static const Class class_;
@ -598,8 +598,13 @@ IsSettledMaybeWrappedPromise(JSObject* promise)
return promise->as<PromiseObject>().state() != JS::PromiseState::Pending; return promise->as<PromiseObject>().state() != JS::PromiseState::Pending;
} }
static MOZ_MUST_USE bool RejectMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj, // ES2016, 25.4.1.7.
HandleValue reason); static MOZ_MUST_USE bool
RejectMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj, HandleValue reason);
// ES2016, 25.4.1.7.
static MOZ_MUST_USE bool
RejectPromiseInternal(JSContext* cx, Handle<PromiseObject*> promise, HandleValue reason);
// ES2016, 25.4.1.3.1. // ES2016, 25.4.1.3.1.
static bool static bool
@ -607,11 +612,11 @@ RejectPromiseFunction(JSContext* cx, unsigned argc, Value* vp)
{ {
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
RootedFunction reject(cx, &args.callee().as<JSFunction>()); JSFunction* reject = &args.callee().as<JSFunction>();
RootedValue reasonVal(cx, args.get(0)); HandleValue reasonVal = args.get(0);
// Steps 1-2. // Steps 1-2.
RootedValue promiseVal(cx, reject->getExtendedSlot(RejectFunctionSlot_Promise)); const Value& promiseVal = reject->getExtendedSlot(RejectFunctionSlot_Promise);
// Steps 3-4. // Steps 3-4.
// If the Promise isn't available anymore, it has been resolved and the // If the Promise isn't available anymore, it has been resolved and the
@ -621,13 +626,15 @@ RejectPromiseFunction(JSContext* cx, unsigned argc, Value* vp)
return true; return true;
} }
// Store the promise value in |promise| before ClearResolutionFunctionSlots
// removes the reference.
RootedObject promise(cx, &promiseVal.toObject());
// Step 5. // Step 5.
// Here, we only remove the Promise reference from the resolution // Here, we only remove the Promise reference from the resolution
// functions. Actually marking it as fulfilled/rejected happens later. // functions. Actually marking it as fulfilled/rejected happens later.
ClearResolutionFunctionSlots(reject); ClearResolutionFunctionSlots(reject);
RootedObject promise(cx, &promiseVal.toObject());
// In some cases the Promise reference on the resolution function won't // In some cases the Promise reference on the resolution function won't
// have been removed during resolution, so we need to check that here, // have been removed during resolution, so we need to check that here,
// too. // too.
@ -749,8 +756,8 @@ ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp)
{ {
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
RootedFunction resolve(cx, &args.callee().as<JSFunction>()); JSFunction* resolve = &args.callee().as<JSFunction>();
RootedValue resolutionVal(cx, args.get(0)); HandleValue resolutionVal = args.get(0);
// Steps 3-4 (reordered). // Steps 3-4 (reordered).
// We use the reference to the reject function as a signal for whether // We use the reference to the reject function as a signal for whether
@ -813,14 +820,15 @@ EnqueuePromiseReactionJob(JSContext* cx, HandleObject reactionObj,
MOZ_RELEASE_ASSERT(reactionObj->is<PromiseReactionRecord>()); MOZ_RELEASE_ASSERT(reactionObj->is<PromiseReactionRecord>());
reaction = &reactionObj->as<PromiseReactionRecord>(); reaction = &reactionObj->as<PromiseReactionRecord>();
} else { } else {
if (JS_IsDeadWrapper(UncheckedUnwrap(reactionObj))) { JSObject* unwrappedReactionObj = UncheckedUnwrap(reactionObj);
if (JS_IsDeadWrapper(unwrappedReactionObj)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
return false; return false;
} }
reaction = &UncheckedUnwrap(reactionObj)->as<PromiseReactionRecord>(); reaction = &unwrappedReactionObj->as<PromiseReactionRecord>();
MOZ_RELEASE_ASSERT(reaction->is<PromiseReactionRecord>()); MOZ_RELEASE_ASSERT(reaction->is<PromiseReactionRecord>());
ac.emplace(cx, reaction); ac.emplace(cx, reaction);
if (!reaction->compartment()->wrap(cx, &handlerArg)) if (!cx->compartment()->wrap(cx, &handlerArg))
return false; return false;
} }
@ -842,13 +850,11 @@ EnqueuePromiseReactionJob(JSContext* cx, HandleObject reactionObj,
// from said global. // from said global.
mozilla::Maybe<AutoCompartment> ac2; mozilla::Maybe<AutoCompartment> ac2;
if (handler.isObject()) { if (handler.isObject()) {
RootedObject handlerObj(cx, &handler.toObject());
// The unwrapping has to be unchecked because we specifically want to // The unwrapping has to be unchecked because we specifically want to
// be able to use handlers with wrappers that would only allow calls. // be able to use handlers with wrappers that would only allow calls.
// E.g., it's ok to have a handler from a chrome compartment in a // E.g., it's ok to have a handler from a chrome compartment in a
// reaction to a content compartment's Promise instance. // reaction to a content compartment's Promise instance.
handlerObj = UncheckedUnwrap(handlerObj); JSObject* handlerObj = UncheckedUnwrap(&handler.toObject());
MOZ_ASSERT(handlerObj); MOZ_ASSERT(handlerObj);
ac2.emplace(cx, handlerObj); ac2.emplace(cx, handlerObj);
@ -890,8 +896,7 @@ EnqueuePromiseReactionJob(JSContext* cx, HandleObject reactionObj,
// much better than having to store the original global as a private value // 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. // because we couldn't wrap it to store it as a normal JS value.
RootedObject global(cx); RootedObject global(cx);
RootedObject objectFromIncumbentGlobal(cx, reaction->incumbentGlobalObject()); if (JSObject* objectFromIncumbentGlobal = reaction->incumbentGlobalObject()) {
if (objectFromIncumbentGlobal) {
objectFromIncumbentGlobal = CheckedUnwrap(objectFromIncumbentGlobal); objectFromIncumbentGlobal = CheckedUnwrap(objectFromIncumbentGlobal);
MOZ_ASSERT(objectFromIncumbentGlobal); MOZ_ASSERT(objectFromIncumbentGlobal);
global = &objectFromIncumbentGlobal->global(); global = &objectFromIncumbentGlobal->global();
@ -951,6 +956,13 @@ ResolvePromise(JSContext* cx, Handle<PromiseObject*> promise, HandleValue valueO
return true; return true;
} }
// ES2016, 25.4.1.7.
static MOZ_MUST_USE bool
RejectPromiseInternal(JSContext* cx, Handle<PromiseObject*> promise, HandleValue reason)
{
return ResolvePromise(cx, promise, reason, JS::PromiseState::Rejected);
}
// ES2016, 25.4.1.4. // ES2016, 25.4.1.4.
static MOZ_MUST_USE bool static MOZ_MUST_USE bool
FulfillMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj, HandleValue value_) FulfillMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj, HandleValue value_)
@ -962,13 +974,14 @@ FulfillMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj, HandleValue v
if (!IsProxy(promiseObj)) { if (!IsProxy(promiseObj)) {
promise = &promiseObj->as<PromiseObject>(); promise = &promiseObj->as<PromiseObject>();
} else { } else {
if (JS_IsDeadWrapper(UncheckedUnwrap(promiseObj))) { JSObject* unwrappedPromiseObj = UncheckedUnwrap(promiseObj);
if (JS_IsDeadWrapper(unwrappedPromiseObj)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
return false; return false;
} }
promise = &UncheckedUnwrap(promiseObj)->as<PromiseObject>(); promise = &unwrappedPromiseObj->as<PromiseObject>();
ac.emplace(cx, promise); ac.emplace(cx, promise);
if (!promise->compartment()->wrap(cx, &value)) if (!cx->compartment()->wrap(cx, &value))
return false; return false;
} }
@ -1079,7 +1092,7 @@ NewPromiseCapability(JSContext* cx, HandleObject C, MutableHandleObject promise,
return false; return false;
// Step 7. // Step 7.
RootedValue resolveVal(cx, executor->getExtendedSlot(GetCapabilitiesExecutorSlots_Resolve)); const Value& resolveVal = executor->getExtendedSlot(GetCapabilitiesExecutorSlots_Resolve);
if (!IsCallable(resolveVal)) { if (!IsCallable(resolveVal)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE); JSMSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE);
@ -1087,7 +1100,7 @@ NewPromiseCapability(JSContext* cx, HandleObject C, MutableHandleObject promise,
} }
// Step 8. // Step 8.
RootedValue rejectVal(cx, executor->getExtendedSlot(GetCapabilitiesExecutorSlots_Reject)); const Value& rejectVal = executor->getExtendedSlot(GetCapabilitiesExecutorSlots_Reject);
if (!IsCallable(rejectVal)) { if (!IsCallable(rejectVal)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE); JSMSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE);
@ -1107,7 +1120,7 @@ static bool
GetCapabilitiesExecutor(JSContext* cx, unsigned argc, Value* vp) GetCapabilitiesExecutor(JSContext* cx, unsigned argc, Value* vp)
{ {
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
RootedFunction F(cx, &args.callee().as<JSFunction>()); JSFunction* F = &args.callee().as<JSFunction>();
// Steps 1-2 (implicit). // Steps 1-2 (implicit).
@ -1142,11 +1155,12 @@ RejectMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj, HandleValue re
if (!IsProxy(promiseObj)) { if (!IsProxy(promiseObj)) {
promise = &promiseObj->as<PromiseObject>(); promise = &promiseObj->as<PromiseObject>();
} else { } else {
if (JS_IsDeadWrapper(UncheckedUnwrap(promiseObj))) { JSObject* unwrappedPromiseObj = UncheckedUnwrap(promiseObj);
if (JS_IsDeadWrapper(unwrappedPromiseObj)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
return false; return false;
} }
promise = &UncheckedUnwrap(promiseObj)->as<PromiseObject>(); promise = &unwrappedPromiseObj->as<PromiseObject>();
ac.emplace(cx, promise); ac.emplace(cx, promise);
// The rejection reason might've been created in a compartment with higher // The rejection reason might've been created in a compartment with higher
@ -1156,7 +1170,7 @@ RejectMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj, HandleValue re
// avoid that situation, we synthesize a generic error that doesn't // avoid that situation, we synthesize a generic error that doesn't
// expose any privileged information but can safely be used in the // expose any privileged information but can safely be used in the
// rejection handler. // rejection handler.
if (!promise->compartment()->wrap(cx, &reason)) if (!cx->compartment()->wrap(cx, &reason))
return false; return false;
if (reason.isObject() && !CheckedUnwrap(&reason.toObject())) { if (reason.isObject() && !CheckedUnwrap(&reason.toObject())) {
// Async stacks are only properly adopted if there's at least one // Async stacks are only properly adopted if there's at least one
@ -1179,17 +1193,19 @@ TriggerPromiseReactions(JSContext* cx, HandleValue reactionsVal, JS::PromiseStat
MOZ_ASSERT(state == JS::PromiseState::Fulfilled || state == JS::PromiseState::Rejected); MOZ_ASSERT(state == JS::PromiseState::Fulfilled || state == JS::PromiseState::Rejected);
RootedObject reactions(cx, &reactionsVal.toObject()); RootedObject reactions(cx, &reactionsVal.toObject());
RootedObject reaction(cx);
if (reactions->is<PromiseReactionRecord>() || IsWrapper(reactions)) if (reactions->is<PromiseReactionRecord>() || IsWrapper(reactions))
return EnqueuePromiseReactionJob(cx, reactions, valueOrReason, state); return EnqueuePromiseReactionJob(cx, reactions, valueOrReason, state);
RootedNativeObject reactionsList(cx, &reactions->as<NativeObject>()); HandleNativeObject reactionsList = reactions.as<NativeObject>();
size_t reactionsCount = reactionsList->getDenseInitializedLength(); uint32_t reactionsCount = reactionsList->getDenseInitializedLength();
MOZ_ASSERT(reactionsCount > 1, "Reactions list should be created lazily"); MOZ_ASSERT(reactionsCount > 1, "Reactions list should be created lazily");
for (size_t i = 0; i < reactionsCount; i++) { RootedObject reaction(cx);
reaction = &reactionsList->getDenseElement(i).toObject(); for (uint32_t i = 0; i < reactionsCount; i++) {
const Value& reactionVal = reactionsList->getDenseElement(i);
MOZ_RELEASE_ASSERT(reactionVal.isObject());
reaction = &reactionVal.toObject();
if (!EnqueuePromiseReactionJob(cx, reaction, valueOrReason, state)) if (!EnqueuePromiseReactionJob(cx, reaction, valueOrReason, state))
return false; return false;
} }
@ -1223,7 +1239,7 @@ DefaultResolvingPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*>
if (reaction->targetState() == JS::PromiseState::Fulfilled) if (reaction->targetState() == JS::PromiseState::Fulfilled)
ok = ResolvePromiseInternal(cx, promiseToResolve, argument); ok = ResolvePromiseInternal(cx, promiseToResolve, argument);
else else
ok = RejectMaybeWrappedPromise(cx, promiseToResolve, argument); ok = RejectPromiseInternal(cx, promiseToResolve, argument);
if (!ok) { if (!ok) {
resolutionMode = RejectMode; resolutionMode = RejectMode;
@ -1233,9 +1249,9 @@ DefaultResolvingPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*>
} }
// Steps 7-9. // Steps 7-9.
size_t hookSlot = resolutionMode == RejectMode uint32_t hookSlot = resolutionMode == RejectMode
? ReactionRecordSlot_Reject ? ReactionRecordSlot_Reject
: ReactionRecordSlot_Resolve; : ReactionRecordSlot_Resolve;
RootedObject callee(cx, reaction->getFixedSlot(hookSlot).toObjectOrNull()); RootedObject callee(cx, reaction->getFixedSlot(hookSlot).toObjectOrNull());
RootedObject promiseObj(cx, reaction->promise()); RootedObject promiseObj(cx, reaction->promise());
if (!RunResolutionFunction(cx, callee, handlerResult, resolutionMode, promiseObj)) if (!RunResolutionFunction(cx, callee, handlerResult, resolutionMode, promiseObj))
@ -1364,7 +1380,7 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp)
} }
// Steps 1-2. // Steps 1-2.
Rooted<PromiseReactionRecord*> reaction(cx, &reactionObj->as<PromiseReactionRecord>()); Handle<PromiseReactionRecord*> reaction = reactionObj.as<PromiseReactionRecord>();
if (reaction->isDefaultResolvingHandler()) if (reaction->isDefaultResolvingHandler())
return DefaultResolvingPromiseReactionJob(cx, reaction, args.rval()); return DefaultResolvingPromiseReactionJob(cx, reaction, args.rval());
if (reaction->isAsyncFunction()) if (reaction->isAsyncFunction())
@ -1399,7 +1415,7 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp)
bool done = handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone; bool done = handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone;
// Async Iteration proposal 11.1.3.2.5 step 1. // Async Iteration proposal 11.1.3.2.5 step 1.
RootedObject resultObj(cx, CreateIterResultObject(cx, argument, done)); JSObject* resultObj = CreateIterResultObject(cx, argument, done);
if (!resultObj) if (!resultObj)
return false; return false;
@ -1420,9 +1436,9 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp)
} }
// Steps 7-9. // Steps 7-9.
size_t hookSlot = resolutionMode == RejectMode uint32_t hookSlot = resolutionMode == RejectMode
? ReactionRecordSlot_Reject ? ReactionRecordSlot_Reject
: ReactionRecordSlot_Resolve; : ReactionRecordSlot_Resolve;
RootedObject callee(cx, reaction->getFixedSlot(hookSlot).toObjectOrNull()); RootedObject callee(cx, reaction->getFixedSlot(hookSlot).toObjectOrNull());
RootedObject promiseObj(cx, reaction->promise()); RootedObject promiseObj(cx, reaction->promise());
if (!RunResolutionFunction(cx, callee, handlerResult, resolutionMode, promiseObj)) if (!RunResolutionFunction(cx, callee, handlerResult, resolutionMode, promiseObj))
@ -1540,7 +1556,7 @@ PromiseResolveBuiltinThenableJob(JSContext* cx, unsigned argc, Value* vp)
if (promise->as<PromiseObject>().state() != JS::PromiseState::Pending) if (promise->as<PromiseObject>().state() != JS::PromiseState::Pending)
return true; return true;
return RejectMaybeWrappedPromise(cx, promise, exception); return RejectPromiseInternal(cx, promise.as<PromiseObject>(), exception);
} }
/** /**
@ -1566,6 +1582,14 @@ EnqueuePromiseResolveThenableJob(JSContext* cx, HandleValue promiseToResolve_,
RootedObject then(cx, CheckedUnwrap(&thenVal.toObject())); RootedObject then(cx, CheckedUnwrap(&thenVal.toObject()));
AutoCompartment ac(cx, then); AutoCompartment ac(cx, then);
// Wrap the `promiseToResolve` and `thenable` arguments.
if (!cx->compartment()->wrap(cx, &promiseToResolve))
return false;
MOZ_ASSERT(thenable.isObject());
if (!cx->compartment()->wrap(cx, &thenable))
return false;
HandlePropertyName funName = cx->names().empty; HandlePropertyName funName = cx->names().empty;
RootedFunction job(cx, NewNativeFunction(cx, PromiseResolveThenableJob, 0, funName, RootedFunction job(cx, NewNativeFunction(cx, PromiseResolveThenableJob, 0, funName,
gc::AllocKind::FUNCTION_EXTENDED, GenericObject)); gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
@ -1579,29 +1603,21 @@ EnqueuePromiseResolveThenableJob(JSContext* cx, HandleValue promiseToResolve_,
// work. // work.
// The layout is described in the ThenableJobDataIndices enum. // The layout is described in the ThenableJobDataIndices enum.
RootedArrayObject data(cx, NewDenseFullyAllocatedArray(cx, ThenableJobDataLength)); RootedArrayObject data(cx, NewDenseFullyAllocatedArray(cx, ThenableJobDataLength));
if (!data || if (!data)
data->ensureDenseElements(cx, 0, ThenableJobDataLength) != DenseElementResult::Success)
{
return false; return false;
}
// Wrap and set the `promiseToResolve` argument. // Set the `promiseToResolve` and `thenable` arguments.
if (!cx->compartment()->wrap(cx, &promiseToResolve)) data->setDenseInitializedLength(ThenableJobDataLength);
return false; data->initDenseElement(ThenableJobDataIndex_Promise, promiseToResolve);
data->setDenseElement(ThenableJobDataIndex_Promise, promiseToResolve); data->initDenseElement(ThenableJobDataIndex_Thenable, thenable);
// 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(ThenableJobDataIndex_Thenable, thenable);
// Store the data array on the reaction job. // Store the data array on the reaction job.
job->setExtendedSlot(ThenableJobSlot_JobData, ObjectValue(*data)); job->setExtendedSlot(ThenableJobSlot_JobData, ObjectValue(*data));
// At this point the promise is guaranteed to be wrapped into the job's
// compartment.
RootedObject promise(cx, &promiseToResolve.toObject());
RootedObject incumbentGlobal(cx, cx->runtime()->getIncumbentGlobal(cx)); RootedObject incumbentGlobal(cx, cx->runtime()->getIncumbentGlobal(cx));
return cx->runtime()->enqueuePromiseJob(cx, job, promise, incumbentGlobal); return cx->runtime()->enqueuePromiseJob(cx, job, promise, incumbentGlobal);
} }
@ -1771,7 +1787,7 @@ PromiseConstructor(JSContext* cx, unsigned argc, Value* vp)
return false; return false;
// Step 2. // Step 2.
RootedValue executorVal(cx, args.get(0)); HandleValue executorVal = args.get(0);
if (!IsCallable(executorVal)) if (!IsCallable(executorVal))
return ReportIsNotFunction(cx, executorVal); return ReportIsNotFunction(cx, executorVal);
RootedObject executor(cx, &executorVal.toObject()); RootedObject executor(cx, &executorVal.toObject());
@ -1809,13 +1825,16 @@ PromiseConstructor(JSContext* cx, unsigned argc, Value* vp)
// Promises, with the unprivileged one resolved with the resolution of the // Promises, with the unprivileged one resolved with the resolution of the
// privileged one. // privileged one.
if (IsWrapper(newTarget)) { if (IsWrapper(newTarget)) {
newTarget = CheckedUnwrap(newTarget); JSObject* unwrappedNewTarget = CheckedUnwrap(newTarget);
MOZ_ASSERT(newTarget); MOZ_ASSERT(unwrappedNewTarget);
MOZ_ASSERT(newTarget != originalNewTarget); MOZ_ASSERT(unwrappedNewTarget != newTarget);
newTarget = unwrappedNewTarget;
{ {
AutoCompartment ac(cx, newTarget); AutoCompartment ac(cx, newTarget);
RootedObject promiseCtor(cx); Handle<GlobalObject*> global = cx->global();
if (!GetBuiltinConstructor(cx, JSProto_Promise, &promiseCtor)) JSFunction* promiseCtor = GlobalObject::getOrCreatePromiseConstructor(cx, global);
if (!promiseCtor)
return false; return false;
// Promise subclasses don't get the special Xray treatment, so // Promise subclasses don't get the special Xray treatment, so
@ -1857,8 +1876,10 @@ PromiseObject::create(JSContext* cx, HandleObject executor, HandleObject proto /
if (needsWrapping) { if (needsWrapping) {
MOZ_ASSERT(proto); MOZ_ASSERT(proto);
usedProto = CheckedUnwrap(proto); usedProto = CheckedUnwrap(proto);
if (!usedProto) if (!usedProto) {
/* ReportAccessDenied(cx); */
return nullptr; return nullptr;
}
} }
@ -1944,10 +1965,10 @@ static bool
Promise_static_all(JSContext* cx, unsigned argc, Value* vp) Promise_static_all(JSContext* cx, unsigned argc, Value* vp)
{ {
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
RootedValue iterable(cx, args.get(0)); HandleValue iterable = args.get(0);
// Step 2 (reordered). // Step 2 (reordered).
RootedValue CVal(cx, args.thisv()); HandleValue CVal = args.thisv();
if (!CVal.isObject()) { if (!CVal.isObject()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
"Receiver of Promise.all call"); "Receiver of Promise.all call");
@ -2098,8 +2119,7 @@ js::GetWaitForAllPromise(JSContext* cx, const JS::AutoObjectVector& promises)
RootedNativeObject valuesArray(cx, NewDenseFullyAllocatedArray(cx, promiseCount)); RootedNativeObject valuesArray(cx, NewDenseFullyAllocatedArray(cx, promiseCount));
if (!valuesArray) if (!valuesArray)
return nullptr; return nullptr;
if (valuesArray->ensureDenseElements(cx, 0, promiseCount) != DenseElementResult::Success) valuesArray->ensureDenseInitializedLength(cx, 0, promiseCount);
return nullptr;
// Sub-step 4. // Sub-step 4.
// Create our data holder that holds all the things shared across // Create our data holder that holds all the things shared across
@ -2112,7 +2132,6 @@ js::GetWaitForAllPromise(JSContext* cx, const JS::AutoObjectVector& promises)
resolve)); resolve));
if (!dataHolder) if (!dataHolder)
return nullptr; return nullptr;
RootedValue dataHolderVal(cx, ObjectValue(*dataHolder));
// Sub-step 5 (inline in loop-header below). // Sub-step 5 (inline in loop-header below).
@ -2137,7 +2156,8 @@ js::GetWaitForAllPromise(JSContext* cx, const JS::AutoObjectVector& promises)
return nullptr; return nullptr;
// Steps k-o. // Steps k-o.
resolveFunc->setExtendedSlot(PromiseAllResolveElementFunctionSlot_Data, dataHolderVal); resolveFunc->setExtendedSlot(PromiseAllResolveElementFunctionSlot_Data,
ObjectValue(*dataHolder));
resolveFunc->setExtendedSlot(PromiseAllResolveElementFunctionSlot_ElementIndex, resolveFunc->setExtendedSlot(PromiseAllResolveElementFunctionSlot_ElementIndex,
Int32Value(index)); Int32Value(index));
@ -2205,7 +2225,7 @@ RunResolutionFunction(JSContext *cx, HandleObject resolutionFun, HandleValue res
if (!promiseObj) if (!promiseObj)
return true; return true;
Rooted<PromiseObject*> promise(cx, &promiseObj->as<PromiseObject>()); Handle<PromiseObject*> promise = promiseObj.as<PromiseObject>();
if (promise->state() != JS::PromiseState::Pending) if (promise->state() != JS::PromiseState::Pending)
return true; return true;
@ -2215,7 +2235,7 @@ RunResolutionFunction(JSContext *cx, HandleObject resolutionFun, HandleValue res
if (mode == ResolveMode) if (mode == ResolveMode)
return ResolvePromiseInternal(cx, promise, result); return ResolvePromiseInternal(cx, promise, result);
return RejectMaybeWrappedPromise(cx, promiseObj, result); return RejectPromiseInternal(cx, promise, result);
} }
// ES2016, 25.4.4.1.1. // ES2016, 25.4.4.1.1.
@ -2226,12 +2246,6 @@ PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C,
{ {
*done = false; *done = false;
RootedObject unwrappedPromiseObj(cx);
if (IsWrapper(promiseObj)) {
unwrappedPromiseObj = CheckedUnwrap(promiseObj);
MOZ_ASSERT(unwrappedPromiseObj);
}
// Step 1. // Step 1.
MOZ_ASSERT(C->isConstructor()); MOZ_ASSERT(C->isConstructor());
RootedValue CVal(cx, ObjectValue(*C)); RootedValue CVal(cx, ObjectValue(*C));
@ -2259,8 +2273,11 @@ PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C,
// canonical "then" function and content can't see our // canonical "then" function and content can't see our
// PromiseAllResolveElement. // PromiseAllResolveElement.
RootedObject valuesArray(cx); RootedObject valuesArray(cx);
if (unwrappedPromiseObj) { if (IsWrapper(promiseObj)) {
JSAutoCompartment ac(cx, unwrappedPromiseObj); JSObject* unwrappedPromiseObj = CheckedUnwrap(promiseObj);
MOZ_ASSERT(unwrappedPromiseObj);
AutoCompartment ac(cx, unwrappedPromiseObj);
valuesArray = NewDenseFullyAllocatedArray(cx, 0); valuesArray = NewDenseFullyAllocatedArray(cx, 0);
} else { } else {
valuesArray = NewDenseFullyAllocatedArray(cx, 0); valuesArray = NewDenseFullyAllocatedArray(cx, 0);
@ -2281,15 +2298,17 @@ PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C,
valuesArrayVal, resolve)); valuesArrayVal, resolve));
if (!dataHolder) if (!dataHolder)
return false; return false;
RootedValue dataHolderVal(cx, ObjectValue(*dataHolder));
// Step 5. // Step 5.
uint32_t index = 0; uint32_t index = 0;
// Step 6. // Step 6.
RootedValue nextValue(cx); RootedValue nextValue(cx);
RootedValue nextPromise(cx);
RootedId indexId(cx); RootedId indexId(cx);
RootedValue rejectFunVal(cx, ObjectValue(*reject)); RootedValue rejectFunVal(cx, ObjectValue(*reject));
RootedValue resolveFunVal(cx);
RootedValue staticResolve(cx);
while (true) { while (true) {
// Steps a-c, e-g. // Steps a-c, e-g.
@ -2319,10 +2338,10 @@ PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C,
} }
// Step h. // Step h.
{ // Scope for the JSAutoCompartment we need to work with valuesArray. We { // Scope for the AutoCompartment we need to work with valuesArray. We
// mostly do this for performance; we could go ahead and do the define via // mostly do this for performance; we could go ahead and do the define via
// a cross-compartment proxy instead... // a cross-compartment proxy instead...
JSAutoCompartment ac(cx, valuesArray); AutoCompartment ac(cx, valuesArray);
indexId = INT_TO_JSID(index); indexId = INT_TO_JSID(index);
if (!DefineProperty(cx, valuesArray, indexId, UndefinedHandleValue)) if (!DefineProperty(cx, valuesArray, indexId, UndefinedHandleValue))
return false; return false;
@ -2331,9 +2350,7 @@ PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C,
// Step i. // Step i.
// Sadly, because someone could have overridden // Sadly, because someone could have overridden
// "resolve" on the canonical Promise constructor. // "resolve" on the canonical Promise constructor.
RootedValue nextPromise(cx); if (!GetProperty(cx, CVal, cx->names().resolve, &staticResolve))
RootedValue staticResolve(cx);
if (!GetProperty(cx, C, CVal, cx->names().resolve, &staticResolve))
return false; return false;
FixedInvokeArgs<1> resolveArgs(cx); FixedInvokeArgs<1> resolveArgs(cx);
@ -2342,15 +2359,15 @@ PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C,
return false; return false;
// Step j. // Step j.
RootedFunction resolveFunc(cx, NewNativeFunction(cx, PromiseAllResolveElementFunction, JSFunction* resolveFunc = NewNativeFunction(cx, PromiseAllResolveElementFunction, 1,
1, nullptr, nullptr,gc::AllocKind::FUNCTION_EXTENDED,
gc::AllocKind::FUNCTION_EXTENDED, GenericObject);
GenericObject));
if (!resolveFunc) if (!resolveFunc)
return false; return false;
// Steps k,m,n. // Steps k,m,n.
resolveFunc->setExtendedSlot(PromiseAllResolveElementFunctionSlot_Data, dataHolderVal); resolveFunc->setExtendedSlot(PromiseAllResolveElementFunctionSlot_Data,
ObjectValue(*dataHolder));
// Step l. // Step l.
resolveFunc->setExtendedSlot(PromiseAllResolveElementFunctionSlot_ElementIndex, resolveFunc->setExtendedSlot(PromiseAllResolveElementFunctionSlot_ElementIndex,
@ -2360,7 +2377,7 @@ PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C,
dataHolder->increaseRemainingCount(); dataHolder->increaseRemainingCount();
// Step q. // Step q.
RootedValue resolveFunVal(cx, ObjectValue(*resolveFunc)); resolveFunVal.setObject(*resolveFunc);
if (!BlockOnPromise(cx, nextPromise, promiseObj, resolveFunVal, rejectFunVal, true)) if (!BlockOnPromise(cx, nextPromise, promiseObj, resolveFunVal, rejectFunVal, true))
return false; return false;
@ -2490,11 +2507,11 @@ PromiseAllResolveElementFunction(JSContext* cx, unsigned argc, Value* vp)
{ {
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
RootedFunction resolve(cx, &args.callee().as<JSFunction>()); JSFunction* resolve = &args.callee().as<JSFunction>();
RootedValue xVal(cx, args.get(0)); RootedValue xVal(cx, args.get(0));
// Step 1. // Step 1.
RootedValue dataVal(cx, resolve->getExtendedSlot(PromiseAllResolveElementFunctionSlot_Data)); const Value& dataVal = resolve->getExtendedSlot(PromiseAllResolveElementFunctionSlot_Data);
// Step 2. // Step 2.
// We use the existence of the data holder as a signal for whether the // We use the existence of the data holder as a signal for whether the
@ -2517,24 +2534,27 @@ PromiseAllResolveElementFunction(JSContext* cx, unsigned argc, Value* vp)
// Step 5. // Step 5.
RootedValue valuesVal(cx, data->valuesArray()); RootedValue valuesVal(cx, data->valuesArray());
RootedObject valuesObj(cx, &valuesVal.toObject()); RootedObject valuesObj(cx, &valuesVal.toObject());
bool valuesListIsWrapped = false; if (IsProxy(valuesObj)) {
if (IsWrapper(valuesObj)) {
valuesListIsWrapped = true;
// See comment for PerformPromiseAll, step 3 for why we unwrap here. // See comment for PerformPromiseAll, step 3 for why we unwrap here.
valuesObj = UncheckedUnwrap(valuesObj); valuesObj = UncheckedUnwrap(valuesObj);
if (JS_IsDeadWrapper(valuesObj)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
return false;
}
AutoCompartment ac(cx, valuesObj);
if (!cx->compartment()->wrap(cx, &xVal))
return false;
} }
RootedNativeObject values(cx, &valuesObj->as<NativeObject>()); HandleNativeObject values = valuesObj.as<NativeObject>();
// Step 6 (moved under step 10). // Step 6 (moved under step 10).
// Step 7 (moved to step 9). // Step 7 (moved to step 9).
// Step 8. // Step 8.
// The index is guaranteed to be initialized to `undefined`. // The index is guaranteed to be initialized to `undefined`.
if (valuesListIsWrapped) { MOZ_ASSERT(values->getDenseElement(index).isUndefined());
AutoCompartment ac(cx, values);
if (!cx->compartment()->wrap(cx, &xVal))
return false;
}
values->setDenseElement(index, xVal); values->setDenseElement(index, xVal);
// Steps 7,9. // Steps 7,9.
@ -2581,20 +2601,23 @@ PromiseAllSettledResolveElementFunction(JSContext* cx, unsigned argc, Value* vp)
RootedValue valuesVal(cx, data->valuesArray()); RootedValue valuesVal(cx, data->valuesArray());
RootedObject valuesObj(cx, &valuesVal.toObject()); RootedObject valuesObj(cx, &valuesVal.toObject());
bool valuesListIsWrapped = false; if (IsProxy(valuesObj)) {
if (IsWrapper(valuesObj)) {
valuesListIsWrapped = true;
// See comment for PerformPromiseAll, step 3 for why we unwrap here. // See comment for PerformPromiseAll, step 3 for why we unwrap here.
valuesObj = UncheckedUnwrap(valuesObj); valuesObj = UncheckedUnwrap(valuesObj);
if (JS_IsDeadWrapper(valuesObj)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
return false;
}
AutoCompartment ac(cx, valuesObj);
if (!cx->compartment()->wrap(cx, &xVal))
return false;
} }
NativeObject* values = &valuesObj->as<NativeObject>(); NativeObject* values = &valuesObj->as<NativeObject>();
// The index is guaranteed to be initialized to `undefined`. // The index is guaranteed to be initialized to `undefined`.
if (valuesListIsWrapped) { MOZ_ASSERT(values->getDenseElement(index).isUndefined());
AutoCompartment ac(cx, values);
if (!cx->compartment()->wrap(cx, &xVal))
return false;
}
RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx)); RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
if (!obj) { if (!obj) {
@ -2656,20 +2679,23 @@ PromiseAllSettledRejectElementFunction(JSContext* cx, unsigned argc, Value* vp)
RootedValue valuesVal(cx, data->valuesArray()); RootedValue valuesVal(cx, data->valuesArray());
RootedObject valuesObj(cx, &valuesVal.toObject()); RootedObject valuesObj(cx, &valuesVal.toObject());
bool valuesListIsWrapped = false; if (IsProxy(valuesObj)) {
if (IsWrapper(valuesObj)) {
valuesListIsWrapped = true;
// See comment for PerformPromiseAll, step 3 for why we unwrap here. // See comment for PerformPromiseAll, step 3 for why we unwrap here.
valuesObj = UncheckedUnwrap(valuesObj); valuesObj = UncheckedUnwrap(valuesObj);
if (JS_IsDeadWrapper(valuesObj)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
return false;
}
AutoCompartment ac(cx, valuesObj);
if (!cx->compartment()->wrap(cx, &xVal))
return false;
} }
NativeObject* values = &valuesObj->as<NativeObject>(); NativeObject* values = &valuesObj->as<NativeObject>();
// The index is guaranteed to be initialized to `undefined`. // The index is guaranteed to be initialized to `undefined`.
if (valuesListIsWrapped) { MOZ_ASSERT(values->getDenseElement(index).isUndefined());
AutoCompartment ac(cx, values);
if (!cx->compartment()->wrap(cx, &xVal))
return false;
}
RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx)); RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
if (!obj) { if (!obj) {
@ -2720,10 +2746,10 @@ static bool
Promise_static_race(JSContext* cx, unsigned argc, Value* vp) Promise_static_race(JSContext* cx, unsigned argc, Value* vp)
{ {
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
RootedValue iterable(cx, args.get(0)); HandleValue iterable = args.get(0);
// Step 2 (reordered). // Step 2 (reordered).
RootedValue CVal(cx, args.thisv()); HandleValue CVal = args.thisv();
if (!CVal.isObject()) { if (!CVal.isObject()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
"Receiver of Promise.race call"); "Receiver of Promise.race call");
@ -2862,7 +2888,7 @@ CommonStaticResolveRejectImpl(JSContext* cx, HandleValue thisVal, HandleValue ar
// outcome, so instead of unwrapping and then performing the // outcome, so instead of unwrapping and then performing the
// GetProperty, just check here and then operate on the original // GetProperty, just check here and then operate on the original
// object again. // object again.
RootedObject unwrappedObject(cx, CheckedUnwrap(xObj)); JSObject* unwrappedObject = CheckedUnwrap(xObj);
if (unwrappedObject && unwrappedObject->is<PromiseObject>()) if (unwrappedObject && unwrappedObject->is<PromiseObject>())
isPromise = true; isPromise = true;
} }
@ -2907,8 +2933,8 @@ static bool
Promise_reject(JSContext* cx, unsigned argc, Value* vp) Promise_reject(JSContext* cx, unsigned argc, Value* vp)
{ {
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
RootedValue thisVal(cx, args.thisv()); HandleValue thisVal = args.thisv();
RootedValue argVal(cx, args.get(0)); HandleValue argVal = args.get(0);
JSObject* result = CommonStaticResolveRejectImpl(cx, thisVal, argVal, RejectMode); JSObject* result = CommonStaticResolveRejectImpl(cx, thisVal, argVal, RejectMode);
if (!result) if (!result)
return false; return false;
@ -2922,7 +2948,7 @@ Promise_reject(JSContext* cx, unsigned argc, Value* vp)
/* static */ JSObject* /* static */ JSObject*
PromiseObject::unforgeableReject(JSContext* cx, HandleValue value) PromiseObject::unforgeableReject(JSContext* cx, HandleValue value)
{ {
RootedObject promiseCtor(cx, JS::GetPromiseConstructor(cx)); JSObject* promiseCtor = JS::GetPromiseConstructor(cx);
if (!promiseCtor) if (!promiseCtor)
return nullptr; return nullptr;
RootedValue cVal(cx, ObjectValue(*promiseCtor)); RootedValue cVal(cx, ObjectValue(*promiseCtor));
@ -2936,8 +2962,8 @@ static bool
Promise_static_resolve(JSContext* cx, unsigned argc, Value* vp) Promise_static_resolve(JSContext* cx, unsigned argc, Value* vp)
{ {
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
RootedValue thisVal(cx, args.thisv()); HandleValue thisVal = args.thisv();
RootedValue argVal(cx, args.get(0)); HandleValue argVal = args.get(0);
JSObject* result = CommonStaticResolveRejectImpl(cx, thisVal, argVal, ResolveMode); JSObject* result = CommonStaticResolveRejectImpl(cx, thisVal, argVal, ResolveMode);
if (!result) if (!result)
return false; return false;
@ -2951,7 +2977,7 @@ Promise_static_resolve(JSContext* cx, unsigned argc, Value* vp)
/* static */ JSObject* /* static */ JSObject*
PromiseObject::unforgeableResolve(JSContext* cx, HandleValue value) PromiseObject::unforgeableResolve(JSContext* cx, HandleValue value)
{ {
RootedObject promiseCtor(cx, JS::GetPromiseConstructor(cx)); JSObject* promiseCtor = JS::GetPromiseConstructor(cx);
if (!promiseCtor) if (!promiseCtor)
return nullptr; return nullptr;
RootedValue cVal(cx, ObjectValue(*promiseCtor)); RootedValue cVal(cx, ObjectValue(*promiseCtor));
@ -3012,7 +3038,7 @@ NewReactionRecord(JSContext* cx, HandleObject resultPromise, HandleValue onFulfi
return nullptr; return nullptr;
} }
Rooted<PromiseReactionRecord*> reaction(cx, NewObjectWithClassProto<PromiseReactionRecord>(cx)); PromiseReactionRecord* reaction = NewObjectWithClassProto<PromiseReactionRecord>(cx);
if (!reaction) if (!reaction)
return nullptr; return nullptr;
@ -3153,7 +3179,7 @@ js::AsyncFunctionThrown(JSContext* cx, Handle<PromiseObject*> resultPromise)
if (!MaybeGetAndClearException(cx, &exc)) if (!MaybeGetAndClearException(cx, &exc))
return false; return false;
if (!RejectMaybeWrappedPromise(cx, resultPromise, exc)) if (!RejectPromiseInternal(cx, resultPromise, exc))
return false; return false;
// Step 3.g. // Step 3.g.
@ -3244,10 +3270,10 @@ bool
js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind completionKind) js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind completionKind)
{ {
// Step 1. // Step 1.
RootedValue thisVal(cx, args.thisv()); HandleValue thisVal = args.thisv();
// Step 2. // Step 2.
RootedObject resultPromise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); Rooted<PromiseObject*> resultPromise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
if (!resultPromise) if (!resultPromise)
return false; return false;
@ -3259,7 +3285,7 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co
return false; return false;
// Step 3.b. // Step 3.b.
if (!RejectMaybeWrappedPromise(cx, resultPromise, badGeneratorError)) if (!RejectPromiseInternal(cx, resultPromise, badGeneratorError))
return false; return false;
// Step 3.c. // Step 3.c.
@ -3273,7 +3299,6 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co
// Step 4. // Step 4.
RootedObject iter(cx, asyncIter->iterator()); RootedObject iter(cx, asyncIter->iterator());
RootedValue resultVal(cx);
RootedValue func(cx); RootedValue func(cx);
if (completionKind == CompletionKind::Normal) { if (completionKind == CompletionKind::Normal) {
// 11.1.3.2.1 steps 5-6 (partially). // 11.1.3.2.1 steps 5-6 (partially).
@ -3287,7 +3312,7 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co
// Step 7. // Step 7.
if (func.isNullOrUndefined()) { if (func.isNullOrUndefined()) {
// Step 7.a. // Step 7.a.
RootedObject resultObj(cx, CreateIterResultObject(cx, args.get(0), true)); JSObject* resultObj = CreateIterResultObject(cx, args.get(0), true);
if (!resultObj) if (!resultObj)
return AbruptRejectPromise(cx, args, resultPromise, nullptr); return AbruptRejectPromise(cx, args, resultPromise, nullptr);
@ -3310,7 +3335,7 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co
// Step 7. // Step 7.
if (func.isNullOrUndefined()) { if (func.isNullOrUndefined()) {
// Step 7.a. // Step 7.a.
if (!RejectMaybeWrappedPromise(cx, resultPromise, args.get(0))) if (!RejectPromiseInternal(cx, resultPromise, args.get(0)))
return AbruptRejectPromise(cx, args, resultPromise, nullptr); return AbruptRejectPromise(cx, args, resultPromise, nullptr);
// Step 7.b. // Step 7.b.
@ -3324,6 +3349,8 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co
RootedValue iterVal(cx, ObjectValue(*iter)); RootedValue iterVal(cx, ObjectValue(*iter));
FixedInvokeArgs<1> args2(cx); FixedInvokeArgs<1> args2(cx);
args2[0].set(args.get(0)); args2[0].set(args.get(0));
RootedValue resultVal(cx);
if (!js::Call(cx, func, iterVal, args2, &resultVal)) if (!js::Call(cx, func, iterVal, args2, &resultVal))
return AbruptRejectPromise(cx, args, resultPromise, nullptr); return AbruptRejectPromise(cx, args, resultPromise, nullptr);
@ -3428,18 +3455,18 @@ AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenOb
MOZ_ASSERT(!asyncGenObj->isQueueEmpty()); MOZ_ASSERT(!asyncGenObj->isQueueEmpty());
// Step 4. // Step 4.
Rooted<AsyncGeneratorRequest*> request( AsyncGeneratorRequest* request =
cx, AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj)); AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj);
if (!request) if (!request)
return false; return false;
// Step 5. // Step 5.
RootedObject resultPromise(cx, request->promise()); Rooted<PromiseObject*> resultPromise(cx, request->promise());
asyncGenObj->cacheRequest(request); asyncGenObj->cacheRequest(request);
// Step 6. // Step 6.
if (!RejectMaybeWrappedPromise(cx, resultPromise, exception)) if (!RejectPromiseInternal(cx, resultPromise, exception))
return false; return false;
// Steps 7-8. // Steps 7-8.
@ -3455,18 +3482,18 @@ AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenOb
MOZ_ASSERT(!asyncGenObj->isQueueEmpty()); MOZ_ASSERT(!asyncGenObj->isQueueEmpty());
// Step 4. // Step 4.
Rooted<AsyncGeneratorRequest*> request( AsyncGeneratorRequest* request =
cx, AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj)); AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj);
if (!request) if (!request)
return false; return false;
// Step 5. // Step 5.
RootedObject resultPromise(cx, request->promise()); Rooted<PromiseObject*> resultPromise(cx, request->promise());
asyncGenObj->cacheRequest(request); asyncGenObj->cacheRequest(request);
// Step 6. // Step 6.
RootedObject resultObj(cx, CreateIterResultObject(cx, value, done)); JSObject* resultObj = CreateIterResultObject(cx, value, done);
if (!resultObj) if (!resultObj)
return false; return false;
@ -3594,7 +3621,7 @@ js::AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal,
// Step 1 (implicit). // Step 1 (implicit).
// Step 2. // Step 2.
RootedObject resultPromise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); Rooted<PromiseObject*> resultPromise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
if (!resultPromise) if (!resultPromise)
return false; return false;
@ -3606,7 +3633,7 @@ js::AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal,
return false; return false;
// Step 3.b. // Step 3.b.
if (!RejectMaybeWrappedPromise(cx, resultPromise, badGeneratorError)) if (!RejectPromiseInternal(cx, resultPromise, badGeneratorError))
return false; return false;
// Step 3.c. // Step 3.c.
@ -3716,11 +3743,10 @@ Promise_then_impl(JSContext* cx, HandleValue promiseVal, HandleValue onFulfilled
RootedObject promiseObj(cx, &promiseVal.toObject()); RootedObject promiseObj(cx, &promiseVal.toObject());
Rooted<PromiseObject*> promise(cx); Rooted<PromiseObject*> promise(cx);
bool isPromise = promiseObj->is<PromiseObject>(); if (promiseObj->is<PromiseObject>()) {
if (isPromise) {
promise = &promiseObj->as<PromiseObject>(); promise = &promiseObj->as<PromiseObject>();
} else { } else {
RootedObject unwrappedPromiseObj(cx, CheckedUnwrap(promiseObj)); JSObject* unwrappedPromiseObj = CheckedUnwrap(promiseObj);
if (!unwrappedPromiseObj) { if (!unwrappedPromiseObj) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
return false; return false;
@ -3934,7 +3960,7 @@ BlockOnPromise(JSContext* cx, HandleValue promiseVal, HandleObject blockedPromis
} }
// 25.4.5.3., step 5. // 25.4.5.3., step 5.
Rooted<PromiseObject*> promise(cx, &promiseObj->as<PromiseObject>()); Handle<PromiseObject*> promise = promiseObj.as<PromiseObject>();
if (!PerformPromiseThen(cx, promise, onFulfilled, onRejected, resultPromise, if (!PerformPromiseThen(cx, promise, onFulfilled, onRejected, resultPromise,
resolveFun, rejectFun)) resolveFun, rejectFun))
{ {
@ -3993,7 +4019,7 @@ BlockOnPromise(JSContext* cx, HandleValue promiseVal, HandleObject blockedPromis
if (!blockedPromise_->is<PromiseObject>()) if (!blockedPromise_->is<PromiseObject>())
return true; return true;
Rooted<PromiseObject*> promise(cx, &unwrappedPromiseObj->as<PromiseObject>()); Handle<PromiseObject*> promise = unwrappedPromiseObj.as<PromiseObject>();
return AddDummyPromiseReactionForDebugger(cx, promise, blockedPromise); return AddDummyPromiseReactionForDebugger(cx, promise, blockedPromise);
} }
@ -4017,7 +4043,6 @@ AddPromiseReaction(JSContext* cx, Handle<PromiseObject*> promise,
// 25.4.5.3.1 steps 7.a,b. // 25.4.5.3.1 steps 7.a,b.
RootedValue reactionsVal(cx, promise->reactions()); RootedValue reactionsVal(cx, promise->reactions());
RootedNativeObject reactions(cx);
if (reactionsVal.isUndefined()) { if (reactionsVal.isUndefined()) {
// If no reactions existed so far, just store the reaction record directly. // If no reactions existed so far, just store the reaction record directly.
@ -4042,22 +4067,24 @@ AddPromiseReaction(JSContext* cx, Handle<PromiseObject*> promise,
if (reactionsObj->is<PromiseReactionRecord>()) { if (reactionsObj->is<PromiseReactionRecord>()) {
// If a single reaction existed so far, create a list and store the // If a single reaction existed so far, create a list and store the
// old and the new reaction in it. // old and the new reaction in it.
reactions = NewDenseFullyAllocatedArray(cx, 2); ArrayObject* reactions = NewDenseFullyAllocatedArray(cx, 2);
if (!reactions) if (!reactions)
return false; return false;
if (reactions->ensureDenseElements(cx, 0, 2) != DenseElementResult::Success)
return false;
reactions->setDenseElement(0, reactionsVal); reactions->setDenseInitializedLength(2);
reactions->setDenseElement(1, reactionVal); reactions->initDenseElement(0, reactionsVal);
reactions->initDenseElement(1, reactionVal);
promise->setFixedSlot(PromiseSlot_ReactionsOrResult, ObjectValue(*reactions)); promise->setFixedSlot(PromiseSlot_ReactionsOrResult, ObjectValue(*reactions));
} else { } else {
// Otherwise, just store the new reaction. // Otherwise, just store the new reaction.
reactions = &reactionsObj->as<NativeObject>(); HandleNativeObject reactions = reactionsObj.as<NativeObject>();
uint32_t len = reactions->getDenseInitializedLength(); uint32_t len = reactions->getDenseInitializedLength();
if (reactions->ensureDenseElements(cx, 0, len + 1) != DenseElementResult::Success) DenseElementResult result = reactions->ensureDenseElements(cx, len, 1);
if (result != DenseElementResult::Success) {
MOZ_ASSERT(result == DenseElementResult::Failure);
return false; return false;
}
reactions->setDenseElement(len, reactionVal); reactions->setDenseElement(len, reactionVal);
} }
@ -4137,10 +4164,10 @@ PromiseObject::dependentPromises(JSContext* cx, MutableHandle<GCVector<Value>> v
uint32_t len = reactions->getDenseInitializedLength(); uint32_t len = reactions->getDenseInitializedLength();
MOZ_ASSERT(len >= 2); MOZ_ASSERT(len >= 2);
size_t valuesIndex = 0; uint32_t valuesIndex = 0;
Rooted<PromiseReactionRecord*> reaction(cx); for (uint32_t i = 0; i < len; i++) {
for (size_t i = 0; i < len; i++) { const Value& element = reactions->getDenseElement(i);
reaction = &reactions->getDenseElement(i).toObject().as<PromiseReactionRecord>(); PromiseReactionRecord* reaction = &element.toObject().as<PromiseReactionRecord>();
// Not all reactions have a Promise on them. // Not all reactions have a Promise on them.
RootedObject promiseObj(cx, reaction->promise()); RootedObject promiseObj(cx, reaction->promise());
@ -4165,7 +4192,7 @@ PromiseObject::resolve(JSContext* cx, Handle<PromiseObject*> promise, HandleValu
if (PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_RESOLVING_FUNCTIONS)) if (PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_RESOLVING_FUNCTIONS))
return ResolvePromiseInternal(cx, promise, resolutionValue); return ResolvePromiseInternal(cx, promise, resolutionValue);
RootedObject resolveFun(cx, GetResolveFunctionFromPromise(promise)); JSFunction* resolveFun = GetResolveFunctionFromPromise(promise);
if (!resolveFun) if (!resolveFun)
return true; return true;

View file

@ -319,7 +319,7 @@ AsyncGeneratorObject::create(JSContext* cx, HandleFunction asyncGen, HandleValue
/* static */ AsyncGeneratorRequest* /* static */ AsyncGeneratorRequest*
AsyncGeneratorObject::createRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, AsyncGeneratorObject::createRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
CompletionKind completionKind, HandleValue completionValue, CompletionKind completionKind, HandleValue completionValue,
HandleObject promise) Handle<PromiseObject*> promise)
{ {
if (!asyncGenObj->hasCachedRequest()) if (!asyncGenObj->hasCachedRequest())
return AsyncGeneratorRequest::create(cx, completionKind, completionValue, promise); return AsyncGeneratorRequest::create(cx, completionKind, completionValue, promise);
@ -444,7 +444,7 @@ const Class AsyncGeneratorRequest::class_ = {
// Async Iteration proposal 11.4.3.1. // Async Iteration proposal 11.4.3.1.
/* static */ AsyncGeneratorRequest* /* static */ AsyncGeneratorRequest*
AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind, AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind,
HandleValue completionValue, HandleObject promise) HandleValue completionValue, Handle<PromiseObject*> promise)
{ {
RootedObject obj(cx, NewNativeObjectWithGivenProto(cx, &class_, nullptr)); RootedObject obj(cx, NewNativeObjectWithGivenProto(cx, &class_, nullptr));
if (!obj) if (!obj)

View file

@ -62,8 +62,7 @@ class AsyncGeneratorRequest : public NativeObject
Slots, Slots,
}; };
void init(CompletionKind completionKind, HandleValue completionValue, void init(CompletionKind completionKind, const Value& completionValue, PromiseObject* promise) {
HandleObject promise) {
setFixedSlot(Slot_CompletionKind, setFixedSlot(Slot_CompletionKind,
Int32Value(static_cast<int32_t>(completionKind))); Int32Value(static_cast<int32_t>(completionKind)));
setFixedSlot(Slot_CompletionValue, completionValue); setFixedSlot(Slot_CompletionValue, completionValue);
@ -81,7 +80,8 @@ class AsyncGeneratorRequest : public NativeObject
static const Class class_; static const Class class_;
static AsyncGeneratorRequest* create(JSContext* cx, CompletionKind completionKind, static AsyncGeneratorRequest* create(JSContext* cx, CompletionKind completionKind,
HandleValue completionValue, HandleObject promise); HandleValue completionValue,
Handle<PromiseObject*> promise);
CompletionKind completionKind() const { CompletionKind completionKind() const {
return static_cast<CompletionKind>(getFixedSlot(Slot_CompletionKind).toInt32()); return static_cast<CompletionKind>(getFixedSlot(Slot_CompletionKind).toInt32());
@ -89,8 +89,8 @@ class AsyncGeneratorRequest : public NativeObject
JS::Value completionValue() const { JS::Value completionValue() const {
return getFixedSlot(Slot_CompletionValue); return getFixedSlot(Slot_CompletionValue);
} }
JSObject* promise() const { PromiseObject* promise() const {
return &getFixedSlot(Slot_Promise).toObject(); return &getFixedSlot(Slot_Promise).toObject().as<PromiseObject>();
} }
}; };
@ -232,7 +232,7 @@ class AsyncGeneratorObject : public NativeObject
Handle<AsyncGeneratorObject*> asyncGenObj, Handle<AsyncGeneratorObject*> asyncGenObj,
CompletionKind completionKind, CompletionKind completionKind,
HandleValue completionValue, HandleValue completionValue,
HandleObject promise); Handle<PromiseObject*> promise);
// Stores the given request to the generator's cache after clearing its data // Stores the given request to the generator's cache after clearing its data
// slots. The cached request will be reused in the subsequent createRequest // slots. The cached request will be reused in the subsequent createRequest