summaryrefslogtreecommitdiffstats
path: root/js
diff options
context:
space:
mode:
authorGaming4JC <g4jc@hyperbola.info>2019-12-13 20:13:07 -0500
committerGaming4JC <g4jc@hyperbola.info>2019-12-17 06:25:22 -0500
commitcb732e5fdda3528f18aee6a761354fe8c1b7c274 (patch)
tree3850f7f47212d30379b0bac98658fc9ac39a31b1 /js
parentf589ef816682918dddaf13f9dc06aae5253cd56a (diff)
downloadUXP-cb732e5fdda3528f18aee6a761354fe8c1b7c274.tar
UXP-cb732e5fdda3528f18aee6a761354fe8c1b7c274.tar.gz
UXP-cb732e5fdda3528f18aee6a761354fe8c1b7c274.tar.lz
UXP-cb732e5fdda3528f18aee6a761354fe8c1b7c274.tar.xz
UXP-cb732e5fdda3528f18aee6a761354fe8c1b7c274.zip
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
Diffstat (limited to 'js')
-rw-r--r--js/src/builtin/Promise.cpp80
-rw-r--r--js/src/builtin/Promise.h2
2 files changed, 48 insertions, 34 deletions
diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp
index 2e4529140..884cd6bac 100644
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -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());
+ // 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);
// 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(resolve);
-
- bool status = ResolvePromiseInternal(cx, promise, resolutionVal);
- if (status)
- args.rval().setUndefined();
- return status;
+ // 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));
diff --git a/js/src/builtin/Promise.h b/js/src/builtin/Promise.h
index 6a6453e46..8f716dfbc 100644
--- a/js/src/builtin/Promise.h
+++ b/js/src/builtin/Promise.h
@@ -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);