builtins-async-generator-gen.cc 27.9 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/builtins/builtins-async-gen.h"
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
8 9
#include "src/codegen/code-factory.h"
#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 15 16 17 18 19 20 21 22 23

namespace v8 {
namespace internal {

namespace {

class AsyncGeneratorBuiltinsAssembler : public AsyncBuiltinsAssembler {
 public:
  explicit AsyncGeneratorBuiltinsAssembler(CodeAssemblerState* state)
      : AsyncBuiltinsAssembler(state) {}

24 25 26 27
  inline TNode<Smi> LoadGeneratorState(
      const TNode<JSGeneratorObject> generator) {
    return LoadObjectField<Smi>(generator,
                                JSGeneratorObject::kContinuationOffset);
28 29
  }

30
  inline TNode<BoolT> IsGeneratorStateClosed(const TNode<Smi> state) {
31 32
    return SmiEqual(state, SmiConstant(JSGeneratorObject::kGeneratorClosed));
  }
33 34
  inline TNode<BoolT> IsGeneratorClosed(
      const TNode<JSGeneratorObject> generator) {
35 36 37
    return IsGeneratorStateClosed(LoadGeneratorState(generator));
  }

38
  inline TNode<BoolT> IsGeneratorStateSuspended(const TNode<Smi> state) {
39 40 41
    return SmiGreaterThanOrEqual(state, SmiConstant(0));
  }

42 43
  inline TNode<BoolT> IsGeneratorSuspended(
      const TNode<JSGeneratorObject> generator) {
44 45 46
    return IsGeneratorStateSuspended(LoadGeneratorState(generator));
  }

47
  inline TNode<BoolT> IsGeneratorStateSuspendedAtStart(const TNode<Smi> state) {
48 49 50
    return SmiEqual(state, SmiConstant(0));
  }

51
  inline TNode<BoolT> IsGeneratorStateNotExecuting(const TNode<Smi> state) {
52 53 54
    return SmiNotEqual(state,
                       SmiConstant(JSGeneratorObject::kGeneratorExecuting));
  }
55 56
  inline TNode<BoolT> IsGeneratorNotExecuting(
      const TNode<JSGeneratorObject> generator) {
57 58 59
    return IsGeneratorStateNotExecuting(LoadGeneratorState(generator));
  }

60 61
  inline TNode<BoolT> IsGeneratorAwaiting(
      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 70
    StoreObjectFieldNoWriteBarrier(
        generator, JSAsyncGeneratorObject::kIsAwaitingOffset, SmiConstant(1));
71
    CSA_DCHECK(this, IsGeneratorAwaiting(generator));
72 73
  }

74 75
  inline void SetGeneratorNotAwaiting(
      const TNode<JSGeneratorObject> generator) {
76
    CSA_DCHECK(this, IsGeneratorAwaiting(generator));
77 78
    StoreObjectFieldNoWriteBarrier(
        generator, JSAsyncGeneratorObject::kIsAwaitingOffset, SmiConstant(0));
79
    CSA_DCHECK(this, Word32BinaryNot(IsGeneratorAwaiting(generator)));
80 81
  }

82
  inline void CloseGenerator(const TNode<JSGeneratorObject> generator) {
83 84 85 86 87
    StoreObjectFieldNoWriteBarrier(
        generator, JSGeneratorObject::kContinuationOffset,
        SmiConstant(JSGeneratorObject::kGeneratorClosed));
  }

88 89 90 91
  inline TNode<HeapObject> LoadFirstAsyncGeneratorRequestFromQueue(
      const TNode<JSGeneratorObject> generator) {
    return LoadObjectField<HeapObject>(generator,
                                       JSAsyncGeneratorObject::kQueueOffset);
92 93
  }

94 95 96 97
  inline TNode<Smi> LoadResumeTypeFromAsyncGeneratorRequest(
      const TNode<AsyncGeneratorRequest> request) {
    return LoadObjectField<Smi>(request,
                                AsyncGeneratorRequest::kResumeModeOffset);
98 99
  }

100 101 102 103
  inline TNode<JSPromise> LoadPromiseFromAsyncGeneratorRequest(
      const TNode<AsyncGeneratorRequest> request) {
    return LoadObjectField<JSPromise>(request,
                                      AsyncGeneratorRequest::kPromiseOffset);
104 105
  }

106 107
  inline TNode<Object> LoadValueFromAsyncGeneratorRequest(
      const TNode<AsyncGeneratorRequest> request) {
108 109 110
    return LoadObjectField(request, AsyncGeneratorRequest::kValueOffset);
  }

111
  inline TNode<BoolT> IsAbruptResumeType(const TNode<Smi> resume_type) {
112 113 114
    return SmiNotEqual(resume_type, SmiConstant(JSGeneratorObject::kNext));
  }

115 116
  void AsyncGeneratorEnqueue(CodeStubArguments* args, TNode<Context> context,
                             TNode<Object> receiver, TNode<Object> value,
117 118 119
                             JSAsyncGeneratorObject::ResumeMode resume_mode,
                             const char* method_name);

120 121 122 123
  TNode<AsyncGeneratorRequest> TakeFirstAsyncGeneratorRequestFromQueue(
      TNode<JSAsyncGeneratorObject> generator);
  void AddAsyncGeneratorRequestToQueue(TNode<JSAsyncGeneratorObject> generator,
                                       TNode<AsyncGeneratorRequest> request);
124

125 126 127
  TNode<AsyncGeneratorRequest> AllocateAsyncGeneratorRequest(
      JSAsyncGeneratorObject::ResumeMode resume_mode,
      TNode<Object> resume_value, TNode<JSPromise> promise);
128 129 130 131 132

