diff options
-rw-r--r-- | js/src/builtin/Promise.cpp | 93 |
1 files changed, 41 insertions, 52 deletions
diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index 1d068f8c6..18a20ccab 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -31,10 +31,12 @@ MillisecondsSinceStartup() return (now - mozilla::TimeStamp::ProcessCreation(ignored)).ToMilliseconds(); } -#define PROMISE_HANDLER_IDENTITY 0 -#define PROMISE_HANDLER_THROWER 1 -#define PROMISE_HANDLER_AWAIT_FULFILLED 2 -#define PROMISE_HANDLER_AWAIT_REJECTED 3 +enum PromiseHandler { + PromiseHandlerIdentity = 0, + PromiseHandlerThrower, + PromiseHandlerAwaitFulfilled, + PromiseHandlerAwaitRejected, +}; enum ResolutionMode { ResolveMode, @@ -47,9 +49,8 @@ enum ResolveFunctionSlots { }; enum RejectFunctionSlots { - RejectFunctionSlot_Promise = ResolveFunctionSlot_Promise, + RejectFunctionSlot_Promise = 0, RejectFunctionSlot_ResolveFunction, - RejectFunctionSlot_PromiseAllData = RejectFunctionSlot_ResolveFunction, }; enum PromiseAllResolveElementFunctionSlots { @@ -86,7 +87,7 @@ class PromiseAllDataHolder : public NativeObject static const Class class_; JSObject* promiseObj() { return &getFixedSlot(PromiseAllDataHolderSlot_Promise).toObject(); } JSObject* resolveObj() { - return getFixedSlot(PromiseAllDataHolderSlot_ResolveFunction).toObjectOrNull(); + return &getFixedSlot(PromiseAllDataHolderSlot_ResolveFunction).toObject(); } Value valuesArray() { return getFixedSlot(PromiseAllDataHolderSlot_ValuesArray); } int32_t remainingCount() { @@ -126,7 +127,7 @@ NewPromiseAllDataHolder(JSContext* cx, HandleObject resultPromise, HandleValue v dataHolder->setFixedSlot(PromiseAllDataHolderSlot_Promise, ObjectValue(*resultPromise)); dataHolder->setFixedSlot(PromiseAllDataHolderSlot_RemainingElements, Int32Value(1)); dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ValuesArray, valuesArray); - dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ResolveFunction, ObjectOrNullValue(resolve)); + dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ResolveFunction, ObjectValue(*resolve)); return dataHolder; } @@ -277,12 +278,6 @@ CreateResolvingFunctions(JSContext* cx, HandleValue promise, if (!reject) return false; - // TODO: remove this once all self-hosted promise code is gone. - // The resolving functions are trusted, so self-hosted code should be able - // to call them using callFunction instead of callContentFunction. - resolve->setFlags(resolve->flags() | JSFunction::SELF_HOSTED); - reject->setFlags(reject->flags() | JSFunction::SELF_HOSTED); - resolve->setExtendedSlot(ResolveFunctionSlot_Promise, promise); resolve->setExtendedSlot(ResolveFunctionSlot_RejectFunction, ObjectValue(*reject)); @@ -402,10 +397,9 @@ ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp) RootedValue promiseVal(cx, resolve->getExtendedSlot(ResolveFunctionSlot_Promise)); // Steps 3-4. - // We use the reference to the reject function as a signal for whether - // the resolve or reject function was already called, at which point - // the references on each of the functions are cleared. - if (!resolve->getExtendedSlot(ResolveFunctionSlot_RejectFunction).isObject()) { + // If the Promise isn't available anymore, it has been rejected and the + // reference to it removed to make it eligible for collection. + if (promiseVal.isUndefined()) { args.rval().setUndefined(); return true; } @@ -843,12 +837,12 @@ AwaitPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction, RootedValue generatorVal(cx, resultPromise->getFixedSlot(PromiseSlot_AwaitGenerator)); int32_t handlerNum = int32_t(handlerVal.toNumber()); - MOZ_ASSERT(handlerNum == PROMISE_HANDLER_AWAIT_FULFILLED - || handlerNum == PROMISE_HANDLER_AWAIT_REJECTED); + MOZ_ASSERT(handlerNum == PromiseHandlerAwaitFulfilled + || handlerNum == PromiseHandlerAwaitRejected); // Await's handlers don't return a value, nor throw exception. // They fail only on OOM. - if (handlerNum == PROMISE_HANDLER_AWAIT_FULFILLED) { + if (handlerNum == PromiseHandlerAwaitFulfilled) { if (!AsyncFunctionAwaitedFulfilled(cx, resultPromise, generatorVal, argument)) return false; } else { @@ -919,11 +913,11 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp) int32_t handlerNum = int32_t(handlerVal.toNumber()); // Step 4. - if (handlerNum == PROMISE_HANDLER_IDENTITY) { + if (handlerNum == PromiseHandlerIdentity) { handlerResult = argument; } else { // Step 5. - MOZ_ASSERT(handlerNum == PROMISE_HANDLER_THROWER); + MOZ_ASSERT(handlerNum == PromiseHandlerThrower); resolutionMode = RejectMode; handlerResult = argument; } @@ -964,8 +958,8 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp) * * 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, - * PROMISE_HANDLER_THROWER, or a callable. In the + * This can be PromiseHandlerIdentity, + * PromiseHandlerThrower, or a callable. In the * latter case, it's guaranteed to be an object * from the same compartment as the * PromiseReactionJob. @@ -1100,11 +1094,17 @@ GetResolveFunctionFromReject(JSFunction* reject) { MOZ_ASSERT(reject->maybeNative() == RejectPromiseFunction); Value resolveFunVal = reject->getExtendedSlot(RejectFunctionSlot_ResolveFunction); - if (IsNativeFunction(resolveFunVal, ResolvePromiseFunction)) - return &resolveFunVal.toObject().as<JSFunction>(); + MOZ_ASSERT(IsNativeFunction(resolveFunVal, ResolvePromiseFunction)); + return &resolveFunVal.toObject().as<JSFunction>(); +} - PromiseAllDataHolder* resolveFunObj = &resolveFunVal.toObject().as<PromiseAllDataHolder>(); - return &resolveFunObj->resolveObj()->as<JSFunction>(); +static JSFunction* +GetRejectFunctionFromResolve(JSFunction* resolve) +{ + MOZ_ASSERT(resolve->maybeNative() == ResolvePromiseFunction); + Value rejectFunVal = resolve->getExtendedSlot(ResolveFunctionSlot_RejectFunction); + MOZ_ASSERT(IsNativeFunction(rejectFunVal, RejectPromiseFunction)); + return &rejectFunVal.toObject().as<JSFunction>(); } static JSFunction* @@ -1140,8 +1140,7 @@ ClearResolutionFunctionSlots(JSFunction* resolutionFun) JSFunction* reject; if (resolutionFun->maybeNative() == ResolvePromiseFunction) { resolve = resolutionFun; - reject = &resolutionFun->getExtendedSlot(ResolveFunctionSlot_RejectFunction) - .toObject().as<JSFunction>(); + reject = GetRejectFunctionFromResolve(resolutionFun); } else { resolve = GetResolveFunctionFromReject(resolutionFun); reject = resolutionFun; @@ -1585,7 +1584,6 @@ RunResolutionFunction(JSContext *cx, HandleObject resolutionFun, HandleValue res if (promise->state() != JS::PromiseState::Pending) return true; - if (mode == ResolveMode) { if (!PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_RESOLVE_FUNCTION)) return true; @@ -1595,7 +1593,6 @@ RunResolutionFunction(JSContext *cx, HandleObject resolutionFun, HandleValue res if (!PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_REJECT_FUNCTION)) return true; return RejectMaybeWrappedPromise(cx, promiseObj, result); - } // ES2016, 25.4.4.1.1. @@ -1669,7 +1666,7 @@ PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C, // Step 6. RootedValue nextValue(cx); RootedId indexId(cx); - RootedValue rejectFunVal(cx, ObjectOrNullValue(reject)); + RootedValue rejectFunVal(cx, ObjectValue(*reject)); while (true) { // Steps a-c, e-g. @@ -1690,11 +1687,8 @@ PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C, // Steps d.iii-iv. if (remainingCount == 0) { - if (resolve) { - return RunResolutionFunction(cx, resolve, valuesArrayVal, ResolveMode, - promiseObj); - } - return ResolvePromiseInternal(cx, promiseObj, valuesArrayVal); + return RunResolutionFunction(cx, resolve, valuesArrayVal, ResolveMode, + promiseObj); } // We're all set for now! @@ -1817,13 +1811,8 @@ PromiseAllResolveElementFunction(JSContext* cx, unsigned argc, Value* vp) // Step 6 (Adapted to work with PromiseAllDataHolder's layout). 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; - } + if (!RunResolutionFunction(cx, resolveAllFun, valuesVal, ResolveMode, promiseObj)) + return false; } // Step 11. @@ -1904,8 +1893,8 @@ PerformPromiseRace(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C, RootedValue CVal(cx, ObjectValue(*C)); RootedValue nextValue(cx); - RootedValue resolveFunVal(cx, ObjectOrNullValue(resolve)); - RootedValue rejectFunVal(cx, ObjectOrNullValue(reject)); + RootedValue resolveFunVal(cx, ObjectValue(*resolve)); + RootedValue rejectFunVal(cx, ObjectValue(*reject)); while (true) { // Steps a-c, e-g. @@ -2211,8 +2200,8 @@ js::AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, Hand return false; // Steps 4-5. - RootedValue onFulfilled(cx, Int32Value(PROMISE_HANDLER_AWAIT_FULFILLED)); - RootedValue onRejected(cx, Int32Value(PROMISE_HANDLER_AWAIT_REJECTED)); + RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAwaitFulfilled)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerAwaitRejected)); RootedObject incumbentGlobal(cx); if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) @@ -2291,12 +2280,12 @@ PerformPromiseThen(JSContext* cx, Handle<PromiseObject*> promise, HandleValue on // Step 3. RootedValue onFulfilled(cx, onFulfilled_); if (!IsCallable(onFulfilled)) - onFulfilled = Int32Value(PROMISE_HANDLER_IDENTITY); + onFulfilled = Int32Value(PromiseHandlerIdentity); // Step 4. RootedValue onRejected(cx, onRejected_); if (!IsCallable(onRejected)) - onRejected = Int32Value(PROMISE_HANDLER_THROWER); + onRejected = Int32Value(PromiseHandlerThrower); RootedObject incumbentGlobal(cx); if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) |