Bug 1530324 - Part 1: Store the internal generator of async functions in the Promise reaction. r=arai
authorAndré Bargull <andre.bargull@gmail.com>
Tue, 26 Feb 2019 08:02:59 -0800
changeset 519553 1dd26ddfc3734772ec7531d34792ee2c80402fe8
parent 519552 6ad7a291385a560da0a7b7019f03f9afde786112
child 519554 90d0e91224a9f061e631f88a4aed499108349b5d
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1530324
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 1530324 - Part 1: Store the internal generator of async functions in the Promise reaction. r=arai
js/src/builtin/Promise.cpp
js/src/builtin/Promise.h
js/src/vm/AsyncFunction.cpp
js/src/vm/AsyncFunction.h
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -16,16 +16,17 @@
 #include "jsfriendapi.h"
 
 #include "gc/Heap.h"
 #include "js/Debug.h"
 #include "js/PropertySpec.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #include "vm/Debugger.h"
+#include "vm/GeneratorObject.h"
 #include "vm/Iteration.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/SelfHosting.h"
 
 #include "vm/Compartment-inl.h"
 #include "vm/Debugger-inl.h"
 #include "vm/JSObject-inl.h"
@@ -530,18 +531,20 @@ enum ReactionRecordSlots {
   // The incumbent global for this reaction record. Can be null.
   ReactionRecordSlot_IncumbentGlobalObject,
 
   // Bitmask of the REACTION_FLAG values.
   ReactionRecordSlot_Flags,
 
   // Additional slot to store extra data for specific reaction record types.
   //
-  // - When the REACTION_FLAG_ASYNC_GENERATOR flag is set, this slot store
-  //   the async generator function for this promise reaction.
+  // - When the REACTION_FLAG_ASYNC_FUNCTION flag is set, this slot stores
+  //   the (internal) generator object for this promise reaction.
+  // - When the REACTION_FLAG_ASYNC_GENERATOR flag is set, this slot stores
+  //   the async generator object for this promise reaction.
   // - When the REACTION_FLAG_DEFAULT_RESOLVING_HANDLER flag is set, this
   //   slot stores the promise to resolve when conceptually "calling" the
   //   OnFulfilled or OnRejected handlers.
   ReactionRecordSlot_GeneratorOrPromiseToResolve,
 
   ReactionRecordSlots,
 };
 
@@ -614,23 +617,31 @@ class PromiseReactionRecord : public Nat
     return flags & REACTION_FLAG_DEFAULT_RESOLVING_HANDLER;
   }
   PromiseObject* defaultResolvingPromise() {
     MOZ_ASSERT(isDefaultResolvingHandler());
     const Value& promiseToResolve =
         getFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve);
     return &promiseToResolve.toObject().as<PromiseObject>();
   }
-  void setIsAsyncFunction() {
+  void setIsAsyncFunction(GeneratorObject* genObj) {
     setFlagOnInitialState(REACTION_FLAG_ASYNC_FUNCTION);
+    setFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve,
+                 ObjectValue(*genObj));
   }
   bool isAsyncFunction() {
     int32_t flags = this->flags();
     return flags & REACTION_FLAG_ASYNC_FUNCTION;
   }