  // Shared implementation of the catchable and uncatchable variations of Await
  // for AsyncGenerators.
  template <typename Descriptor>
  void AsyncGeneratorAwait(bool is_catchable);
133
  void AsyncGeneratorAwaitResumeClosure(
134
      TNode<Context> context, TNode<Object> value,
135 136 137 138 139 140
      JSAsyncGeneratorObject::ResumeMode resume_mode);
};

// Shared implementation for the 3 Async Iterator protocol methods of Async
// Generators.
void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorEnqueue(
141 142 143
    CodeStubArguments* args, TNode<Context> context, TNode<Object> receiver,
    TNode<Object> value, JSAsyncGeneratorObject::ResumeMode resume_mode,
    const char* method_name) {
144 145 146 147 148
  // AsyncGeneratorEnqueue produces a new Promise, and appends it to the list
  // of async generator requests to be executed. If the generator is not
  // presently executing, then this method will loop through, processing each
  // request from front to back.
  // This loop resides in AsyncGeneratorResumeNext.
149
  TNode<JSPromise> promise = NewJSPromise(context);
150

151 152 153 154
  Label if_receiverisincompatible(this, Label::kDeferred);
  GotoIf(TaggedIsSmi(receiver), &if_receiverisincompatible);
  GotoIfNot(HasInstanceType(CAST(receiver), JS_ASYNC_GENERATOR_OBJECT_TYPE),
            &if_receiverisincompatible);
155 156 157

  {
    Label done(this);
158 159
    const TNode<JSAsyncGeneratorObject> generator = CAST(receiver);
    const TNode<AsyncGeneratorRequest> req =
160 161 162 163 164 165 166 167
        AllocateAsyncGeneratorRequest(resume_mode, value, promise);

    AddAsyncGeneratorRequestToQueue(generator, req);

    // Let state be generator.[[AsyncGeneratorState]]
    // If state is not "executing", then
    //     Perform AsyncGeneratorResumeNext(Generator)
    // Check if the {receiver} is running or already closed.
168
    TNode<Smi> continuation = LoadGeneratorState(generator);
169 170 171 172 173

    GotoIf(SmiEqual(continuation,
                    SmiConstant(JSAsyncGeneratorObject::kGeneratorExecuting)),
           &done);

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 185
                MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver,
                              context, StringConstant(method_name), receiver),
186
                TrueConstant());
187
    args->PopAndReturn(promise);
188 189 190
  }
}

191 192 193 194
TNode<AsyncGeneratorRequest>
AsyncGeneratorBuiltinsAssembler::AllocateAsyncGeneratorRequest(
    JSAsyncGeneratorObject::ResumeMode resume_mode, TNode<Object> resume_value,
    TNode<JSPromise> promise) {
195
  TNode<HeapObject> request = Allocate(AsyncGeneratorRequest::kSize);
196
  StoreMapNoWriteBarrier(request, RootIndex::kAsyncGeneratorRequestMap);
197 198 199 200 201 202 203 204 205 206
  StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kNextOffset,
                                 UndefinedConstant());
  StoreObjectFieldNoWriteBarrier(request,
                                 AsyncGeneratorRequest::kResumeModeOffset,
                                 SmiConstant(resume_mode));
  StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kValueOffset,
                                 resume_value);
  StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kPromiseOffset,
                                 promise);
  StoreObjectFieldRoot(request, AsyncGeneratorRequest::kNextOffset,
207
                       RootIndex::kUndefinedValue);
