Bug 1495072 - Part 2: Implement new, faster proposed await semantics. r=arai
authorJason Orendorff <jorendorff@mozilla.com>
Mon, 11 Mar 2019 15:24:01 +0000
changeset 521392 a14fcb229ddd59f9d7efb4f3b086503e804dfe8c
parent 521391 43c98aac932ad9d4f494d1aaef43e8fa2aa38839
child 521393 fdd61e6fb91139bedb183b393f4eecd45f914356
push id10866
push usernerli@mozilla.com
push dateTue, 12 Mar 2019 18:59:09 +0000
treeherdermozilla-beta@445c24a51727 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1495072
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1495072 - Part 2: Implement new, faster proposed await semantics. r=arai This patch implements the proposal in this pull request: <https://github.com/tc39/ecma262/pull/1250> Differential Revision: https://phabricator.services.mozilla.com/D21816
js/src/builtin/Promise.cpp
js/src/jit-test/tests/debug/onEnterFrame-async-01.js
js/src/tests/jstests.list
toolkit/components/promiseworker/tests/xpcshell/test_Promise.js
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -3554,42 +3554,47 @@ MOZ_MUST_USE bool js::AsyncFunctionRetur
 template <typename T>
 static MOZ_MUST_USE bool InternalAwait(JSContext* cx, HandleValue value,
                                        HandleObject resultPromise,
                                        HandleValue onFulfilled,
                                        HandleValue onRejected, T extraStep) {
   MOZ_ASSERT(onFulfilled.isInt32());
   MOZ_ASSERT(onRejected.isInt32());
 
-  // Step 2: Let promiseCapability be ! NewPromiseCapability(%Promise%).
-  Rooted<PromiseObject*> promise(
-      cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
+  // The proposal <https://github.com/tc39/ecma262/pull/1250>
+  // replaces steps 2-3 with the following updated step:
+  // Step 2: Let promise be ? PromiseResolve(« value »).
+  // Step 3 is deleted.
+  RootedObject promise(cx, PromiseObject::unforgeableResolve(cx, value));
   if (!promise) {
     return false;
   }
 
-  // Step 3: Perform ! Call(promiseCapability.[[Resolve]], undefined,
-  //                        « promise »).
-  if (!ResolvePromiseInternal(cx, promise, value)) {
+  // This downcast is safe because unforgeableResolve either returns `value`
+  // (only if it is already a possibly-wrapped promise) or creates a new
+  // promise using the Promise constructor.
+  Rooted<PromiseObject*> unwrappedPromise(
+      cx, UnwrapAndDowncastObject<PromiseObject>(cx, promise));
+  if (!unwrappedPromise) {
     return false;
   }
 
   // Steps 4-9 of the spec create onFulfilled and onRejected functions.
   Rooted<PromiseCapability> resultCapability(cx);
   resultCapability.promise().set(resultPromise);
   Rooted<PromiseReactionRecord*> reaction(
       cx, NewReactionRecord(cx, resultCapability, onFulfilled, onRejected,
                             IncumbentGlobalObject::Yes));
   if (!reaction) {
     return false;
   }
   extraStep(reaction);
 
   // Step 10: Perform ! PerformPromiseThen(promise, onFulfilled, onRejected).
-  return PerformPromiseThenWithReaction(cx, promise, reaction);
+  return PerformPromiseThenWithReaction(cx, unwrappedPromise, reaction);
 }
 
 // https://tc39.github.io/ecma262/#await
 //
 // 6.2.3.1 Await(promise) steps 2-10 when the running execution context is
 // evaluating an `await` expression in an async function.
 MOZ_MUST_USE JSObject* js::AsyncFunctionAwait(
     JSContext* cx, Handle<AsyncFunctionGeneratorObject*> genObj,
@@ -3802,25 +3807,23 @@ bool js::AsyncFromSyncIteratorMethod(JSC
   // Step 8: Let onFulfilled be CreateBuiltinFunction(steps, « [[Done]] »).
   // Step 9: Set onFulfilled.[[Done]] to done.
   RootedValue onFulfilled(
       cx,
       Int32Value(done ? PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone
                       : PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone));
   RootedValue onRejected(cx, Int32Value(PromiseHandlerThrower));
 
-  // These three steps are identical to some steps in Await; we have a utility
+  // These steps are identical to some steps in Await; we have a utility
   // function InternalAwait() that implements the idiom.
   //
-  // Step 5: Let valueWrapperCapability be ! NewPromiseCapability(%Promise%).
-  // Step 6: Perform ! Call(valueWrapperCapability.[[Resolve]], undefined,
-  //                        « value »).
-  // Step 10: Perform ! PerformPromiseThen(valueWrapperCapability.[[Promise]],
-  //                                       onFulfilled, undefined,
-  //                                       promiseCapability).
+  // Steps 5-6, as amended by <https://github.com/tc39/ecma262/pull/1250>:
+  //      Let valueWrapper be ? PromiseResolve(« value »).
+  // Step 10: Perform ! PerformPromiseThen(valueWrapper, onFulfilled,
+  //                                       undefined, promiseCapability).
   auto extra = [](Handle<PromiseReactionRecord*> reaction) {};
   if (!InternalAwait(cx, value, resultPromise, onFulfilled, onRejected,
                      extra)) {
     return false;
   }
 
   // Step 11: Return promiseCapability.[[Promise]].
   args.rval().setObject(*resultPromise);
@@ -4008,23 +4011,21 @@ static MOZ_MUST_USE bool AsyncGeneratorR
           static constexpr int32_t ResumeNextReturnRejected =
               PromiseHandlerAsyncGeneratorResumeNextReturnRejected;
           RootedValue onFulfilled(cx, Int32Value(ResumeNextReturnFulfilled));
           RootedValue onRejected(cx, Int32Value(ResumeNextReturnRejected));
 
           // These steps are nearly identical to some steps in Await;
           // InternalAwait() implements the idiom.
           //
-          // Step 10.b.i.2:  Let promiseCapability be
-          //                 ! NewPromiseCapability(%Promise%).
-          // Step 10.b.i.3:  Perform ! Call(promiseCapability.[[Resolve]],
-          //                 undefined, « completion.[[Value]] »).
-          // Step 10.b.i.10: Perform ! PerformPromiseThen(
-          //                 promiseCapability.[[Promise]], onFulfilled,
-          //                 onRejected).
+          // Steps 10.b.i.2-3, as amended by
+          // <https://github.com/tc39/ecma262/pull/1250>:
+          //      Let promise be ? PromiseResolve(« _completion_.[[Value]] »).
+          // Step 10.b.i.10: Perform ! PerformPromiseThen(promise, onFulfilled,
+          //                                              onRejected).
           // Step 10.b.i.11: Return undefined.
           auto extra = [&](Handle<PromiseReactionRecord*> reaction) {
             reaction->setIsAsyncGenerator(unwrappedGenerator);
           };
           return InternalAwait(cx, value, nullptr, onFulfilled, onRejected,
                                extra);
         }
 
--- a/js/src/jit-test/tests/debug/onEnterFrame-async-01.js
+++ b/js/src/jit-test/tests/debug/onEnterFrame-async-01.js
@@ -23,10 +23,10 @@ dbg.onEnterFrame = frame => {
     log += "(" + frame.nickname;
     frame.onPop = completion => { log += ")"; };
 };
 
 g.job();
 drainJobQueue();
 assertEq(log,
          "(job(t5)(t3))" +
-         "(t5)(t3)".repeat(3) + "(job)" +
-         "(t5)(t5)(job)");
+         "(t5)(t3)".repeat(3) +
+         "(t5)(job)(t5)(job)");
--- a/js/src/tests/jstests.list
+++ b/js/src/tests/jstests.list
@@ -1188,22 +1188,16 @@ skip script test262/intl402/NumberFormat
 # Hoisted block-level function named "arguments" not initialized with undefined per B.3.3.1
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1339123
 skip script test262/annexB/language/function-code/block-decl-func-skip-arguments.js
 
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1407587
 skip script test262/language/expressions/assignment/destructuring/keyed-destructuring-property-reference-target-evaluation-order.js
 skip script test262/language/expressions/assignment/destructuring/iterator-destructuring-property-reference-target-evaluation-order.js
 
-# https://bugzilla.mozilla.org/show_bug.cgi?id=1495072
-skip script test262/language/expressions/await/await-monkey-patched-promise.js
-skip script test262/language/expressions/await/async-await-interleaved.js
-skip script test262/language/expressions/await/for-await-of-interleaved.js
-skip script test262/language/expressions/await/async-generator-interleaved.js
-
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1321616
 skip script test262/annexB/built-ins/Function/createdynfn-html-close-comment-params.js
 
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1462745
 skip script test262/annexB/language/function-code/block-decl-nested-blocks-with-fun-decl.js
 
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1473229
 skip include test262/intl402/RelativeTimeFormat/prototype/formatToParts/jstests.list
--- a/toolkit/components/promiseworker/tests/xpcshell/test_Promise.js
+++ b/toolkit/components/promiseworker/tests/xpcshell/test_Promise.js
@@ -117,17 +117,18 @@ add_task(async function test_throw_error
 
 add_task(async function test_terminate() {
   let previousWorker = worker._worker;
 
   // Send two messages that we'll expect to be rejected.
   let message = ["test_simple_args", Math.random()];
   let promise1 = worker.post("bounce", message);
   let promise2 = worker.post("throwError", ["error message"]);
-  // Skip a beat so we can be sure that the two messages are in the queue.
+  // Skip a few beats so we can be sure that the two messages are in the queue.
+  await Promise.resolve();
   await Promise.resolve();
 
   worker.terminate();
 
   await Assert.rejects(promise1, /worker terminated/, "Pending promise should be rejected");
   await Assert.rejects(promise2, /worker terminated/, "Pending promise should be rejected");
 
   // Unfortunately, there's no real way to check whether a terminate worked from