Bug 336705 - Part 1: Support creating and resolving Promises without resolve/reject functions.

Useful for internally-created Promises that'll only ever be resolved/rejected internally.

Tag #1287
This commit is contained in:
Gaming4JC
2019-12-13 20:13:07 -05:00
committed by Roy Tam
parent 70cbae88ea
commit f15e675b86
2 changed files with 51 additions and 37 deletions
+49 -37
View File
@@ -314,28 +314,27 @@ RejectPromiseFunction(JSContext* cx, unsigned argc, Value* vp)
return true;
}
// Step 5.
// Here, we only remove the Promise reference from the resolution
// functions. Actually marking it as fulfilled/rejected happens later.
ClearResolutionFunctionSlots(reject);
RootedObject promise(cx, &promiseVal.toObject());
// In some cases the Promise reference on the resolution function won't
// have been removed during resolution, so we need to check that here,
// too.
if (promise->is<PromiseObject>() &&
PromiseHasAnyFlag(promise->as<PromiseObject>(), PROMISE_FLAG_RESOLVED))
promise->as<PromiseObject>().state() != JS::PromiseState::Pending)
{
args.rval().setUndefined();
return true;
}
// Step 5.
// Here, we only remove the Promise reference from the resolution
// functions. Actually marking it as fulfilled/rejected happens later.
ClearResolutionFunctionSlots(reject);
// Step 6.
bool result = RejectMaybeWrappedPromise(cx, promise, reasonVal);
if (result)
args.rval().setUndefined();
return result;
if (!RejectMaybeWrappedPromise(cx, promise, reasonVal))
return false;
args.rval().setUndefined();
return true;
}
static MOZ_MUST_USE bool FulfillMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj,
@@ -405,38 +404,37 @@ ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp)
RootedFunction resolve(cx, &args.callee().as<JSFunction>());
RootedValue resolutionVal(cx, args.get(0));
// Steps 1-2.
RootedValue promiseVal(cx, resolve->getExtendedSlot(ResolveFunctionSlot_Promise));
// Steps 3-4.
// 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()) {
// Steps 3-4 (reordered).
// 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()) {
args.rval().setUndefined();
return true;
}
RootedObject promise(cx, &promiseVal.toObject());
// In some cases the Promise reference on the resolution function won't
// have been removed during resolution, so we need to check that here,
// too.
if (promise->is<PromiseObject>() &&
PromiseHasAnyFlag(promise->as<PromiseObject>(), PROMISE_FLAG_RESOLVED))
{
args.rval().setUndefined();
return true;
}
// Steps 1-2 (reordered).
RootedObject promise(cx, &resolve->getExtendedSlot(ResolveFunctionSlot_Promise).toObject());
// Step 5.
// Here, we only remove the Promise reference from the resolution
// functions. Actually marking it as fulfilled/rejected happens later.
ClearResolutionFunctionSlots(resolve);
bool status = ResolvePromiseInternal(cx, promise, resolutionVal);
if (status)
args.rval().setUndefined();
return status;
// In some cases the Promise reference on the resolution function won't
// have been removed during resolution, so we need to check that here,
// too.
if (promise->is<PromiseObject>() &&
promise->as<PromiseObject>().state() != JS::PromiseState::Pending)
{
return true;
}
// Steps 6-13.
if (!ResolvePromiseInternal(cx, promise, resolutionVal))
return false;
args.rval().setUndefined();
return true;
}
static bool PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp);
@@ -642,7 +640,7 @@ enum GetCapabilitiesExecutorSlots {
};
static MOZ_MUST_USE PromiseObject*
CreatePromiseObjectWithDefaultResolution(JSContext* cx)
CreatePromiseObjectWithoutResolutionFunctions(JSContext* cx)
{
Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx));
if (!promise)
@@ -677,7 +675,7 @@ NewPromiseCapability(JSContext* cx, HandleObject C, MutableHandleObject promise,
// in the list passed to all/race, which (potentially) means exposing them
// to content.
if (canOmitResolutionFunctions && IsNativeFunction(cVal, PromiseConstructor)) {
promise.set(CreatePromiseObjectWithDefaultResolution(cx));
promise.set(CreatePromiseObjectWithoutResolutionFunctions(cx));
if (!promise)
return false;
return true;
@@ -1362,6 +1360,14 @@ PromiseObject::create(JSContext* cx, HandleObject executor, HandleObject proto /
return promise;
}
// ES2016, 25.4.3.1. skipping creation of resolution functions and executor
// function invocation.
/* static */ PromiseObject*
PromiseObject::createSkippingExecutor(JSContext* cx)
{
return CreatePromiseObjectWithoutResolutionFunctions(cx);
}
static MOZ_MUST_USE bool PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator,
HandleObject C, HandleObject promiseObj,
HandleObject resolve, HandleObject reject,
@@ -2145,7 +2151,7 @@ MOZ_MUST_USE PromiseObject*
js::CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal)
{
// Step 1.
Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithDefaultResolution(cx));
Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
if (!promise)
return nullptr;
@@ -2187,7 +2193,7 @@ MOZ_MUST_USE bool
js::AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value)
{
// Step 2.
Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithDefaultResolution(cx));
Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
if (!promise)
return false;
@@ -2630,6 +2636,9 @@ PromiseObject::resolve(JSContext* cx, Handle<PromiseObject*> promise, HandleValu
if (promise->state() != JS::PromiseState::Pending)
return true;
if (PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_RESOLVE_FUNCTION))
return ResolvePromiseInternal(cx, promise, resolutionValue);
RootedObject resolveFun(cx, GetResolveFunctionFromPromise(promise));
RootedValue funVal(cx, ObjectValue(*resolveFun));
@@ -2653,6 +2662,9 @@ PromiseObject::reject(JSContext* cx, Handle<PromiseObject*> promise, HandleValue
if (promise->state() != JS::PromiseState::Pending)
return true;
if (PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_REJECT_FUNCTION))
return RejectMaybeWrappedPromise(cx, promise, rejectionValue);
RootedValue funVal(cx, promise->getFixedSlot(PromiseSlot_RejectFunction));
MOZ_ASSERT(IsCallable(funVal));
+2
View File
@@ -44,6 +44,8 @@ class PromiseObject : public NativeObject
static PromiseObject* create(JSContext* cx, HandleObject executor,
HandleObject proto = nullptr, bool needsWrapping = false);
static PromiseObject* createSkippingExecutor(JSContext* cx);
static JSObject* unforgeableResolve(JSContext* cx, HandleValue value);
static JSObject* unforgeableReject(JSContext* cx, HandleValue value);