208
  return CAST(request);
209 210
}

211
void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwaitResumeClosure(
212
    TNode<Context> context, TNode<Object> value,
213
    JSAsyncGeneratorObject::ResumeMode resume_mode) {
214 215
  const TNode<JSAsyncGeneratorObject> generator =
      CAST(LoadContextElement(context, Context::EXTENSION_INDEX));
216

217
  SetGeneratorNotAwaiting(generator);
218

219
  CSA_SLOW_DCHECK(this, IsGeneratorSuspended(generator));
220

221 222 223 224 225
  // Remember the {resume_mode} for the {generator}.
  StoreObjectFieldNoWriteBarrier(generator,
                                 JSGeneratorObject::kResumeModeOffset,
                                 SmiConstant(resume_mode));

226
  CallStub(CodeFactory::ResumeGenerator(isolate()), context, value, generator);
227

228
  TailCallBuiltin(Builtin::kAsyncGeneratorResumeNext, context, generator);
229 230 231 232
}

template <typename Descriptor>
void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwait(bool is_catchable) {
233 234 235 236
  auto async_generator_object =
      Parameter<JSAsyncGeneratorObject>(Descriptor::kAsyncGeneratorObject);
  auto value = Parameter<Object>(Descriptor::kValue);
  auto context = Parameter<Context>(Descriptor::kContext);
237

238 239 240 241
  TNode<AsyncGeneratorRequest> request =
      CAST(LoadFirstAsyncGeneratorRequestFromQueue(async_generator_object));
  TNode<JSPromise> outer_promise = LoadObjectField<JSPromise>(
      request, AsyncGeneratorRequest::kPromiseOffset);
242

243 244 245
  Await(context, async_generator_object, value, outer_promise,
        AsyncGeneratorAwaitResolveSharedFunConstant(),
        AsyncGeneratorAwaitRejectSharedFunConstant(), is_catchable);
246
  SetGeneratorAwaiting(async_generator_object);
247 248 249 250
  Return(UndefinedConstant());
}

void AsyncGeneratorBuiltinsAssembler::AddAsyncGeneratorRequestToQueue(
251 252 253
    TNode<JSAsyncGeneratorObject> generator,
    TNode<AsyncGeneratorRequest> request) {
  TVARIABLE(HeapObject, var_current);
254 255
  Label empty(this), loop(this, &var_current), done(this);

256 257
  var_current = LoadObjectField<HeapObject>(
      generator, JSAsyncGeneratorObject::kQueueOffset);
258 259
  Branch(IsUndefined(var_current.value()), &empty, &loop);

260
  BIND(&empty);
261 262 263 264 265
  {
    StoreObjectField(generator, JSAsyncGeneratorObject::kQueueOffset, request);
    Goto(&done);
  }

266
  BIND(&loop);
267 268
  {
    Label loop_next(this), next_empty(this);
269 270 271
    TNode<AsyncGeneratorRequest> current = CAST(var_current.value());
    TNode<HeapObject> next = LoadObjectField<HeapObject>(
        current, AsyncGeneratorRequest::kNextOffset);
272 273

    Branch(IsUndefined(next), &next_empty, &loop_next);
274
    BIND(&next_empty);
275 276 277 278 279
    {
      StoreObjectField(current, AsyncGeneratorRequest::kNextOffset, request);
      Goto(&done);
    }

280
    BIND(&loop_next);
281
    {
282
      var_current = next;
283 284 285
      Goto(&loop);
    }
  }
286
  BIND(&done);
287 288
}

