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