mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-26 14:54:25 +00:00
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:
+49
-37
@@ -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));
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user