289 290 291
TNode<AsyncGeneratorRequest>
AsyncGeneratorBuiltinsAssembler::TakeFirstAsyncGeneratorRequestFromQueue(
    TNode<JSAsyncGeneratorObject> generator) {
292 293
  // Removes and returns the first AsyncGeneratorRequest from a
  // JSAsyncGeneratorObject's queue. Asserts that the queue is not empty.
294 295
  TNode<AsyncGeneratorRequest> request = LoadObjectField<AsyncGeneratorRequest>(
      generator, JSAsyncGeneratorObject::kQueueOffset);
296

297 298
  TNode<Object> next =
      LoadObjectField(request, AsyncGeneratorRequest::kNextOffset);
299 300 301 302 303 304 305 306 307

  StoreObjectField(generator, JSAsyncGeneratorObject::kQueueOffset, next);
  return request;
}
}  // namespace

// https://tc39.github.io/proposal-async-iteration/
// Section #sec-asyncgenerator-prototype-next
TF_BUILTIN(AsyncGeneratorPrototypeNext, AsyncGeneratorBuiltinsAssembler) {
308 309
  const int kValueArg = 0;

310
  TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
311
      UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
312 313
  CodeStubArguments args(this, argc);

314 315
  TNode<Object> generator = args.GetReceiver();
  TNode<Object> value = args.GetOptionalArgumentValue(kValueArg);
316
  auto context = Parameter<Context>(Descriptor::kContext);
317 318

  AsyncGeneratorEnqueue(&args, context, generator, value,
319 320 321 322 323 324 325
                        JSAsyncGeneratorObject::kNext,
                        "[AsyncGenerator].prototype.next");
}

// https://tc39.github.io/proposal-async-iteration/
// Section #sec-asyncgenerator-prototype-return
TF_BUILTIN(AsyncGeneratorPrototypeReturn, AsyncGeneratorBuiltinsAssembler) {
326 327
  const int kValueArg = 0;

328
  TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
329
      UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
330 331
  CodeStubArguments args(this, argc);

332 333
  TNode<Object> generator = args.GetReceiver();
  TNode<Object> value = args.GetOptionalArgumentValue(kValueArg);
334
  auto context = Parameter<Context>(Descriptor::kContext);
335 336

  AsyncGeneratorEnqueue(&args, context, generator, value,
337 338 339 340 341 342 343
                        JSAsyncGeneratorObject::kReturn,
                        "[AsyncGenerator].prototype.return");
}

// https://tc39.github.io/proposal-async-iteration/
// Section #sec-asyncgenerator-prototype-throw
TF_BUILTIN(AsyncGeneratorPrototypeThrow, AsyncGeneratorBuiltinsAssembler) {
344 345
  const int kValueArg = 0;

346
  TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
347
      UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
348 349
  CodeStubArguments args(this, argc);

350 351
  TNode<Object> generator = args.GetReceiver();
  TNode<Object> value = args.GetOptionalArgumentValue(kValueArg);
352
  auto context = Parameter<Context>(Descriptor::kContext);
353 354

  AsyncGeneratorEnqueue(&args, context, generator, value,
355 356 357 358
                        JSAsyncGeneratorObject::kThrow,
                        "[AsyncGenerator].prototype.throw");
}

359
TF_BUILTIN(AsyncGeneratorAwaitResolveClosure, AsyncGeneratorBuiltinsAssembler) {
360 361
  auto value = Parameter<Object>(Descriptor::kValue);
  auto context = Parameter<Context>(Descriptor::kContext);
362 363
  AsyncGeneratorAwaitResumeClosure(context, value,
                                   JSAsyncGeneratorObject::kNext);
364 365
}

366
TF_BUILTIN(AsyncGeneratorAwaitRejectClosure, AsyncGeneratorBuiltinsAssembler) {
367 368
  auto value = Parameter<Object>(Descriptor::kValue);
  auto context = Parameter<Context>(Descriptor::kContext);
369 370
  AsyncGeneratorAwaitResumeClosure(context, value,
                                   JSAsyncGeneratorObject::kThrow);
371 372 373 374 375 376 377 378 379 380 381 382 383
}

TF_BUILTIN(AsyncGeneratorAwaitUncaught, AsyncGeneratorBuiltinsAssembler) {
  const bool kIsCatchable = false;
  AsyncGeneratorAwait<Descriptor>(kIsCatchable);
}

TF_BUILTIN(AsyncGeneratorAwaitCaught, AsyncGeneratorBuiltinsAssembler) {
  const bool kIsCatchable = true;
  AsyncGeneratorAwait<Descriptor>(kIsCatchable);
}

