builtins-async-generator-gen.cc 28.1 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 24 25

namespace v8 {
namespace internal {

using compiler::Node;

namespace {

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

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

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

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

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

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

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

62 63
  inline TNode<BoolT> IsGeneratorAwaiting(
      const TNode<JSGeneratorObject> generator) {
64
    TNode<Object> is_generator_awaiting =
65
        LoadObjectField(generator, JSAsyncGeneratorObject::kIsAwaitingOffset);
66
    return TaggedEqual(is_generator_awaiting, SmiConstant(1));
67 68
  }

69
  inline void SetGeneratorAwaiting(const TNode<JSGeneratorObject> generator) {
70 71 72 73
    CSA_ASSERT(this, Word32BinaryNot(IsGeneratorAwaiting(generator)));
    StoreObjectFieldNoWriteBarrier(
        generator, JSAsyncGeneratorObject::kIsAwaitingOffset, SmiConstant(1));
    CSA_ASSERT(this, IsGeneratorAwaiting(generator));
74 75
  }

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

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

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

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

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

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

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

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

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

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

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

// Shared implementation for the 3 Async Iterator protocol methods of Async
// Generators.
void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorEnqueue(
143 144 145
    CodeStubArguments* args, TNode<Context> context, TNode<Object> receiver,
    TNode<Object> value, JSAsyncGeneratorObject::ResumeMode resume_mode,
    const char* method_name) {
146 147 148 149 150
  // 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.
151
  TNode<JSPromise> promise = NewJSPromise(context);
152

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

  {
    Label done(this);
160 161
    const TNode<JSAsyncGeneratorObject> generator = CAST(receiver);
    const TNode<AsyncGeneratorRequest> req =
162 163 164 165 166 167 168 169
        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.
170
    TNode<Smi> continuation = LoadGeneratorState(generator);
171 172 173 174 175 176 177 178

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

    CallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator);

    Goto(&done);
179
    BIND(&done);
180
    args->PopAndReturn(promise);
181 182
  }

183
  BIND(&if_receiverisincompatible);
184
  {
185 186 187
    CallBuiltin(Builtins::kRejectPromise, context, promise,
                MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver,
                              context, StringConstant(method_name), receiver),
188
                TrueConstant());
189
    args->PopAndReturn(promise);
190 191 192
  }
}

193 194 195 196
TNode<AsyncGeneratorRequest>
AsyncGeneratorBuiltinsAssembler::AllocateAsyncGeneratorRequest(
    JSAsyncGeneratorObject::ResumeMode resume_mode, TNode<Object> resume_value,
    TNode<JSPromise> promise) {
197
  TNode<HeapObject> request = Allocate(AsyncGeneratorRequest::kSize);
198
  StoreMapNoWriteBarrier(request, RootIndex::kAsyncGeneratorRequestMap);
199 200 201 202 203 204 205 206 207 208
  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,
209
                       RootIndex::kUndefinedValue);
210
  return CAST(request);
211 212
}

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

219
  SetGeneratorNotAwaiting(generator);
220 221 222

  CSA_SLOW_ASSERT(this, IsGeneratorSuspended(generator));

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

228
  CallStub(CodeFactory::ResumeGenerator(isolate()), context, value, generator);
229

230
  TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator);
