Home | History | Annotate | Download | only in builtins
      1 // Copyright 2017 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/builtins/builtins-async-gen.h"
      6 #include "src/builtins/builtins-utils-gen.h"
      7 #include "src/builtins/builtins.h"
      8 #include "src/code-factory.h"
      9 #include "src/code-stub-assembler.h"
     10 #include "src/frames-inl.h"
     11 #include "src/objects/js-generator.h"
     12 
     13 namespace v8 {
     14 namespace internal {
     15 
     16 using compiler::Node;
     17 
     18 namespace {
     19 
     20 // Describe fields of Context associated with AsyncGeneratorAwait resume
     21 // closures.
     22 class AwaitContext {
     23  public:
     24   enum Fields { kGeneratorSlot = Context::MIN_CONTEXT_SLOTS, kLength };
     25 };
     26 
     27 class AsyncGeneratorBuiltinsAssembler : public AsyncBuiltinsAssembler {
     28  public:
     29   explicit AsyncGeneratorBuiltinsAssembler(CodeAssemblerState* state)
     30       : AsyncBuiltinsAssembler(state) {}
     31 
     32   inline Node* TaggedIsAsyncGenerator(Node* tagged_object) {
     33     TNode<BoolT> if_notsmi = TaggedIsNotSmi(tagged_object);
     34     return Select<BoolT>(if_notsmi,
     35                          [=] {
     36                            return HasInstanceType(
     37                                tagged_object, JS_ASYNC_GENERATOR_OBJECT_TYPE);
     38                          },
     39                          [=] { return if_notsmi; });
     40   }
     41   inline Node* LoadGeneratorState(Node* const generator) {
     42     return LoadObjectField(generator, JSGeneratorObject::kContinuationOffset);
     43   }
     44 
     45   inline TNode<BoolT> IsGeneratorStateClosed(SloppyTNode<Smi> const state) {
     46     return SmiEqual(state, SmiConstant(JSGeneratorObject::kGeneratorClosed));
     47   }
     48   inline TNode<BoolT> IsGeneratorClosed(Node* const generator) {
     49     return IsGeneratorStateClosed(LoadGeneratorState(generator));
     50   }
     51 
     52   inline TNode<BoolT> IsGeneratorStateSuspended(SloppyTNode<Smi> const state) {
     53     return SmiGreaterThanOrEqual(state, SmiConstant(0));
     54   }
     55 
     56   inline TNode<BoolT> IsGeneratorSuspended(Node* const generator) {
     57     return IsGeneratorStateSuspended(LoadGeneratorState(generator));
     58   }
     59 
     60   inline TNode<BoolT> IsGeneratorStateSuspendedAtStart(
     61       SloppyTNode<Smi> const state) {
     62     return SmiEqual(state, SmiConstant(0));
     63   }
     64 
     65   inline TNode<BoolT> IsGeneratorStateNotExecuting(
     66       SloppyTNode<Smi> const state) {
     67     return SmiNotEqual(state,
     68                        SmiConstant(JSGeneratorObject::kGeneratorExecuting));
     69   }
     70   inline TNode<BoolT> IsGeneratorNotExecuting(Node* const generator) {
     71     return IsGeneratorStateNotExecuting(LoadGeneratorState(generator));
     72   }
     73 
     74   inline TNode<BoolT> IsGeneratorAwaiting(Node* const generator) {
     75     TNode<Object> is_generator_awaiting =
     76         LoadObjectField(generator, JSAsyncGeneratorObject::kIsAwaitingOffset);
     77     return WordEqual(is_generator_awaiting, SmiConstant(1));
     78   }
     79 
     80   inline void SetGeneratorAwaiting(Node* const generator) {
     81     CSA_ASSERT(this, Word32BinaryNot(IsGeneratorAwaiting(generator)));
     82     StoreObjectFieldNoWriteBarrier(
     83         generator, JSAsyncGeneratorObject::kIsAwaitingOffset, SmiConstant(1));
     84     CSA_ASSERT(this, IsGeneratorAwaiting(generator));
     85   }
     86 
     87   inline void SetGeneratorNotAwaiting(Node* const generator) {
     88     CSA_ASSERT(this, IsGeneratorAwaiting(generator));
     89     StoreObjectFieldNoWriteBarrier(
     90         generator, JSAsyncGeneratorObject::kIsAwaitingOffset, SmiConstant(0));
     91     CSA_ASSERT(this, Word32BinaryNot(IsGeneratorAwaiting(generator)));
     92   }
     93 
     94   inline void CloseGenerator(Node* const generator) {
     95     StoreObjectFieldNoWriteBarrier(
     96         generator, JSGeneratorObject::kContinuationOffset,
     97         SmiConstant(JSGeneratorObject::kGeneratorClosed));
     98   }
     99 
    100   inline Node* IsFastJSIterResult(Node* const value, Node* const context) {
    101     CSA_ASSERT(this, TaggedIsNotSmi(value));
    102     Node* const native_context = LoadNativeContext(context);
    103     return WordEqual(
    104         LoadMap(value),
    105         LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX));
    106   }
    107 
    108   inline Node* LoadFirstAsyncGeneratorRequestFromQueue(Node* const generator) {
    109     return LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset);
    110   }
    111 
    112   inline Node* LoadResumeTypeFromAsyncGeneratorRequest(Node* const request) {
    113     return LoadObjectField(request, AsyncGeneratorRequest::kResumeModeOffset);
    114   }
    115 
    116   inline Node* LoadPromiseFromAsyncGeneratorRequest(Node* const request) {
    117     return LoadObjectField(request, AsyncGeneratorRequest::kPromiseOffset);
    118   }
    119 
    120   inline Node* LoadValueFromAsyncGeneratorRequest(Node* const request) {
    121     return LoadObjectField(request, AsyncGeneratorRequest::kValueOffset);
    122   }
    123 
    124   inline TNode<BoolT> IsAbruptResumeType(SloppyTNode<Smi> const resume_type) {
    125     return SmiNotEqual(resume_type, SmiConstant(JSGeneratorObject::kNext));
    126   }
    127 
    128   void AsyncGeneratorEnqueue(CodeStubArguments* args, Node* context,
    129                              Node* generator, Node* value,
    130                              JSAsyncGeneratorObject::ResumeMode resume_mode,
    131                              const char* method_name);
    132 
    133   Node* TakeFirstAsyncGeneratorRequestFromQueue(Node* generator);
    134   Node* TakeFirstAsyncGeneratorRequestFromQueueIfPresent(Node* generator,
    135                                                          Label* if_not_present);
    136   void AddAsyncGeneratorRequestToQueue(Node* generator, Node* request);
    137 
    138   Node* AllocateAsyncGeneratorRequest(
    139       JSAsyncGeneratorObject::ResumeMode resume_mode, Node* resume_value,
    140       Node* promise);
    141 
    142   // Shared implementation of the catchable and uncatchable variations of Await
    143   // for AsyncGenerators.
    144   template <typename Descriptor>
    145   void AsyncGeneratorAwait(bool is_catchable);
    146   void AsyncGeneratorAwaitResumeClosure(
    147       Node* context, Node* value,
    148       JSAsyncGeneratorObject::ResumeMode resume_mode);
    149 };
    150 
    151 // Shared implementation for the 3 Async Iterator protocol methods of Async
    152 // Generators.
    153 void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorEnqueue(
    154     CodeStubArguments* args, Node* context, Node* generator, Node* value,
    155     JSAsyncGeneratorObject::ResumeMode resume_mode, const char* method_name) {
    156   // AsyncGeneratorEnqueue produces a new Promise, and appends it to the list
    157   // of async generator requests to be executed. If the generator is not
    158   // presently executing, then this method will loop through, processing each
    159   // request from front to back.
    160   // This loop resides in AsyncGeneratorResumeNext.
    161   Node* promise = AllocateAndInitJSPromise(context);
    162 
    163   Label enqueue(this), if_receiverisincompatible(this, Label::kDeferred);
    164 
    165   Branch(TaggedIsAsyncGenerator(generator), &enqueue,
    166          &if_receiverisincompatible);
    167 
    168   BIND(&enqueue);
    169   {
    170     Label done(this);
    171     Node* const req =
    172         AllocateAsyncGeneratorRequest(resume_mode, value, promise);
    173 
    174     AddAsyncGeneratorRequestToQueue(generator, req);
    175 
    176     // Let state be generator.[[AsyncGeneratorState]]
    177     // If state is not "executing", then
    178     //     Perform AsyncGeneratorResumeNext(Generator)
    179     // Check if the {receiver} is running or already closed.
    180     TNode<Smi> continuation = CAST(LoadGeneratorState(generator));
    181 
    182     GotoIf(SmiEqual(continuation,
    183                     SmiConstant(JSAsyncGeneratorObject::kGeneratorExecuting)),
    184            &done);
    185 
    186     CallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator);
    187 
    188     Goto(&done);
    189     BIND(&done);
    190     args->PopAndReturn(promise);
    191   }
    192 
    193   BIND(&if_receiverisincompatible);
    194   {
    195     Node* const error =
    196         MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver, context,
    197                       StringConstant(method_name), generator);
    198 
    199     CallBuiltin(Builtins::kRejectPromise, context, promise, error,
    200                 TrueConstant());
    201     args->PopAndReturn(promise);
    202   }
    203 }
    204 
    205 Node* AsyncGeneratorBuiltinsAssembler::AllocateAsyncGeneratorRequest(
    206     JSAsyncGeneratorObject::ResumeMode resume_mode, Node* resume_value,
    207     Node* promise) {
    208   CSA_SLOW_ASSERT(this, HasInstanceType(promise, JS_PROMISE_TYPE));
    209   Node* request = Allocate(AsyncGeneratorRequest::kSize);
    210   StoreMapNoWriteBarrier(request, Heap::kAsyncGeneratorRequestMapRootIndex);
    211   StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kNextOffset,
    212                                  UndefinedConstant());
    213   StoreObjectFieldNoWriteBarrier(request,
    214                                  AsyncGeneratorRequest::kResumeModeOffset,
    215                                  SmiConstant(resume_mode));
    216   StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kValueOffset,
    217                                  resume_value);
    218   StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kPromiseOffset,
    219                                  promise);
    220   StoreObjectFieldRoot(request, AsyncGeneratorRequest::kNextOffset,
    221                        Heap::kUndefinedValueRootIndex);
    222   return request;
    223 }
    224 
    225 void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwaitResumeClosure(
    226     Node* context, Node* value,
    227     JSAsyncGeneratorObject::ResumeMode resume_mode) {
    228   Node* const generator =
    229       LoadContextElement(context, AwaitContext::kGeneratorSlot);
    230   CSA_SLOW_ASSERT(this, TaggedIsAsyncGenerator(generator));
    231 
    232   SetGeneratorNotAwaiting(generator);
    233 
    234   CSA_SLOW_ASSERT(this, IsGeneratorSuspended(generator));
    235 
    236   // Remember the {resume_mode} for the {generator}.
    237   StoreObjectFieldNoWriteBarrier(generator,
    238                                  JSGeneratorObject::kResumeModeOffset,
    239                                  SmiConstant(resume_mode));
    240 
    241   CallStub(CodeFactory::ResumeGenerator(isolate()), context, value, generator);
    242 
    243   TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator);
    244 }
    245 
    246 template <typename Descriptor>
    247 void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwait(bool is_catchable) {
    248   Node* generator = Parameter(Descriptor::kGenerator);
    249   Node* value = Parameter(Descriptor::kAwaited);
    250   Node* context = Parameter(Descriptor::kContext);
    251 
    252   CSA_SLOW_ASSERT(this, TaggedIsAsyncGenerator(generator));
    253 
    254   Node* const request = LoadFirstAsyncGeneratorRequestFromQueue(generator);
    255   CSA_ASSERT(this, IsNotUndefined(request));
    256 
    257   ContextInitializer init_closure_context = [&](Node* context) {
    258     StoreContextElementNoWriteBarrier(context, AwaitContext::kGeneratorSlot,
    259                                       generator);
    260   };
    261 
    262   Node* outer_promise =
    263       LoadObjectField(request, AsyncGeneratorRequest::kPromiseOffset);
    264 
    265   const int resolve_index = Context::ASYNC_GENERATOR_AWAIT_RESOLVE_SHARED_FUN;
    266   const int reject_index = Context::ASYNC_GENERATOR_AWAIT_REJECT_SHARED_FUN;
    267 
    268   SetGeneratorAwaiting(generator);
    269   Await(context, generator, value, outer_promise, AwaitContext::kLength,
    270         init_closure_context, resolve_index, reject_index, is_catchable);
    271   Return(UndefinedConstant());
    272 }
    273 
    274 void AsyncGeneratorBuiltinsAssembler::AddAsyncGeneratorRequestToQueue(
    275     Node* generator, Node* request) {
    276   VARIABLE(var_current, MachineRepresentation::kTagged);
    277   Label empty(this), loop(this, &var_current), done(this);
    278 
    279   var_current.Bind(
    280       LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset));
    281   Branch(IsUndefined(var_current.value()), &empty, &loop);
    282 
    283   BIND(&empty);
    284   {
    285     StoreObjectField(generator, JSAsyncGeneratorObject::kQueueOffset, request);
    286     Goto(&done);
    287   }
    288 
    289   BIND(&loop);
    290   {
    291     Label loop_next(this), next_empty(this);
    292     Node* current = var_current.value();
    293     Node* next = LoadObjectField(current, AsyncGeneratorRequest::kNextOffset);
    294 
    295     Branch(IsUndefined(next), &next_empty, &loop_next);
    296     BIND(&next_empty);
    297     {
    298       StoreObjectField(current, AsyncGeneratorRequest::kNextOffset, request);
    299       Goto(&done);
    300     }
    301 
    302     BIND(&loop_next);
    303     {
    304       var_current.Bind(next);
    305       Goto(&loop);
    306     }
    307   }
    308   BIND(&done);
    309 }
    310 
    311 Node* AsyncGeneratorBuiltinsAssembler::TakeFirstAsyncGeneratorRequestFromQueue(
    312     Node* generator) {
    313   // Removes and returns the first AsyncGeneratorRequest from a
    314   // JSAsyncGeneratorObject's queue. Asserts that the queue is not empty.
    315   CSA_ASSERT(this, TaggedIsAsyncGenerator(generator));
    316   Node* request =
    317       LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset);
    318   CSA_ASSERT(this, IsNotUndefined(request));
    319 
    320   Node* next = LoadObjectField(request, AsyncGeneratorRequest::kNextOffset);
    321 
    322   StoreObjectField(generator, JSAsyncGeneratorObject::kQueueOffset, next);
    323   return request;
    324 }
    325 }  // namespace
    326 
    327 // https://tc39.github.io/proposal-async-iteration/
    328 // Section #sec-asyncgenerator-prototype-next
    329 TF_BUILTIN(AsyncGeneratorPrototypeNext, AsyncGeneratorBuiltinsAssembler) {
    330   const int kValueArg = 0;
    331 
    332   Node* argc =
    333       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
    334   CodeStubArguments args(this, argc);
    335 
    336   Node* generator = args.GetReceiver();
    337   Node* value = args.GetOptionalArgumentValue(kValueArg);
    338   Node* context = Parameter(Descriptor::kContext);
    339 
    340   AsyncGeneratorEnqueue(&args, context, generator, value,
    341                         JSAsyncGeneratorObject::kNext,
    342                         "[AsyncGenerator].prototype.next");
    343 }
    344 
    345 // https://tc39.github.io/proposal-async-iteration/
    346 // Section #sec-asyncgenerator-prototype-return
    347 TF_BUILTIN(AsyncGeneratorPrototypeReturn, AsyncGeneratorBuiltinsAssembler) {
    348   const int kValueArg = 0;
    349 
    350   Node* argc =
    351       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
    352   CodeStubArguments args(this, argc);
    353 
    354   Node* generator = args.GetReceiver();
    355   Node* value = args.GetOptionalArgumentValue(kValueArg);
    356   Node* context = Parameter(Descriptor::kContext);
    357 
    358   AsyncGeneratorEnqueue(&args, context, generator, value,
    359                         JSAsyncGeneratorObject::kReturn,
    360                         "[AsyncGenerator].prototype.return");
    361 }
    362 
    363 // https://tc39.github.io/proposal-async-iteration/
    364 // Section #sec-asyncgenerator-prototype-throw
    365 TF_BUILTIN(AsyncGeneratorPrototypeThrow, AsyncGeneratorBuiltinsAssembler) {
    366   const int kValueArg = 0;
    367 
    368   Node* argc =
    369       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
    370   CodeStubArguments args(this, argc);
    371 
    372   Node* generator = args.GetReceiver();
    373   Node* value = args.GetOptionalArgumentValue(kValueArg);
    374   Node* context = Parameter(Descriptor::kContext);
    375 
    376   AsyncGeneratorEnqueue(&args, context, generator, value,
    377                         JSAsyncGeneratorObject::kThrow,
    378                         "[AsyncGenerator].prototype.throw");
    379 }
    380 
    381 TF_BUILTIN(AsyncGeneratorAwaitResolveClosure, AsyncGeneratorBuiltinsAssembler) {
    382   Node* value = Parameter(Descriptor::kValue);
    383   Node* context = Parameter(Descriptor::kContext);
    384   AsyncGeneratorAwaitResumeClosure(context, value,
    385                                    JSAsyncGeneratorObject::kNext);
    386 }
    387 
    388 TF_BUILTIN(AsyncGeneratorAwaitRejectClosure, AsyncGeneratorBuiltinsAssembler) {
    389   Node* value = Parameter(Descriptor::kValue);
    390   Node* context = Parameter(Descriptor::kContext);
    391   AsyncGeneratorAwaitResumeClosure(context, value,
    392                                    JSAsyncGeneratorObject::kThrow);
    393 }
    394 
    395 TF_BUILTIN(AsyncGeneratorAwaitUncaught, AsyncGeneratorBuiltinsAssembler) {
    396   const bool kIsCatchable = false;
    397   AsyncGeneratorAwait<Descriptor>(kIsCatchable);
    398 }
    399 
    400 TF_BUILTIN(AsyncGeneratorAwaitCaught, AsyncGeneratorBuiltinsAssembler) {
    401   const bool kIsCatchable = true;
    402   AsyncGeneratorAwait<Descriptor>(kIsCatchable);
    403 }
    404 
    405 TF_BUILTIN(AsyncGeneratorResumeNext, AsyncGeneratorBuiltinsAssembler) {
    406   typedef AsyncGeneratorResumeNextDescriptor Descriptor;
    407   Node* const generator = Parameter(Descriptor::kGenerator);
    408   Node* const context = Parameter(Descriptor::kContext);
    409 
    410   // The penultimate step of proposal-async-iteration/#sec-asyncgeneratorresolve
    411   // and proposal-async-iteration/#sec-asyncgeneratorreject both recursively
    412   // invoke AsyncGeneratorResumeNext() again.
    413   //
    414   // This implementation does not implement this recursively, but instead
    415   // performs a loop in AsyncGeneratorResumeNext, which  continues as long as
    416   // there is an AsyncGeneratorRequest in the queue, and as long as the
    417   // generator is not suspended due to an AwaitExpression.
    418   VARIABLE(var_state, MachineRepresentation::kTaggedSigned,
    419            LoadGeneratorState(generator));
    420   VARIABLE(var_next, MachineRepresentation::kTagged,
    421            LoadFirstAsyncGeneratorRequestFromQueue(generator));
    422   Variable* loop_variables[] = {&var_state, &var_next};
    423   Label start(this, 2, loop_variables);
    424   Goto(&start);
    425   BIND(&start);
    426 
    427   CSA_ASSERT(this, IsGeneratorNotExecuting(generator));
    428 
    429   // Stop resuming if suspended for Await.
    430   ReturnIf(IsGeneratorAwaiting(generator), UndefinedConstant());
    431 
    432   // Stop resuming if request queue is empty.
    433   ReturnIf(IsUndefined(var_next.value()), UndefinedConstant());
    434 
    435   Node* const next = var_next.value();
    436   TNode<Smi> const resume_type =
    437       CAST(LoadResumeTypeFromAsyncGeneratorRequest(next));
    438 
    439   Label if_abrupt(this), if_normal(this), resume_generator(this);
    440   Branch(IsAbruptResumeType(resume_type), &if_abrupt, &if_normal);
    441   BIND(&if_abrupt);
    442   {
    443     Label settle_promise(this), if_return(this), if_throw(this);
    444     GotoIfNot(IsGeneratorStateSuspendedAtStart(var_state.value()),
    445               &settle_promise);
    446     CloseGenerator(generator);
    447     var_state.Bind(SmiConstant(JSGeneratorObject::kGeneratorClosed));
    448     Goto(&settle_promise);
    449 
    450     BIND(&settle_promise);
    451     Node* next_value = LoadValueFromAsyncGeneratorRequest(next);
    452     Branch(SmiEqual(resume_type, SmiConstant(JSGeneratorObject::kReturn)),
    453            &if_return, &if_throw);
    454 
    455     BIND(&if_return);
    456     // For "return" completions, await the sent value. If the Await succeeds,
    457     // and the generator is not closed, resume the generator with a "return"
    458     // completion to allow `finally` blocks to be evaluated. Otherwise, perform
    459     // AsyncGeneratorResolve(awaitedValue, true). If the await fails and the
    460     // generator is not closed, resume the generator with a "throw" completion.
    461     // If the generator was closed, perform AsyncGeneratorReject(thrownValue).
    462     // In all cases, the last step is to call AsyncGeneratorResumeNext.
    463     Node* is_caught = CallRuntime(Runtime::kAsyncGeneratorHasCatchHandlerForPC,
    464                                   context, generator);
    465     TailCallBuiltin(Builtins::kAsyncGeneratorReturn, context, generator,
    466                     next_value, is_caught);
    467 
    468     BIND(&if_throw);
    469     GotoIfNot(IsGeneratorStateClosed(var_state.value()), &resume_generator);
    470     CallBuiltin(Builtins::kAsyncGeneratorReject, context, generator,
    471                 next_value);
    472     var_next.Bind(LoadFirstAsyncGeneratorRequestFromQueue(generator));
    473     Goto(&start);
    474   }
    475 
    476   BIND(&if_normal);
    477   {
    478     GotoIfNot(IsGeneratorStateClosed(var_state.value()), &resume_generator);
    479     CallBuiltin(Builtins::kAsyncGeneratorResolve, context, generator,
    480                 UndefinedConstant(), TrueConstant());
    481     var_state.Bind(LoadGeneratorState(generator));
    482     var_next.Bind(LoadFirstAsyncGeneratorRequestFromQueue(generator));
    483     Goto(&start);
    484   }
    485 
    486   BIND(&resume_generator);
    487   {
    488     // Remember the {resume_type} for the {generator}.
    489     StoreObjectFieldNoWriteBarrier(
    490         generator, JSGeneratorObject::kResumeModeOffset, resume_type);
    491     CallStub(CodeFactory::ResumeGenerator(isolate()), context,
    492              LoadValueFromAsyncGeneratorRequest(next), generator);
    493     var_state.Bind(LoadGeneratorState(generator));
    494     var_next.Bind(LoadFirstAsyncGeneratorRequestFromQueue(generator));
    495     Goto(&start);
    496   }
    497 }
    498 
    499 TF_BUILTIN(AsyncGeneratorResolve, AsyncGeneratorBuiltinsAssembler) {
    500   Node* const generator = Parameter(Descriptor::kGenerator);
    501   Node* const value = Parameter(Descriptor::kValue);
    502   Node* const done = Parameter(Descriptor::kDone);
    503   Node* const context = Parameter(Descriptor::kContext);
    504 
    505   CSA_SLOW_ASSERT(this, TaggedIsAsyncGenerator(generator));
    506   CSA_ASSERT(this, Word32BinaryNot(IsGeneratorAwaiting(generator)));
    507 
    508   // If this assertion fails, the `value` component was not Awaited as it should
    509   // have been, per https://github.com/tc39/proposal-async-iteration/pull/102/.
    510   CSA_SLOW_ASSERT(this, TaggedDoesntHaveInstanceType(value, JS_PROMISE_TYPE));
    511 
    512   Node* const next = TakeFirstAsyncGeneratorRequestFromQueue(generator);
    513   Node* const promise = LoadPromiseFromAsyncGeneratorRequest(next);
    514 
    515   // Let iteratorResult be CreateIterResultObject(value, done).
    516   Node* const iter_result = Allocate(JSIteratorResult::kSize);
    517   {
    518     Node* map = LoadContextElement(LoadNativeContext(context),
    519                                    Context::ITERATOR_RESULT_MAP_INDEX);
    520     StoreMapNoWriteBarrier(iter_result, map);
    521     StoreObjectFieldRoot(iter_result, JSIteratorResult::kPropertiesOrHashOffset,
    522                          Heap::kEmptyFixedArrayRootIndex);
    523     StoreObjectFieldRoot(iter_result, JSIteratorResult::kElementsOffset,
    524                          Heap::kEmptyFixedArrayRootIndex);
    525     StoreObjectFieldNoWriteBarrier(iter_result, JSIteratorResult::kValueOffset,
    526                                    value);
    527     StoreObjectFieldNoWriteBarrier(iter_result, JSIteratorResult::kDoneOffset,
    528                                    done);
    529   }
    530 
    531   // We know that {iter_result} itself doesn't have any "then" property (a
    532   // freshly allocated IterResultObject only has "value" and "done" properties)
    533   // and we also know that the [[Prototype]] of {iter_result} is the intrinsic
    534   // %ObjectPrototype%. So we can skip the [[Resolve]] logic here completely
    535   // and directly call into the FulfillPromise operation if we can prove
    536   // that the %ObjectPrototype% also doesn't have any "then" property. This
    537   // is guarded by the Promise#then() protector.
    538   // If the PromiseHooks are enabled, we cannot take the shortcut here, since
    539   // the "promiseResolve" hook would not be fired otherwise.
    540   Label if_fast(this), if_slow(this, Label::kDeferred), return_promise(this);
    541   GotoIfForceSlowPath(&if_slow);
    542   GotoIf(IsPromiseHookEnabled(), &if_slow);
    543   Branch(IsPromiseThenProtectorCellInvalid(), &if_slow, &if_fast);
    544 
    545   BIND(&if_fast);
    546   {
    547     // Skip the "then" on {iter_result} and directly fulfill the {promise}
    548     // with the {iter_result}.
    549     CallBuiltin(Builtins::kFulfillPromise, context, promise, iter_result);
    550     Goto(&return_promise);
    551   }
    552 
    553   BIND(&if_slow);
    554   {
    555     // Perform Call(promiseCapability.[[Resolve]], undefined, iteratorResult).
    556     CallBuiltin(Builtins::kResolvePromise, context, promise, iter_result);
    557     Goto(&return_promise);
    558   }
    559 
    560   // Per spec, AsyncGeneratorResolve() returns undefined. However, for the
    561   // benefit of %TraceExit(), return the Promise.
    562   BIND(&return_promise);
    563   Return(promise);
    564 }
    565 
    566 TF_BUILTIN(AsyncGeneratorReject, AsyncGeneratorBuiltinsAssembler) {
    567   typedef AsyncGeneratorRejectDescriptor Descriptor;
    568   Node* const generator = Parameter(Descriptor::kGenerator);
    569   Node* const value = Parameter(Descriptor::kValue);
    570   Node* const context = Parameter(Descriptor::kContext);
    571 
    572   Node* const next = TakeFirstAsyncGeneratorRequestFromQueue(generator);
    573   Node* const promise = LoadPromiseFromAsyncGeneratorRequest(next);
    574 
    575   Return(CallBuiltin(Builtins::kRejectPromise, context, promise, value,
    576                      TrueConstant()));
    577 }
    578 
    579 TF_BUILTIN(AsyncGeneratorYield, AsyncGeneratorBuiltinsAssembler) {
    580   Node* const generator = Parameter(Descriptor::kGenerator);
    581   Node* const value = Parameter(Descriptor::kValue);
    582   Node* const is_caught = Parameter(Descriptor::kIsCaught);
    583   Node* const context = Parameter(Descriptor::kContext);
    584 
    585   Node* const request = LoadFirstAsyncGeneratorRequestFromQueue(generator);
    586   Node* const outer_promise = LoadPromiseFromAsyncGeneratorRequest(request);
    587 
    588   ContextInitializer init_closure_context = [&](Node* context) {
    589     StoreContextElementNoWriteBarrier(context, AwaitContext::kGeneratorSlot,
    590                                       generator);
    591   };
    592 
    593   const int on_resolve = Context::ASYNC_GENERATOR_YIELD_RESOLVE_SHARED_FUN;
    594   const int on_reject = Context::ASYNC_GENERATOR_AWAIT_REJECT_SHARED_FUN;
    595 
    596   SetGeneratorAwaiting(generator);
    597   Await(context, generator, value, outer_promise, AwaitContext::kLength,
    598         init_closure_context, on_resolve, on_reject, is_caught);
    599   Return(UndefinedConstant());
    600 }
    601 
    602 TF_BUILTIN(AsyncGeneratorYieldResolveClosure, AsyncGeneratorBuiltinsAssembler) {
    603   Node* const context = Parameter(Descriptor::kContext);
    604   Node* const value = Parameter(Descriptor::kValue);
    605   Node* const generator =
    606       LoadContextElement(context, AwaitContext::kGeneratorSlot);
    607 
    608   SetGeneratorNotAwaiting(generator);
    609 
    610   // Per proposal-async-iteration/#sec-asyncgeneratoryield step 9
    611   // Return ! AsyncGeneratorResolve(_F_.[[Generator]], _value_, *false*).
    612   CallBuiltin(Builtins::kAsyncGeneratorResolve, context, generator, value,
    613               FalseConstant());
    614 
    615   TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator);
    616 }
    617 
    618 TF_BUILTIN(AsyncGeneratorReturn, AsyncGeneratorBuiltinsAssembler) {
    619   // AsyncGeneratorReturn is called when resuming requests with "return" resume
    620   // modes. It is similar to AsyncGeneratorAwait(), but selects different
    621   // resolve/reject closures depending on whether or not the generator is marked
    622   // as closed.
    623   //
    624   // In particular, non-closed generators will resume the generator with either
    625   // "return" or "throw" resume modes, allowing finally blocks or catch blocks
    626   // to be evaluated, as if the `await` were performed within the body of the
    627   // generator. (per proposal-async-iteration/#sec-asyncgeneratoryield step 8.b)
    628   //
    629   // Closed generators do not resume the generator in the resolve/reject
    630   // closures, but instead simply perform AsyncGeneratorResolve or
    631   // AsyncGeneratorReject with the awaited value
    632   // (per proposal-async-iteration/#sec-asyncgeneratorresumenext step 10.b.i)
    633   //
    634   // In all cases, the final step is to jump back to AsyncGeneratorResumeNext.
    635   Node* const generator = Parameter(Descriptor::kGenerator);
    636   Node* const value = Parameter(Descriptor::kValue);
    637   Node* const is_caught = Parameter(Descriptor::kIsCaught);
    638   Node* const req = LoadFirstAsyncGeneratorRequestFromQueue(generator);
    639   CSA_ASSERT(this, IsNotUndefined(req));
    640 
    641   Label perform_await(this);
    642   VARIABLE(var_on_resolve, MachineType::PointerRepresentation(),
    643            IntPtrConstant(
    644                Context::ASYNC_GENERATOR_RETURN_CLOSED_RESOLVE_SHARED_FUN));
    645   VARIABLE(
    646       var_on_reject, MachineType::PointerRepresentation(),
    647       IntPtrConstant(Context::ASYNC_GENERATOR_RETURN_CLOSED_REJECT_SHARED_FUN));
    648 
    649   Node* const state = LoadGeneratorState(generator);
    650   GotoIf(IsGeneratorStateClosed(state), &perform_await);
    651   var_on_resolve.Bind(
    652       IntPtrConstant(Context::ASYNC_GENERATOR_RETURN_RESOLVE_SHARED_FUN));
    653   var_on_reject.Bind(
    654       IntPtrConstant(Context::ASYNC_GENERATOR_AWAIT_REJECT_SHARED_FUN));
    655   Goto(&perform_await);
    656 
    657   BIND(&perform_await);
    658 
    659   ContextInitializer init_closure_context = [&](Node* context) {
    660     StoreContextElementNoWriteBarrier(context, AwaitContext::kGeneratorSlot,
    661                                       generator);
    662   };
    663 
    664   SetGeneratorAwaiting(generator);
    665   Node* const context = Parameter(Descriptor::kContext);
    666   Node* const outer_promise = LoadPromiseFromAsyncGeneratorRequest(req);
    667   Await(context, generator, value, outer_promise, AwaitContext::kLength,
    668         init_closure_context, var_on_resolve.value(), var_on_reject.value(),
    669         is_caught);
    670 
    671   Return(UndefinedConstant());
    672 }
    673 
    674 // On-resolve closure for Await in AsyncGeneratorReturn
    675 // Resume the generator with "return" resume_mode, and finally perform
    676 // AsyncGeneratorResumeNext. Per
    677 // proposal-async-iteration/#sec-asyncgeneratoryield step 8.e
    678 TF_BUILTIN(AsyncGeneratorReturnResolveClosure,
    679            AsyncGeneratorBuiltinsAssembler) {
    680   Node* const context = Parameter(Descriptor::kContext);
    681   Node* const value = Parameter(Descriptor::kValue);
    682   AsyncGeneratorAwaitResumeClosure(context, value, JSGeneratorObject::kReturn);
    683 }
    684 
    685 // On-resolve closure for Await in AsyncGeneratorReturn
    686 // Perform AsyncGeneratorResolve({awaited_value}, true) and finally perform
    687 // AsyncGeneratorResumeNext.
    688 TF_BUILTIN(AsyncGeneratorReturnClosedResolveClosure,
    689            AsyncGeneratorBuiltinsAssembler) {
    690   Node* const context = Parameter(Descriptor::kContext);
    691   Node* const value = Parameter(Descriptor::kValue);
    692   Node* const generator =
    693       LoadContextElement(context, AwaitContext::kGeneratorSlot);
    694 
    695   SetGeneratorNotAwaiting(generator);
    696 
    697   // https://tc39.github.io/proposal-async-iteration/
    698   //    #async-generator-resume-next-return-processor-fulfilled step 2:
    699   //  Return ! AsyncGeneratorResolve(_F_.[[Generator]], _value_, *true*).
    700   CallBuiltin(Builtins::kAsyncGeneratorResolve, context, generator, value,
    701               TrueConstant());
    702 
    703   TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator);
    704 }
    705 
    706 TF_BUILTIN(AsyncGeneratorReturnClosedRejectClosure,
    707            AsyncGeneratorBuiltinsAssembler) {
    708   Node* const context = Parameter(Descriptor::kContext);
    709   Node* const value = Parameter(Descriptor::kValue);
    710   Node* const generator =
    711       LoadContextElement(context, AwaitContext::kGeneratorSlot);
    712 
    713   SetGeneratorNotAwaiting(generator);
    714 
    715   // https://tc39.github.io/proposal-async-iteration/
    716   //    #async-generator-resume-next-return-processor-rejected step 2:
    717   // Return ! AsyncGeneratorReject(_F_.[[Generator]], _reason_).
    718   CallBuiltin(Builtins::kAsyncGeneratorReject, context, generator, value);
    719 
    720   TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator);
    721 }
    722 
    723 }  // namespace internal
    724 }  // namespace v8
    725