TF_BUILTIN(AsyncGeneratorResumeNext, AsyncGeneratorBuiltinsAssembler) {
384 385 386
  const auto generator =
      Parameter<JSAsyncGeneratorObject>(Descriptor::kGenerator);
  const auto context = Parameter<Context>(Descriptor::kContext);
387 388 389 390 391 392 393 394 395

  // The penultimate step of proposal-async-iteration/#sec-asyncgeneratorresolve
  // and proposal-async-iteration/#sec-asyncgeneratorreject both recursively
  // invoke AsyncGeneratorResumeNext() again.
  //
  // This implementation does not implement this recursively, but instead
  // performs a loop in AsyncGeneratorResumeNext, which  continues as long as
  // there is an AsyncGeneratorRequest in the queue, and as long as the
  // generator is not suspended due to an AwaitExpression.
396 397 398 399
  TVARIABLE(Smi, var_state, LoadGeneratorState(generator));
  TVARIABLE(HeapObject, var_next,
            LoadFirstAsyncGeneratorRequestFromQueue(generator));
  Label start(this, {&var_state, &var_next});
400
  Goto(&start);
401
  BIND(&start);
402

403
  CSA_DCHECK(this, IsGeneratorNotExecuting(generator));
404 405

  // Stop resuming if suspended for Await.
406
  ReturnIf(IsGeneratorAwaiting(generator), UndefinedConstant());
407 408 409 410

  // Stop resuming if request queue is empty.
  ReturnIf(IsUndefined(var_next.value()), UndefinedConstant());

411 412
  const TNode<AsyncGeneratorRequest> next = CAST(var_next.value());
  const TNode<Smi> resume_type = LoadResumeTypeFromAsyncGeneratorRequest(next);
413 414 415

  Label if_abrupt(this), if_normal(this), resume_generator(this);
  Branch(IsAbruptResumeType(resume_type), &if_abrupt, &if_normal);
416
  BIND(&if_abrupt);
417
  {
418
    Label settle_promise(this), if_return(this), if_throw(this);
419 420 421
    GotoIfNot(IsGeneratorStateSuspendedAtStart(var_state.value()),
              &settle_promise);
    CloseGenerator(generator);
422
    var_state = SmiConstant(JSGeneratorObject::kGeneratorClosed);
423 424
    Goto(&settle_promise);

425
    BIND(&settle_promise);
426
    TNode<Object> next_value = LoadValueFromAsyncGeneratorRequest(next);
427
    Branch(SmiEqual(resume_type, SmiConstant(JSGeneratorObject::kReturn)),
428 429 430 431 432 433 434 435 436 437
           &if_return, &if_throw);

    BIND(&if_return);
    // For "return" completions, await the sent value. If the Await succeeds,
    // and the generator is not closed, resume the generator with a "return"
    // completion to allow `finally` blocks to be evaluated. Otherwise, perform
    // AsyncGeneratorResolve(awaitedValue, true). If the await fails and the
    // generator is not closed, resume the generator with a "throw" completion.
    // If the generator was closed, perform AsyncGeneratorReject(thrownValue).
    // In all cases, the last step is to call AsyncGeneratorResumeNext.
438 439
    TNode<Object> is_caught = CallRuntime(
        Runtime::kAsyncGeneratorHasCatchHandlerForPC, context, generator);
440
    TailCallBuiltin(Builtin::kAsyncGeneratorReturn, context, generator,
441 442 443 444
                    next_value, is_caught);

    BIND(&if_throw);
    GotoIfNot(IsGeneratorStateClosed(var_state.value()), &resume_generator);
445
    CallBuiltin(Builtin::kAsyncGeneratorReject, context, generator, next_value);
446
    var_next = LoadFirstAsyncGeneratorRequestFromQueue(generator);
447 448 449
    Goto(&start);
  }

450
  BIND(&if_normal);
451 452
  {
    GotoIfNot(IsGeneratorStateClosed(var_state.value()), &resume_generator);
453
    CallBuiltin(Builtin::kAsyncGeneratorResolve, context, generator,
454
                UndefinedConstant(), TrueConstant());
455 456
    var_state = LoadGeneratorState(generator);
    var_next = LoadFirstAsyncGeneratorRequestFromQueue(generator);
457 458 459
    Goto(&start);
  }

460
  BIND(&resume_generator);
461
  {
462 463 464
    // Remember the {resume_type} for the {generator}.
    StoreObjectFieldNoWriteBarrier(
        generator, JSGeneratorObject::kResumeModeOffset, resume_type);
465
    CallStub(CodeFactory::ResumeGenerator(isolate()), context,
466
             LoadValueFromAsyncGeneratorRequest(next), generator);
467 468
    var_state = LoadGeneratorState(generator);
    var_next = LoadFirstAsyncGeneratorRequestFromQueue(generator);
469 470 471 472 473
    Goto(&start);
  }
}