231 232 233 234
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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) {
386
  using Descriptor = AsyncGeneratorResumeNextDescriptor;
387 388 389
  const auto generator =
      Parameter<JSAsyncGeneratorObject>(Descriptor::kGenerator);
  const auto context = Parameter<Context>(Descriptor::kContext);
390 391 392 393 394 395 396 397 398

  // 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.
399 400 401 402
  TVARIABLE(Smi, var_state, LoadGeneratorState(generator));
  TVARIABLE(HeapObject, var_next,
            LoadFirstAsyncGeneratorRequestFromQueue(generator));
  Label start(this, {&var_state, &var_next});
403
  Goto(&start);
404
  BIND(&start);
405 406 407 408

  CSA_ASSERT(this, IsGeneratorNotExecuting(generator));

  // Stop resuming if suspended for Await.
409
  ReturnIf(IsGeneratorAwaiting(generator), UndefinedConstant());
410 411 412 413

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

414 415
  const TNode<AsyncGeneratorRequest> next = CAST(var_next.value());
  const TNode<Smi> resume_type = LoadResumeTypeFromAsyncGeneratorRequest(next);
416 417 418

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

428
    BIND(&settle_promise);
429
    TNode<Object> next_value = LoadValueFromAsyncGeneratorRequest(next);
430
    Branch(SmiEqual(resume_type, SmiConstant(JSGeneratorObject::kReturn)),
431 432 433 434 435 436 437 438 439 440
           &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.
441 442
    TNode<Object> is_caught = CallRuntime(
        Runtime::kAsyncGeneratorHasCatchHandlerForPC, context, generator);
443 444 445 446 447
    TailCallBuiltin(Builtins::kAsyncGeneratorReturn, context, generator,
                    next_value, is_caught);

    BIND(&if_throw);
    GotoIfNot(IsGeneratorStateClosed(var_state.value()), &resume_generator);
448
    CallBuiltin(Builtins::kAsyncGeneratorReject, context, generator,
449
                next_value);
450
    var_next = LoadFirstAsyncGeneratorRequestFromQueue(generator);
451 452 453
    Goto(&start);
  }

454
  BIND(&if_normal);
455 456 457 458
  {
    GotoIfNot(IsGeneratorStateClosed(var_state.value()), &resume_generator);
    CallBuiltin(Builtins::kAsyncGeneratorResolve, context, generator,
                UndefinedConstant(), TrueConstant());
459 460
    var_state = LoadGeneratorState(generator);
    var_next = LoadFirstAsyncGeneratorRequestFromQueue(generator);
461 462 463
    Goto(&start);
  }

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

TF_BUILTIN(AsyncGeneratorResolve, AsyncGeneratorBuiltinsAssembler) {
478 479 480 481 482
  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);
483

484
  CSA_ASSERT(this, Word32BinaryNot(IsGeneratorAwaiting(generator)));
485

486 487 488 489 490
  // 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.
491

492 493 494
  const TNode<AsyncGeneratorRequest> next =
      TakeFirstAsyncGeneratorRequestFromQueue(generator);
  const TNode<JSPromise> promise = LoadPromiseFromAsyncGeneratorRequest(next);
495

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

512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
  // 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);
  GotoIf(IsPromiseHookEnabled(), &if_slow);
  Branch(IsPromiseThenProtectorCellInvalid(), &if_slow, &if_fast);

  BIND(&if_fast);
  {
    // Skip the "then" on {iter_result} and directly fulfill the {promise}
    // with the {iter_result}.
    CallBuiltin(Builtins::kFulfillPromise, context, promise, iter_result);
    Goto(&return_promise);
  }

  BIND(&if_slow);
  {
    // Perform Call(promiseCapability.[[Resolve]], undefined, «iteratorResult»).
    CallBuiltin(Builtins::kResolvePromise, context, promise, iter_result);
    Goto(&return_promise);
  }
540 541 542

  // Per spec, AsyncGeneratorResolve() returns undefined. However, for the
  // benefit of %TraceExit(), return the Promise.
543
  BIND(&return_promise);
544
  Return(promise);
545 546 547
}

TF_BUILTIN(AsyncGeneratorReject, AsyncGeneratorBuiltinsAssembler) {
548
  using Descriptor = AsyncGeneratorRejectDescriptor;
549 550 551 552
  const auto generator =
      Parameter<JSAsyncGeneratorObject>(Descriptor::kGenerator);
  const auto value = Parameter<Object>(Descriptor::kValue);
  const auto context = Parameter<Context>(Descriptor::kContext);
553

554 555 556
  TNode<AsyncGeneratorRequest> next =
      TakeFirstAsyncGeneratorRequestFromQueue(generator);
  TNode<JSPromise> promise = LoadPromiseFromAsyncGeneratorRequest(next);
557

558
  Return(CallBuiltin(Builtins::kRejectPromise, context, promise, value,
559
                     TrueConstant()));
560 561
}

562
TF_BUILTIN(AsyncGeneratorYield, AsyncGeneratorBuiltinsAssembler) {
563 564 565 566
  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);
567

568 569
  const TNode<AsyncGeneratorRequest> request =
      CAST(LoadFirstAsyncGeneratorRequestFromQueue(generator));
570
  const TNode<JSPromise> outer_promise =
571
      LoadPromiseFromAsyncGeneratorRequest(request);
572

573
  SetGeneratorAwaiting(generator);
