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