TF_BUILTIN(AsyncGeneratorResolve, AsyncGeneratorBuiltinsAssembler) {
474 475 476 477 478
  const auto generator =
      Parameter<JSAsyncGeneratorObject>(Descriptor::kGenerator);
  const auto value = Parameter<Object>(Descriptor::kValue);
  const auto done = Parameter<Object>(Descriptor::kDone);
  const auto context = Parameter<Context>(Descriptor::kContext);
479

480
  CSA_DCHECK(this, Word32BinaryNot(IsGeneratorAwaiting(generator)));
481

482 483 484 485 486
  // This operation should be called only when the `value` parameter has been
  // Await-ed. Typically, this means `value` is not a JSPromise value. However,
  // it may be a JSPromise value whose "then" method has been overridden to a
  // non-callable value. This can't be checked with assertions due to being
  // observable, but keep it in mind.
487

488 489 490
  const TNode<AsyncGeneratorRequest> next =
      TakeFirstAsyncGeneratorRequestFromQueue(generator);
  const TNode<JSPromise> promise = LoadPromiseFromAsyncGeneratorRequest(next);
491

492
  // Let iteratorResult be CreateIterResultObject(value, done).
493
  const TNode<HeapObject> iter_result = Allocate(JSIteratorResult::kSize);
494
  {
495 496
    TNode<Map> map = CAST(LoadContextElement(
        LoadNativeContext(context), Context::ITERATOR_RESULT_MAP_INDEX));
497 498
    StoreMapNoWriteBarrier(iter_result, map);
    StoreObjectFieldRoot(iter_result, JSIteratorResult::kPropertiesOrHashOffset,
499
                         RootIndex::kEmptyFixedArray);
500
    StoreObjectFieldRoot(iter_result, JSIteratorResult::kElementsOffset,
501
                         RootIndex::kEmptyFixedArray);
502 503 504 505 506
    StoreObjectFieldNoWriteBarrier(iter_result, JSIteratorResult::kValueOffset,
                                   value);
    StoreObjectFieldNoWriteBarrier(iter_result, JSIteratorResult::kDoneOffset,
                                   done);
  }
507

508 509 510 511 512 513 514 515 516 517 518
  // We know that {iter_result} itself doesn't have any "then" property (a
  // freshly allocated IterResultObject only has "value" and "done" properties)
  // and we also know that the [[Prototype]] of {iter_result} is the intrinsic
  // %ObjectPrototype%. So we can skip the [[Resolve]] logic here completely
  // and directly call into the FulfillPromise operation if we can prove
  // that the %ObjectPrototype% also doesn't have any "then" property. This
  // is guarded by the Promise#then() protector.
  // If the PromiseHooks are enabled, we cannot take the shortcut here, since
  // the "promiseResolve" hook would not be fired otherwise.
  Label if_fast(this), if_slow(this, Label::kDeferred), return_promise(this);
  GotoIfForceSlowPath(&if_slow);
519
  GotoIf(IsIsolatePromiseHookEnabledOrHasAsyncEventDelegate(), &if_slow);
520 521 522 523 524 525
  Branch(IsPromiseThenProtectorCellInvalid(), &if_slow, &if_fast);

  BIND(&if_fast);
  {
    // Skip the "then" on {iter_result} and directly fulfill the {promise}
    // with the {iter_result}.
526
    CallBuiltin(Builtin::kFulfillPromise, context, promise, iter_result);
527 528 529 530 531 532
    Goto(&return_promise);
  }

  BIND(&if_slow);
  {
    // Perform Call(promiseCapability.[[Resolve]], undefined, «iteratorResult»).
533
    CallBuiltin(Builtin::kResolvePromise, context, promise, iter_result);
534 535
    Goto(&return_promise);
  }
536 537 538

  // Per spec, AsyncGeneratorResolve() returns undefined. However, for the
  // benefit of %TraceExit(), return the Promise.
539
  BIND(&return_promise);
540
  Return(promise);
541 542 543
}