574 575 576
  Await(context, generator, value, outer_promise,
        AsyncGeneratorYieldResolveSharedFunConstant(),
        AsyncGeneratorAwaitRejectSharedFunConstant(), is_caught);
577
  Return(UndefinedConstant());
578 579
}

580
TF_BUILTIN(AsyncGeneratorYieldResolveClosure, AsyncGeneratorBuiltinsAssembler) {
581 582
  const auto context = Parameter<Context>(Descriptor::kContext);
  const auto value = Parameter<Object>(Descriptor::kValue);
583 584
  const TNode<JSAsyncGeneratorObject> generator =
      CAST(LoadContextElement(context, Context::EXTENSION_INDEX));
585

586
  SetGeneratorNotAwaiting(generator);
587 588 589

  // Per proposal-async-iteration/#sec-asyncgeneratoryield step 9
  // Return ! AsyncGeneratorResolve(_F_.[[Generator]], _value_, *false*).
590
  CallBuiltin(Builtins::kAsyncGeneratorResolve, context, generator, value,
591 592 593 594 595
              FalseConstant());

  TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator);
}

596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612
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.
613 614 615
  const auto generator = Parameter<JSGeneratorObject>(Descriptor::kGenerator);
  const auto value = Parameter<Object>(Descriptor::kValue);
  const auto is_caught = Parameter<Oddball>(Descriptor::kIsCaught);
616 617
  const TNode<AsyncGeneratorRequest> req =
      CAST(LoadFirstAsyncGeneratorRequestFromQueue(generator));
618

619
  Label perform_await(this);
620 621 622 623 624
  TVARIABLE(SharedFunctionInfo, var_on_resolve,
            AsyncGeneratorReturnClosedResolveSharedFunConstant());

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

626
  const TNode<Smi> state = LoadGeneratorState(generator);
627
  GotoIf(IsGeneratorStateClosed(state), &perform_await);
628 629 630
  var_on_resolve = AsyncGeneratorReturnResolveSharedFunConstant();
  var_on_reject = AsyncGeneratorAwaitRejectSharedFunConstant();

631
  Goto(&perform_await);
632

633
  BIND(&perform_await);
634

635
  SetGeneratorAwaiting(generator);
636
  auto context = Parameter<Context>(Descriptor::kContext);
637
  const TNode<JSPromise> outer_promise =
638
      LoadPromiseFromAsyncGeneratorRequest(req);
639 640
  Await(context, generator, value, outer_promise, var_on_resolve.value(),
        var_on_reject.value(), is_caught);
641

642 643 644
  Return(UndefinedConstant());
}

645 646 647 648
// 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
649 650
TF_BUILTIN(AsyncGeneratorReturnResolveClosure,
           AsyncGeneratorBuiltinsAssembler) {
651 652
  const auto context = Parameter<Context>(Descriptor::kContext);
  const auto value = Parameter<Object>(Descriptor::kValue);
653
  AsyncGeneratorAwaitResumeClosure(context, value, JSGeneratorObject::kReturn);
654 655 656 657 658
}

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

666
  SetGeneratorNotAwaiting(generator);
667

668 669
  // https://tc39.github.io/proposal-async-iteration/
  //    #async-generator-resume-next-return-processor-fulfilled step 2:
670
  //  Return ! AsyncGeneratorResolve(_F_.[[Generator]], _value_, *true*).
671
  CallBuiltin(Builtins::kAsyncGeneratorResolve, context, generator, value,
672 673 674 675 676
              TrueConstant());

  TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator);
}

677 678
TF_BUILTIN(AsyncGeneratorReturnClosedRejectClosure,
           AsyncGeneratorBuiltinsAssembler) {
679 680
  const auto context = Parameter<Context>(Descriptor::kContext);
  const auto value = Parameter<Object>(Descriptor::kValue);
681 682
  const TNode<JSAsyncGeneratorObject> generator =
      CAST(LoadContextElement(context, Context::EXTENSION_INDEX));
683

684
  SetGeneratorNotAwaiting(generator);
685

686 687
  // https://tc39.github.io/proposal-async-iteration/
  //    #async-generator-resume-next-return-processor-rejected step 2:
688
  // Return ! AsyncGeneratorReject(_F_.[[Generator]], _reason_).
689
  CallBuiltin(Builtins::kAsyncGeneratorReject, context, generator, value);
690 691 692 693

  TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator);
}

694 695
}  // namespace internal
}  // namespace v8