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