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
14namespace v8 {
15namespace internal {
16
17namespace {
18
19class AsyncGeneratorBuiltinsAssembler : public AsyncBuiltinsAssembler {
20 public:
21  explicit AsyncGeneratorBuiltinsAssembler(CodeAssemblerState* state)
22      : AsyncBuiltinsAssembler(state) {}
23
24  inline TNode<Smi> LoadGeneratorState(
25      const TNode<JSGeneratorObject> generator) {
26    return LoadObjectField<Smi>(generator,
27                                JSGeneratorObject::kContinuationOffset);
28  }
29
30  inline TNode<BoolT> IsGeneratorStateClosed(const TNode<Smi> state) {
31    return SmiEqual(state, SmiConstant(JSGeneratorObject::kGeneratorClosed));
32  }
33  inline TNode<BoolT> IsGeneratorClosed(
34      const TNode<JSGeneratorObject> generator) {
35    return IsGeneratorStateClosed(LoadGeneratorState(generator));
36  }
37
38  inline TNode<BoolT> IsGeneratorStateSuspended(const TNode<Smi> state) {
39    return SmiGreaterThanOrEqual(state, SmiConstant(0));
40  }
41
42  inline TNode<BoolT> IsGeneratorSuspended(
43      const TNode<JSGeneratorObject> generator) {
44    return IsGeneratorStateSuspended(LoadGeneratorState(generator));
45  }
46
47  inline TNode<BoolT> IsGeneratorStateSuspendedAtStart(const TNode<Smi> state) {
48    return SmiEqual(state, SmiConstant(0));
49  }
50
51  inline TNode<BoolT> IsGeneratorStateNotExecuting(const TNode<Smi> state) {
52    return SmiNotEqual(state,
53                       SmiConstant(JSGeneratorObject::kGeneratorExecuting));
54  }
55  inline TNode<BoolT> IsGeneratorNotExecuting(
56      const TNode<JSGeneratorObject> generator) {
57    return IsGeneratorStateNotExecuting(LoadGeneratorState(generator));
58  }
59
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
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
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
82  inline void CloseGenerator(const TNode<JSGeneratorObject> generator) {
83    StoreObjectFieldNoWriteBarrier(
84        generator, JSGeneratorObject::kContinuationOffset,
85        SmiConstant(JSGeneratorObject::kGeneratorClosed));
86  }
87
88  inline TNode<HeapObject> LoadFirstAsyncGeneratorRequestFromQueue(
89      const TNode<JSGeneratorObject> generator) {
90    return LoadObjectField<HeapObject>(generator,
91                                       JSAsyncGeneratorObject::kQueueOffset);
92  }
93
94  inline TNode<Smi> LoadResumeTypeFromAsyncGeneratorRequest(
95      const TNode<AsyncGeneratorRequest> request) {
96    return LoadObjectField<Smi>(request,
97                                AsyncGeneratorRequest::kResumeModeOffset);
98  }
99
100  inline TNode<JSPromise> LoadPromiseFromAsyncGeneratorRequest(
101      const TNode<AsyncGeneratorRequest> request) {
102    return LoadObjectField<JSPromise>(request,
103                                      AsyncGeneratorRequest::kPromiseOffset);
104  }
105
106  inline TNode<Object> LoadValueFromAsyncGeneratorRequest(
107      const TNode<AsyncGeneratorRequest> request) {
108    return LoadObjectField(request, AsyncGeneratorRequest::kValueOffset);
109  }
110
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.
140void 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
191TNode<AsyncGeneratorRequest>
192AsyncGeneratorBuiltinsAssembler::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
211void 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
250template <typename Descriptor>
251void 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
269void 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
308TNode<AsyncGeneratorRequest>
309AsyncGeneratorBuiltinsAssembler::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
326TF_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
344TF_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
362TF_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
378TF_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
385TF_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
392TF_BUILTIN(AsyncGeneratorAwaitUncaught, AsyncGeneratorBuiltinsAssembler) {
393  const bool kIsCatchable = false;
394  AsyncGeneratorAwait<Descriptor>(kIsCatchable);
395}
396
397TF_BUILTIN(AsyncGeneratorAwaitCaught, AsyncGeneratorBuiltinsAssembler) {
398  const bool kIsCatchable = true;
399  AsyncGeneratorAwait<Descriptor>(kIsCatchable);
400}
401
402TF_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
492TF_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
562TF_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
576TF_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
594TF_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
610TF_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
663TF_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.
673TF_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
691TF_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