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