TF_BUILTIN(AsyncGeneratorReject, AsyncGeneratorBuiltinsAssembler) {
544 545 546 547
  const auto generator =
      Parameter<JSAsyncGeneratorObject>(Descriptor::kGenerator);
  const auto value = Parameter<Object>(Descriptor::kValue);
  const auto context = Parameter<Context>(Descriptor::kContext);
548

549 550 551
  TNode<AsyncGeneratorRequest> next =
      TakeFirstAsyncGeneratorRequestFromQueue(generator);
  TNode<JSPromise> promise = LoadPromiseFromAsyncGeneratorRequest(next);
552

553
  Return(CallBuiltin(Builtin::kRejectPromise, context, promise, value,
554
                     TrueConstant()));
555 556
}

557
TF_BUILTIN(AsyncGeneratorYield, AsyncGeneratorBuiltinsAssembler) {
558 559 560 561
  const auto generator = Parameter<JSGeneratorObject>(Descriptor::kGenerator);
  const auto value = Parameter<Object>(Descriptor::kValue);
  const auto is_caught = Parameter<Oddball>(Descriptor::kIsCaught);
  const auto context = Parameter<Context>(Descriptor::kContext);
562

563 564
  const TNode<AsyncGeneratorRequest> request =
      CAST(LoadFirstAsyncGeneratorRequestFromQueue(generator));
565
  const TNode<JSPromise> outer_promise =
566
      LoadPromiseFromAsyncGeneratorRequest(request);
567

568 569 570
  Await(context, generator, value, outer_promise,
        AsyncGeneratorYieldResolveSharedFunConstant(),
        AsyncGeneratorAwaitRejectSharedFunConstant(), is_caught);
571
  SetGeneratorAwaiting(generator);
572
  Return(UndefinedConstant());
573 574
}

575
TF_BUILTIN(AsyncGeneratorYieldResolveClosure, AsyncGeneratorBuiltinsAssembler) {
576 577
  const auto context = Parameter<Context>(Descriptor::kContext);
  const auto value = Parameter<Object>(Descriptor::kValue);
578 579
  const TNode<JSAsyncGeneratorObject> generator =
      CAST(LoadContextElement(context, Context::EXTENSION_INDEX));
580

581
  SetGeneratorNotAwaiting(generator);
582 583 584

  // Per proposal-async-iteration/#sec-asyncgeneratoryield step 9
  // Return ! AsyncGeneratorResolve(_F_.[[Generator]], _value_, *false*).
585
  CallBuiltin(Builtin::kAsyncGeneratorResolve, context, generator, value,
586 587
              FalseConstant());

588
  TailCallBuiltin(Builtin::kAsyncGeneratorResumeNext, context, generator);
589 590
}

