summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGaming4JC <g4jc@hyperbola.info>2019-12-13 19:57:31 -0500
committerGaming4JC <g4jc@hyperbola.info>2019-12-17 06:25:22 -0500
commitf589ef816682918dddaf13f9dc06aae5253cd56a (patch)
tree5b62f711eefd333a6c8e1c529972fd89ce3d4c3d
parente23b013adfffbb33804eef572ff1ebf48923ef26 (diff)
downloaduxp-f589ef816682918dddaf13f9dc06aae5253cd56a.tar.gz
Bug 1317376 - Part 2: Detect Promise self-resolution when resolving through the Promise resolving fast path.
Tag #1287
-rw-r--r--js/src/builtin/Promise.cpp32
-rw-r--r--js/src/tests/ecma_6/Promise/self-resolve.js20
2 files changed, 34 insertions, 18 deletions
diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp
index 18a20ccab2..2e4529140c 100644
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -346,16 +346,28 @@ static MOZ_MUST_USE bool EnqueuePromiseResolveThenableJob(JSContext* cx,
HandleValue thenable,
HandleValue thenVal);
-// ES2016, 25.4.1.3.2, steps 7-13.
+// ES2016, 25.4.1.3.2, steps 6-13.
static MOZ_MUST_USE bool
ResolvePromiseInternal(JSContext* cx, HandleObject promise, HandleValue resolutionVal)
{
- // Step 7.
+ // Step 7 (reordered).
if (!resolutionVal.isObject())
return FulfillMaybeWrappedPromise(cx, promise, resolutionVal);
RootedObject resolution(cx, &resolutionVal.toObject());
+ // Step 6.
+ if (resolution == promise) {
+ // Step 6.a.
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF);
+ RootedValue selfResolutionError(cx);
+ MOZ_ALWAYS_TRUE(GetAndClearException(cx, &selfResolutionError));
+
+ // Step 6.b.
+ return RejectMaybeWrappedPromise(cx, promise, selfResolutionError);
+ }
+
// Step 8.
RootedValue thenVal(cx);
bool status = GetProperty(cx, resolution, resolution, cx->names().then, &thenVal);
@@ -421,22 +433,6 @@ ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp)
// functions. Actually marking it as fulfilled/rejected happens later.
ClearResolutionFunctionSlots(resolve);
- // Step 6.
- if (resolutionVal == promiseVal) {
- // Step 6.a.
- JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
- JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF);
- RootedValue selfResolutionError(cx);
- bool status = GetAndClearException(cx, &selfResolutionError);
- MOZ_ASSERT(status);
-
- // Step 6.b.
- status = RejectMaybeWrappedPromise(cx, promise, selfResolutionError);
- if (status)
- args.rval().setUndefined();
- return status;
- }
-
bool status = ResolvePromiseInternal(cx, promise, resolutionVal);
if (status)
args.rval().setUndefined();
diff --git a/js/src/tests/ecma_6/Promise/self-resolve.js b/js/src/tests/ecma_6/Promise/self-resolve.js
index e16a2ceb3d..4e7e36c6ca 100644
--- a/js/src/tests/ecma_6/Promise/self-resolve.js
+++ b/js/src/tests/ecma_6/Promise/self-resolve.js
@@ -5,6 +5,7 @@ if (!this.Promise) {
quit(0);
}
+// Resolve Promise with itself by directly calling the "Promise Resolve Function".
let resolve;
let promise = new Promise(function(x) { resolve = x; });
resolve(promise)
@@ -20,4 +21,23 @@ drainJobQueue()
assertEq(results.length, 1);
assertEq(results[0], "rejected");
+// Resolve Promise with itself when the "Promise Resolve Function" is called
+// from (the fast path in) PromiseReactionJob.
+results = [];
+
+promise = new Promise(x => { resolve = x; });
+let promise2 = promise.then(() => promise2);
+
+promise2.then(() => assertEq(true, false, "not reached"), res => {
+ assertEq(res instanceof TypeError, true);
+ results.push("rejected");
+});
+
+resolve();
+
+drainJobQueue();
+
+assertEq(results.length, 1);
+assertEq(results[0], "rejected");
+
this.reportCompare && reportCompare(0, 0, "ok");