+  GeneratorObject* asyncFunctionGenerator() {
+    MOZ_ASSERT(isAsyncFunction());
+    const Value& generator =
+        getFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve);
+    return &generator.toObject().as<GeneratorObject>();
+  }
   void setIsAsyncGenerator(AsyncGeneratorObject* asyncGenObj) {
     setFlagOnInitialState(REACTION_FLAG_ASYNC_GENERATOR);
     setFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve,
                  ObjectValue(*asyncGenObj));
   }
   bool isAsyncGenerator() {
     int32_t flags = this->flags();
     return flags & REACTION_FLAG_ASYNC_GENERATOR;
@@ -1466,32 +1477,30 @@ static MOZ_MUST_USE bool AsyncFunctionPr
     JSContext* cx, Handle<PromiseReactionRecord*> reaction,
     MutableHandleValue rval) {
   MOZ_ASSERT(reaction->isAsyncFunction());
 
   RootedValue handlerVal(cx, reaction->handler());
   RootedValue argument(cx, reaction->handlerArg());
   Rooted<PromiseObject*> resultPromise(
       cx, &reaction->promise()->as<PromiseObject>());
-  RootedValue generatorVal(
-      cx, resultPromise->getFixedSlot(PromiseSlot_AwaitGenerator));
+  Rooted<GeneratorObject*> generator(cx, reaction->asyncFunctionGenerator());
 
   int32_t handlerNum = handlerVal.toInt32();
 
   // Await's handlers don't return a value, nor throw exception.
   // They fail only on OOM.
   if (handlerNum == PromiseHandlerAsyncFunctionAwaitedFulfilled) {
-    if (!AsyncFunctionAwaitedFulfilled(cx, resultPromise, generatorVal,
+    if (!AsyncFunctionAwaitedFulfilled(cx, resultPromise, generator,
                                        argument)) {
       return false;
     }
   } else {
     MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFunctionAwaitedRejected);
-    if (!AsyncFunctionAwaitedRejected(cx, resultPromise, generatorVal,
-                                      argument)) {
+    if (!AsyncFunctionAwaitedRejected(cx, resultPromise, generator, argument)) {
       return false;
     }
   }
 
   rval.setUndefined();
   return true;
 }
 
@@ -3485,26 +3494,24 @@ MOZ_MUST_USE bool js::RejectPromiseWithP
 static MOZ_MUST_USE bool PerformPromiseThenWithReaction(
     JSContext* cx, Handle<PromiseObject*> promise,
     Handle<PromiseReactionRecord*> reaction);
 
 // Some async/await functions are implemented here instead of
 // js/src/builtin/AsyncFunction.cpp, to call Promise internal functions.
 
 // ES 2018 draft 14.6.11 and 14.7.14 step 1.
-MOZ_MUST_USE PromiseObject* js::CreatePromiseObjectForAsync(
-    JSContext* cx, HandleValue generatorVal) {
+MOZ_MUST_USE PromiseObject* js::CreatePromiseObjectForAsync(JSContext* cx) {
   // Step 1.
   PromiseObject* promise = CreatePromiseObjectWithoutResolutionFunctions(cx);
   if (!promise) {
     return nullptr;
   }
 
   AddPromiseFlags(*promise, PROMISE_FLAG_ASYNC);
-  promise->setFixedSlot(PromiseSlot_AwaitGenerator, generatorVal);
   return promise;
 }
 
 bool js::IsPromiseForAsync(JSObject* promise) {
   return promise->is<PromiseObject>() &&
          PromiseHasAnyFlag(promise->as<PromiseObject>(), PROMISE_FLAG_ASYNC);
 }
 
@@ -3573,27 +3580,28 @@ static MOZ_MUST_USE bool InternalAwait(J
   extraStep(reaction);
 
   // Step 9.
   return PerformPromiseThenWithReaction(cx, promise, reaction);
 }
 
 // ES 2018 draft 25.5.5.3 steps 2-10.
 MOZ_MUST_USE bool js::AsyncFunctionAwait(JSContext* cx,
+                                         Handle<GeneratorObject*> genObj,
                                          Handle<PromiseObject*> resultPromise,
                                          HandleValue value) {
   // Steps 4-5.
   RootedValue onFulfilled(
       cx, Int32Value(PromiseHandlerAsyncFunctionAwaitedFulfilled));
   RootedValue onRejected(
       cx, Int32Value(PromiseHandlerAsyncFunctionAwaitedRejected));
 
   // Steps 2-3, 6-10.
-  auto extra = [](Handle<PromiseReactionRecord*> reaction) {
-    reaction->setIsAsyncFunction();
+  auto extra = [&](Handle<PromiseReactionRecord*> reaction) {
+    reaction->setIsAsyncFunction(genObj);
   };
   return InternalAwait(cx, value, resultPromise, onFulfilled, onRejected,
                        extra);
 }
 
 // Async Iteration proposal 4.1 Await steps 2-9.
 MOZ_MUST_USE bool js::AsyncGeneratorAwait(
     JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
--- a/js/src/builtin/Promise.h
+++ b/js/src/builtin/Promise.h
@@ -28,20 +28,17 @@ enum PromiseSlots {
   // * if this promise is fulfilled, the resolution value
   // * if this promise is rejected, the reason for the rejection
   PromiseSlot_ReactionsOrResult,
 
   // * if this promise is pending, resolve/reject functions.
   //   This slot holds only the reject function. The resolve function is
   //   reachable from the reject function's extended slot.
   // * if this promise is either fulfilled or rejected, undefined
-  // * (special case) if this promise is the return value of an async function
-  //   invocation, the generator object for the function's internal generator
   PromiseSlot_RejectFunction,
-  PromiseSlot_AwaitGenerator = PromiseSlot_RejectFunction,
 
   // Promise object's debug info, which is created on demand.
   // * if this promise has no debug info, undefined
   // * if this promise contains only its process-unique ID, the ID's number
   //   value
   // * otherwise a PromiseDebugInfo object
   PromiseSlot_DebugInfo,
 
@@ -233,33 +230,35 @@ MOZ_MUST_USE JSObject* PromiseResolve(JS
 
 MOZ_MUST_USE bool RejectPromiseWithPendingError(JSContext* cx,
                                                 Handle<PromiseObject*> promise);
 
 /**
  * Create the promise object which will be used as the return value of an async
  * function.
  */
-MOZ_MUST_USE PromiseObject* CreatePromiseObjectForAsync(
-    JSContext* cx, HandleValue generatorVal);
+MOZ_MUST_USE PromiseObject* CreatePromiseObjectForAsync(JSContext* cx);
 
 /**
  * Returns true if the given object is a promise created by
  * CreatePromiseObjectForAsync function.
  */
 MOZ_MUST_USE bool IsPromiseForAsync(JSObject* promise);
 
+class GeneratorObject;
+
 MOZ_MUST_USE bool AsyncFunctionReturned(JSContext* cx,
                                         Handle<PromiseObject*> resultPromise,
                                         HandleValue value);
 
 MOZ_MUST_USE bool AsyncFunctionThrown(JSContext* cx,
                                       Handle<PromiseObject*> resultPromise);
 
 MOZ_MUST_USE bool AsyncFunctionAwait(JSContext* cx,
+                                     Handle<GeneratorObject*> genObj,
                                      Handle<PromiseObject*> resultPromise,
                                      HandleValue value);
 
 // If the await operation can be skipped and the resolution value for `val` can
 // be acquired, stored the resolved value to `resolved` and `true` to
 // `*canSkip`.  Otherwise, stores `false` to `*canSkip`.
 MOZ_MUST_USE bool TrySkipAwait(JSContext* cx, HandleValue val, bool* canSkip,
                                MutableHandleValue resolved);
--- a/js/src/vm/AsyncFunction.cpp
+++ b/js/src/vm/AsyncFunction.cpp
@@ -56,17 +56,17 @@ using mozilla::Maybe;
   global->setReservedSlot(ASYNC_FUNCTION, ObjectValue(*asyncFunction));
   global->setReservedSlot(ASYNC_FUNCTION_PROTO,
                           ObjectValue(*asyncFunctionProto));
   return true;
 }
 
 static MOZ_MUST_USE bool AsyncFunctionStart(
     JSContext* cx, Handle<PromiseObject*> resultPromise,
-    HandleValue generatorVal);
+    Handle<GeneratorObject*> generator);
 
 #define UNWRAPPED_ASYNC_WRAPPED_SLOT 1
 #define WRAPPED_ASYNC_UNWRAPPED_SLOT 0
 
 // Async Functions proposal 1.1.8 and 1.2.14.
 static bool WrappedAsyncFunction(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -79,24 +79,25 @@ static bool WrappedAsyncFunction(JSConte
   InvokeArgs args2(cx);
   if (!FillArgumentsFromArraylike(cx, args2, args)) {
     return false;
   }
 
   RootedValue generatorVal(cx);
   if (Call(cx, unwrappedVal, args.thisv(), args2, &generatorVal)) {
     // Step 1.
-    Rooted<PromiseObject*> resultPromise(
-        cx, CreatePromiseObjectForAsync(cx, generatorVal));
+    Rooted<PromiseObject*> resultPromise(cx, CreatePromiseObjectForAsync(cx));
     if (!resultPromise) {
       return false;
     }
 
     // Step 3.
-    if (!AsyncFunctionStart(cx, resultPromise, generatorVal)) {
+    Rooted<GeneratorObject*> generator(
+        cx, &generatorVal.toObject().as<GeneratorObject>());
+    if (!AsyncFunctionStart(cx, resultPromise, generator)) {
       return false;
     }
 
     // Step 5.
     args.rval().setObject(*resultPromise);
     return true;
   }
 
@@ -177,74 +178,75 @@ enum class ResumeKind { Normal, Throw };
 
 // Async Functions proposal 2.2 steps 3.f, 3.g.
 // Async Functions proposal 2.2 steps 3.d-e, 3.g.
 // Implemented in js/src/builtin/Promise.cpp
 
 // Async Functions proposal 2.2 steps 3-8, 2.4 steps 2-7, 2.5 steps 2-7.
 static bool AsyncFunctionResume(JSContext* cx,
                                 Handle<PromiseObject*> resultPromise,
-                                HandleValue generatorVal, ResumeKind kind,
-                                HandleValue valueOrReason) {
+                                Handle<GeneratorObject*> generator,
+                                ResumeKind kind, HandleValue valueOrReason) {
   RootedObject stack(cx, resultPromise->allocationSite());
   Maybe<JS::AutoSetAsyncStackForNewCalls> asyncStack;
   if (stack) {
     asyncStack.emplace(
         cx, stack, "async",
         JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT);
   }
 
   // Execution context switching is handled in generator.
   HandlePropertyName funName = kind == ResumeKind::Normal
                                    ? cx->names().GeneratorNext
                                    : cx->names().GeneratorThrow;
   FixedInvokeArgs<1> args(cx);
   args[0].set(valueOrReason);
-  RootedValue value(cx);
-  if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &value)) {
+  RootedValue generatorOrValue(cx, ObjectValue(*generator));
+  if (!CallSelfHostedFunction(cx, funName, generatorOrValue, args,
+                              &generatorOrValue)) {
     return AsyncFunctionThrown(cx, resultPromise);
   }
 
-  if (generatorVal.toObject().as<GeneratorObject>().isAfterAwait()) {
-    return AsyncFunctionAwait(cx, resultPromise, value);
+  if (generator->isAfterAwait()) {
+    return AsyncFunctionAwait(cx, generator, resultPromise, generatorOrValue);
   }
 
-  return AsyncFunctionReturned(cx, resultPromise, value);
+  return AsyncFunctionReturned(cx, resultPromise, generatorOrValue);
 }
 
 // Async Functions proposal 2.2 steps 3-8.
 static MOZ_MUST_USE bool AsyncFunctionStart(
     JSContext* cx, Handle<PromiseObject*> resultPromise,
-    HandleValue generatorVal) {
-  return AsyncFunctionResume(cx, resultPromise, generatorVal,
-                             ResumeKind::Normal, UndefinedHandleValue);
+    Handle<GeneratorObject*> generator) {
+  return AsyncFunctionResume(cx, resultPromise, generator, ResumeKind::Normal,
+                             UndefinedHandleValue);
 }
 
 // Async Functions proposal 2.3 steps 1-8.
 // Implemented in js/src/builtin/Promise.cpp
 
 // Async Functions proposal 2.4.
 MOZ_MUST_USE bool js::AsyncFunctionAwaitedFulfilled(
     JSContext* cx, Handle<PromiseObject*> resultPromise,
-    HandleValue generatorVal, HandleValue value) {
+    Handle<GeneratorObject*> generator, HandleValue value) {
   // Step 1 (implicit).
 
   // Steps 2-7.
-  return AsyncFunctionResume(cx, resultPromise, generatorVal,
-                             ResumeKind::Normal, value);
+  return AsyncFunctionResume(cx, resultPromise, generator, ResumeKind::Normal,
+                             value);
 }
 
 // Async Functions proposal 2.5.
 MOZ_MUST_USE bool js::AsyncFunctionAwaitedRejected(
     JSContext* cx, Handle<PromiseObject*> resultPromise,
-    HandleValue generatorVal, HandleValue reason) {
+    Handle<GeneratorObject*> generator, HandleValue reason) {
   // Step 1 (implicit).
 
   // Step 2-7.
-  return AsyncFunctionResume(cx, resultPromise, generatorVal, ResumeKind::Throw,
+  return AsyncFunctionResume(cx, resultPromise, generator, ResumeKind::Throw,
                              reason);
 }
 
 JSFunction* js::GetWrappedAsyncFunction(JSFunction* unwrapped) {
   MOZ_ASSERT(unwrapped->isAsync());
   return &unwrapped->getExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT)
               .toObject()
               .as<JSFunction>();
--- a/js/src/vm/AsyncFunction.h
+++ b/js/src/vm/AsyncFunction.h
@@ -7,16 +7,18 @@
 #ifndef vm_AsyncFunction_h
 #define vm_AsyncFunction_h
 
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 
 namespace js {
 
+class GeneratorObject;
+
 // An async function is implemented using two function objects, which are
 // referred to as the "unwrapped" and the "wrapped" async function object.
 // The unwrapped function is a generator function compiled from the async
 // function's script. |await| expressions within the async function are
 // compiled like |yield| expression for the generator function with dedicated
 // opcode,. The unwrapped function is never exposed to user script.
 // The wrapped function is a native function which wraps the generator function,
 // hence its name, and is the publicly exposed object of the async function.
@@ -43,17 +45,17 @@ JSObject* WrapAsyncFunctionWithProto(JSC
 // prototype object.
 JSObject* WrapAsyncFunction(JSContext* cx, HandleFunction unwrapped);
 
 // Resume the async function when the `await` operand resolves.
 // Split into two functions depending on whether the awaited value was
 // fulfilled or rejected.
 MOZ_MUST_USE bool AsyncFunctionAwaitedFulfilled(
     JSContext* cx, Handle<PromiseObject*> resultPromise,
-    HandleValue generatorVal, HandleValue value);
+    Handle<GeneratorObject*> generator, HandleValue value);
 
 MOZ_MUST_USE bool AsyncFunctionAwaitedRejected(
     JSContext* cx, Handle<PromiseObject*> resultPromise,
-    HandleValue generatorVal, HandleValue reason);
+    Handle<GeneratorObject*> generator, HandleValue reason);
 
 }  // namespace js
 
 #endif /* vm_AsyncFunction_h */