591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
TF_BUILTIN(AsyncGeneratorReturn, AsyncGeneratorBuiltinsAssembler) {
  // AsyncGeneratorReturn is called when resuming requests with "return" resume
  // modes. It is similar to AsyncGeneratorAwait(), but selects different
  // resolve/reject closures depending on whether or not the generator is marked
  // as closed.
  //
  // In particular, non-closed generators will resume the generator with either
  // "return" or "throw" resume modes, allowing finally blocks or catch blocks
  // to be evaluated, as if the `await` were performed within the body of the
  // generator. (per proposal-async-iteration/#sec-asyncgeneratoryield step 8.b)
  //
  // Closed generators do not resume the generator in the resolve/reject
  // closures, but instead simply perform AsyncGeneratorResolve or
  // AsyncGeneratorReject with the awaited value
  // (per proposal-async-iteration/#sec-asyncgeneratorresumenext step 10.b.i)
  //
  // In all cases, the final step is to jump back to AsyncGeneratorResumeNext.
608 609 610
  const auto generator = Parameter<JSGeneratorObject>(Descriptor::kGenerator);
  const auto value = Parameter<Object>(Descriptor::kValue);
  const auto is_caught = Parameter<Oddball>(Descriptor::kIsCaught);
611 612
  const TNode<AsyncGeneratorRequest> req =
      CAST(LoadFirstAsyncGeneratorRequestFromQueue(generator));
613

614
  Label perform_await(this);
615 616 617 618 619
  TVARIABLE(SharedFunctionInfo, var_on_resolve,
            AsyncGeneratorReturnClosedResolveSharedFunConstant());

  TVARIABLE(SharedFunctionInfo, var_on_reject,
            AsyncGeneratorReturnClosedRejectSharedFunConstant());
620

621
  const TNode<Smi> state = LoadGeneratorState(generator);
622
  GotoIf(IsGeneratorStateClosed(state), &perform_await);
623 624 625
  var_on_resolve = AsyncGeneratorReturnResolveSharedFunConstant();
  var_on_reject = AsyncGeneratorAwaitRejectSharedFunConstant();

626
  Goto(&perform_await);
627

628
  BIND(&perform_await);
629

630
  SetGeneratorAwaiting(generator);
631
  auto context = Parameter<Context>(Descriptor::kContext);
632
  const TNode<JSPromise> outer_promise =
633
      LoadPromiseFromAsyncGeneratorRequest(req);
634 635
  Await(context, generator, value, outer_promise, var_on_resolve.value(),
        var_on_reject.value(), is_caught);
636

637 638 639
  Return(UndefinedConstant());
}

640 641 642 643
// On-resolve closure for Await in AsyncGeneratorReturn
// Resume the generator with "return" resume_mode, and finally perform
// AsyncGeneratorResumeNext. Per
// proposal-async-iteration/#sec-asyncgeneratoryield step 8.e
644 645
TF_BUILTIN(AsyncGeneratorReturnResolveClosure,
           AsyncGeneratorBuiltinsAssembler) {
646 647
  const auto context = Parameter<Context>(Descriptor::kContext);
  const auto value = Parameter<Object>(Descriptor::kValue);
648
  AsyncGeneratorAwaitResumeClosure(context, value, JSGeneratorObject::kReturn);
649 650 651 652 653
}

// On-resolve closure for Await in AsyncGeneratorReturn
// Perform AsyncGeneratorResolve({awaited_value}, true) and finally perform
// AsyncGeneratorResumeNext.
654 655
TF_BUILTIN(AsyncGeneratorReturnClosedResolveClosure,
           AsyncGeneratorBuiltinsAssembler) {
656 657
  const auto context = Parameter<Context>(Descriptor::kContext);
  const auto value = Parameter<Object>(Descriptor::kValue);
658 659
  const TNode<JSAsyncGeneratorObject> generator =
      CAST(LoadContextElement(context, Context::EXTENSION_INDEX));
660

661
  SetGeneratorNotAwaiting(generator);
662

663 664
  // https://tc39.github.io/proposal-async-iteration/
  //    #async-generator-resume-next-return-processor-fulfilled step 2:
665
  //  Return ! AsyncGeneratorResolve(_F_.[[Generator]], _value_, *true*).
666
  CallBuiltin(Builtin::kAsyncGeneratorResolve, context, generator, value,
667 668
              TrueConstant());

669
  TailCallBuiltin(Builtin::kAsyncGeneratorResumeNext, context, generator);
670 671
}

672 673
TF_BUILTIN(AsyncGeneratorReturnClosedRejectClosure,
           AsyncGeneratorBuiltinsAssembler) {
674 675
  const auto context = Parameter<Context>(Descriptor::kContext);
  const auto value = Parameter<Object>(Descriptor::kValue);
676 677
  const TNode<JSAsyncGeneratorObject> generator =
      CAST(LoadContextElement(context, Context::EXTENSION_INDEX));
678

679
  SetGeneratorNotAwaiting(generator);
680

681 682
  // https://tc39.github.io/proposal-async-iteration/
  //    #async-generator-resume-next-return-processor-rejected step 2:
683
  // Return ! AsyncGeneratorReject(_F_.[[Generator]], _reason_).
684
  CallBuiltin(Builtin::kAsyncGeneratorReject, context, generator, value);
685

686
  TailCallBuiltin(Builtin::kAsyncGeneratorResumeNext, context, generator);
687 688
}

689 690
}  // namespace internal
}  // namespace v8