v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
builtins-async-generator-gen.cc
Go to the documentation of this file.
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
12
13namespace v8 {
14namespace internal {
15
17
18namespace {
19
20class AsyncGeneratorBuiltinsAssembler : public AsyncBuiltinsAssembler {
21 public:
22 explicit AsyncGeneratorBuiltinsAssembler(CodeAssemblerState* state)
23 : AsyncBuiltinsAssembler(state) {}
24
25 inline TNode<Smi> LoadGeneratorState(
26 const TNode<JSGeneratorObject> generator) {
27 return LoadObjectField<Smi>(generator,
28 JSGeneratorObject::kContinuationOffset);
29 }
30
31 inline TNode<BoolT> IsGeneratorStateClosed(const TNode<Smi> state) {
32 return SmiEqual(state, SmiConstant(JSGeneratorObject::kGeneratorClosed));
33 }
34 inline TNode<BoolT> IsGeneratorClosed(
35 const TNode<JSGeneratorObject> generator) {
36 return IsGeneratorStateClosed(LoadGeneratorState(generator));
37 }
38
39 inline TNode<BoolT> IsGeneratorStateSuspended(const TNode<Smi> state) {
40 return SmiGreaterThanOrEqual(state, SmiConstant(0));
41 }
42
43 inline TNode<BoolT> IsGeneratorSuspended(
44 const TNode<JSGeneratorObject> generator) {
45 return IsGeneratorStateSuspended(LoadGeneratorState(generator));
46 }
47
48 inline TNode<BoolT> IsGeneratorStateSuspendedAtStart(const TNode<Smi> state) {
49 return SmiEqual(state, SmiConstant(0));
50 }
51
52 inline TNode<BoolT> IsGeneratorStateNotExecuting(const TNode<Smi> state) {
53 return SmiNotEqual(state,
55 }
56 inline TNode<BoolT> IsGeneratorNotExecuting(
57 const TNode<JSGeneratorObject> generator) {
58 return IsGeneratorStateNotExecuting(LoadGeneratorState(generator));
59 }
60
61 inline TNode<BoolT> IsGeneratorAwaiting(
62 const TNode<JSGeneratorObject> generator) {
63 TNode<Object> is_generator_awaiting =
64 LoadObjectField(generator, JSAsyncGeneratorObject::kIsAwaitingOffset);
65 return TaggedEqual(is_generator_awaiting, SmiConstant(1));
66 }
67
68 inline void SetGeneratorAwaiting(const TNode<JSGeneratorObject> generator) {
69 CSA_DCHECK(this, Word32BinaryNot(IsGeneratorAwaiting(generator)));
70 StoreObjectFieldNoWriteBarrier(
71 generator, JSAsyncGeneratorObject::kIsAwaitingOffset, SmiConstant(1));
72 CSA_DCHECK(this, IsGeneratorAwaiting(generator));
73 }
74
75 inline void SetGeneratorNotAwaiting(
76 const TNode<JSGeneratorObject> generator) {
77 CSA_DCHECK(this, IsGeneratorAwaiting(generator));
78 StoreObjectFieldNoWriteBarrier(
79 generator, JSAsyncGeneratorObject::kIsAwaitingOffset, SmiConstant(0));
80 CSA_DCHECK(this, Word32BinaryNot(IsGeneratorAwaiting(generator)));
81 }
82
83 inline void CloseGenerator(const TNode<JSGeneratorObject> generator) {
84 StoreObjectFieldNoWriteBarrier(
85 generator, JSGeneratorObject::kContinuationOffset,
87 }
88
89 inline TNode<HeapObject> LoadFirstAsyncGeneratorRequestFromQueue(
90 const TNode<JSGeneratorObject> generator) {
91 return LoadObjectField<HeapObject>(generator,
92 JSAsyncGeneratorObject::kQueueOffset);
93 }
94
95 inline TNode<Smi> LoadResumeTypeFromAsyncGeneratorRequest(
96 const TNode<AsyncGeneratorRequest> request) {
97 return LoadObjectField<Smi>(request,
98 AsyncGeneratorRequest::kResumeModeOffset);
99 }
100
101 inline TNode<JSPromise> LoadPromiseFromAsyncGeneratorRequest(
102 const TNode<AsyncGeneratorRequest> request) {
103 return LoadObjectField<JSPromise>(request,
104 AsyncGeneratorRequest::kPromiseOffset);
105 }
106
107 inline TNode<Object> LoadValueFromAsyncGeneratorRequest(
108 const TNode<AsyncGeneratorRequest> request) {
109 return LoadObjectField(request, AsyncGeneratorRequest::kValueOffset);
110 }
111
112 inline TNode<BoolT> IsAbruptResumeType(const TNode<Smi> resume_type) {
113 return SmiNotEqual(resume_type, SmiConstant(JSGeneratorObject::kNext));
114 }
115
116 void AsyncGeneratorEnqueue(CodeStubArguments* args, TNode<Context> context,
117 TNode<Object> receiver, TNode<Object> value,
118 JSAsyncGeneratorObject::ResumeMode resume_mode,
119 const char* method_name);
120
121 TNode<AsyncGeneratorRequest> TakeFirstAsyncGeneratorRequestFromQueue(
122 TNode<JSAsyncGeneratorObject> generator);
123 void AddAsyncGeneratorRequestToQueue(TNode<JSAsyncGeneratorObject> generator,
124 TNode<AsyncGeneratorRequest> request);
125
126 TNode<AsyncGeneratorRequest> AllocateAsyncGeneratorRequest(
127 JSAsyncGeneratorObject::ResumeMode resume_mode,
128 TNode<Object> resume_value, TNode<JSPromise> promise);
129
130 // Shared implementation of the catchable and uncatchable variations of Await
131 // for AsyncGenerators.
132 template <typename Descriptor>
133 void AsyncGeneratorAwait();
134 void AsyncGeneratorAwaitResume(
135 TNode<Context> context,
136 TNode<JSAsyncGeneratorObject> async_generator_object, TNode<Object> value,
137 JSAsyncGeneratorObject::ResumeMode resume_mode);
138 void AsyncGeneratorAwaitResumeClosure(
139 TNode<Context> context, TNode<Object> value,
140 JSAsyncGeneratorObject::ResumeMode resume_mode);
141 void AsyncGeneratorReturnClosedReject(
142 TNode<Context> context,
143 TNode<JSAsyncGeneratorObject> async_generator_object,
144 TNode<Object> value);
145};
146
147// Shared implementation for the 3 Async Iterator protocol methods of Async
148// Generators.
149void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorEnqueue(
150 CodeStubArguments* args, TNode<Context> context, TNode<Object> receiver,
151 TNode<Object> value, JSAsyncGeneratorObject::ResumeMode resume_mode,
152 const char* method_name) {
153 // AsyncGeneratorEnqueue produces a new Promise, and appends it to the list
154 // of async generator requests to be executed. If the generator is not
155 // presently executing, then this method will loop through, processing each
156 // request from front to back.
157 // This loop resides in AsyncGeneratorResumeNext.
158 TNode<JSPromise> promise = NewJSPromise(context);
159
160 Label if_receiverisincompatible(this, Label::kDeferred);
161 GotoIf(TaggedIsSmi(receiver), &if_receiverisincompatible);
162 GotoIfNot(HasInstanceType(CAST(receiver), JS_ASYNC_GENERATOR_OBJECT_TYPE),
163 &if_receiverisincompatible);
164
165 {
166 Label done(this);
167 const TNode<JSAsyncGeneratorObject> generator = CAST(receiver);
168 const TNode<AsyncGeneratorRequest> req =
169 AllocateAsyncGeneratorRequest(resume_mode, value, promise);
170
171 AddAsyncGeneratorRequestToQueue(generator, req);
172
173 // Let state be generator.[[AsyncGeneratorState]]
174 // If state is not "executing", then
175 // Perform AsyncGeneratorResumeNext(Generator)
176 // Check if the {receiver} is running or already closed.
177 TNode<Smi> continuation = LoadGeneratorState(generator);
178
179 GotoIf(SmiEqual(continuation,
180 SmiConstant(JSAsyncGeneratorObject::kGeneratorExecuting)),
181 &done);
182
183 CallBuiltin(Builtin::kAsyncGeneratorResumeNext, context, generator);
184
185 Goto(&done);
186 BIND(&done);
187 args->PopAndReturn(promise);
188 }
189
190 BIND(&if_receiverisincompatible);
191 {
192 CallBuiltin(Builtin::kRejectPromise, context, promise,
193 MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver,
194 context, StringConstant(method_name), receiver),
195 TrueConstant());
196 args->PopAndReturn(promise);
197 }
198}
199
200TNode<AsyncGeneratorRequest>
201AsyncGeneratorBuiltinsAssembler::AllocateAsyncGeneratorRequest(
202 JSAsyncGeneratorObject::ResumeMode resume_mode, TNode<Object> resume_value,
203 TNode<JSPromise> promise) {
204 TNode<HeapObject> request = Allocate(AsyncGeneratorRequest::kSize);
205 StoreMapNoWriteBarrier(request, RootIndex::kAsyncGeneratorRequestMap);
206 StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kNextOffset,
208 StoreObjectFieldNoWriteBarrier(request,
209 AsyncGeneratorRequest::kResumeModeOffset,
210 SmiConstant(resume_mode));
211 StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kValueOffset,
212 resume_value);
213 StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kPromiseOffset,
214 promise);
215 StoreObjectFieldRoot(request, AsyncGeneratorRequest::kNextOffset,
216 RootIndex::kUndefinedValue);
217 return CAST(request);
218}
219
220void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwaitResume(
221 TNode<Context> context,
222 TNode<JSAsyncGeneratorObject> async_generator_object, TNode<Object> value,
223 JSAsyncGeneratorObject::ResumeMode resume_mode) {
224 SetGeneratorNotAwaiting(async_generator_object);
225
226 CSA_SLOW_DCHECK(this, IsGeneratorSuspended(async_generator_object));
227
228 // Remember the {resume_mode} for the {async_generator_object}.
229 StoreObjectFieldNoWriteBarrier(async_generator_object,
230 JSGeneratorObject::kResumeModeOffset,
231 SmiConstant(resume_mode));
232
233 CallBuiltin(Builtin::kResumeGeneratorTrampoline, context, value,
234 async_generator_object);
235
236 TailCallBuiltin(Builtin::kAsyncGeneratorResumeNext, context,
237 async_generator_object);
238}
239
240void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwaitResumeClosure(
241 TNode<Context> context, TNode<Object> value,
242 JSAsyncGeneratorObject::ResumeMode resume_mode) {
243 const TNode<JSAsyncGeneratorObject> async_generator_object =
244 CAST(LoadContextElement(context, Context::EXTENSION_INDEX));
245
246 AsyncGeneratorAwaitResume(context, async_generator_object, value,
247 resume_mode);
248}
249
250template <typename Descriptor>
251void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwait() {
252 auto async_generator_object =
253 Parameter<JSAsyncGeneratorObject>(Descriptor::kAsyncGeneratorObject);
254 auto value = Parameter<JSAny>(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 RootIndex::kAsyncGeneratorAwaitResolveClosureSharedFun,
264 RootIndex::kAsyncGeneratorAwaitRejectClosureSharedFun);
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
323void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorReturnClosedReject(
324 TNode<Context> context, TNode<JSAsyncGeneratorObject> generator,
325 TNode<Object> value) {
326 SetGeneratorNotAwaiting(generator);
327
328 // https://tc39.github.io/proposal-async-iteration/
329 // #async-generator-resume-next-return-processor-rejected step 2:
330 // Return ! AsyncGeneratorReject(_F_.[[Generator]], _reason_).
331 CallBuiltin(Builtin::kAsyncGeneratorReject, context, generator, value);
332
333 TailCallBuiltin(Builtin::kAsyncGeneratorResumeNext, context, generator);
334}
335} // namespace
336
337// https://tc39.github.io/proposal-async-iteration/
338// Section #sec-asyncgenerator-prototype-next
339TF_BUILTIN(AsyncGeneratorPrototypeNext, AsyncGeneratorBuiltinsAssembler) {
340 const int kValueArg = 0;
341
342 TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
343 UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
344 CodeStubArguments args(this, argc);
345
346 TNode<Object> generator = args.GetReceiver();
347 TNode<Object> value = args.GetOptionalArgumentValue(kValueArg);
348 auto context = Parameter<Context>(Descriptor::kContext);
349
350 AsyncGeneratorEnqueue(&args, context, generator, value,
351 JSAsyncGeneratorObject::kNext,
352 "[AsyncGenerator].prototype.next");
353}
354
355// https://tc39.github.io/proposal-async-iteration/
356// Section #sec-asyncgenerator-prototype-return
357TF_BUILTIN(AsyncGeneratorPrototypeReturn, AsyncGeneratorBuiltinsAssembler) {
358 const int kValueArg = 0;
359
360 TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
361 UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
362 CodeStubArguments args(this, argc);
363
364 TNode<Object> generator = args.GetReceiver();
365 TNode<Object> value = args.GetOptionalArgumentValue(kValueArg);
366 auto context = Parameter<Context>(Descriptor::kContext);
367
368 AsyncGeneratorEnqueue(&args, context, generator, value,
369 JSAsyncGeneratorObject::kReturn,
370 "[AsyncGenerator].prototype.return");
371}
372
373// https://tc39.github.io/proposal-async-iteration/
374// Section #sec-asyncgenerator-prototype-throw
375TF_BUILTIN(AsyncGeneratorPrototypeThrow, AsyncGeneratorBuiltinsAssembler) {
376 const int kValueArg = 0;
377
378 TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
379 UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
380 CodeStubArguments args(this, argc);
381
382 TNode<Object> generator = args.GetReceiver();
383 TNode<Object> value = args.GetOptionalArgumentValue(kValueArg);
384 auto context = Parameter<Context>(Descriptor::kContext);
385
386 AsyncGeneratorEnqueue(&args, context, generator, value,
387 JSAsyncGeneratorObject::kThrow,
388 "[AsyncGenerator].prototype.throw");
389}
390
391TF_BUILTIN(AsyncGeneratorAwaitResolveClosure, AsyncGeneratorBuiltinsAssembler) {
392 auto value = Parameter<JSAny>(Descriptor::kValue);
393 auto context = Parameter<Context>(Descriptor::kContext);
394 AsyncGeneratorAwaitResumeClosure(context, value,
395 JSAsyncGeneratorObject::kNext);
396}
397
398TF_BUILTIN(AsyncGeneratorAwaitRejectClosure, AsyncGeneratorBuiltinsAssembler) {
399 auto value = Parameter<JSAny>(Descriptor::kValue);
400 auto context = Parameter<Context>(Descriptor::kContext);
401 // Restart in Rethrow mode, as this exception was already thrown and we don't
402 // want to trigger a second debug break event or change the message location.
403 AsyncGeneratorAwaitResumeClosure(context, value,
404 JSAsyncGeneratorObject::kRethrow);
405}
406
407TF_BUILTIN(AsyncGeneratorAwait, AsyncGeneratorBuiltinsAssembler) {
408 AsyncGeneratorAwait<Descriptor>();
409}
410
411TF_BUILTIN(AsyncGeneratorResumeNext, AsyncGeneratorBuiltinsAssembler) {
412 const auto generator =
413 Parameter<JSAsyncGeneratorObject>(Descriptor::kGenerator);
414 const auto context = Parameter<Context>(Descriptor::kContext);
415
416 // The penultimate step of proposal-async-iteration/#sec-asyncgeneratorresolve
417 // and proposal-async-iteration/#sec-asyncgeneratorreject both recursively
418 // invoke AsyncGeneratorResumeNext() again.
419 //
420 // This implementation does not implement this recursively, but instead
421 // performs a loop in AsyncGeneratorResumeNext, which continues as long as
422 // there is an AsyncGeneratorRequest in the queue, and as long as the
423 // generator is not suspended due to an AwaitExpression.
424 TVARIABLE(Smi, var_state, LoadGeneratorState(generator));
425 TVARIABLE(HeapObject, var_next,
426 LoadFirstAsyncGeneratorRequestFromQueue(generator));
427 Label start(this, {&var_state, &var_next});
428 Goto(&start);
429 BIND(&start);
430
431 CSA_DCHECK(this, IsGeneratorNotExecuting(generator));
432
433 // Stop resuming if suspended for Await.
434 ReturnIf(IsGeneratorAwaiting(generator), UndefinedConstant());
435
436 // Stop resuming if request queue is empty.
437 ReturnIf(IsUndefined(var_next.value()), UndefinedConstant());
438
439 const TNode<AsyncGeneratorRequest> next = CAST(var_next.value());
440 const TNode<Smi> resume_type = LoadResumeTypeFromAsyncGeneratorRequest(next);
441
442 Label if_abrupt(this), if_normal(this), resume_generator(this);
443 Branch(IsAbruptResumeType(resume_type), &if_abrupt, &if_normal);
444 BIND(&if_abrupt);
445 {
446 Label settle_promise(this), if_return(this), if_throw(this);
447 GotoIfNot(IsGeneratorStateSuspendedAtStart(var_state.value()),
448 &settle_promise);
449 CloseGenerator(generator);
450 var_state = SmiConstant(JSGeneratorObject::kGeneratorClosed);
451 Goto(&settle_promise);
452
453 BIND(&settle_promise);
454 TNode<Object> next_value = LoadValueFromAsyncGeneratorRequest(next);
455 Branch(SmiEqual(resume_type, SmiConstant(JSGeneratorObject::kReturn)),
456 &if_return, &if_throw);
457
458 BIND(&if_return);
459 // For "return" completions, await the sent value. If the Await succeeds,
460 // and the generator is not closed, resume the generator with a "return"
461 // completion to allow `finally` blocks to be evaluated. Otherwise, perform
462 // AsyncGeneratorResolve(awaitedValue, true). If the await fails and the
463 // generator is not closed, resume the generator with a "throw" completion.
464 // If the generator was closed, perform AsyncGeneratorReject(thrownValue).
465 // In all cases, the last step is to call AsyncGeneratorResumeNext.
466 TailCallBuiltin(Builtin::kAsyncGeneratorReturn, context, generator,
467 next_value);
468
469 BIND(&if_throw);
470 GotoIfNot(IsGeneratorStateClosed(var_state.value()), &resume_generator);
471 CallBuiltin(Builtin::kAsyncGeneratorReject, context, generator, next_value);
472 var_next = LoadFirstAsyncGeneratorRequestFromQueue(generator);
473 Goto(&start);
474 }
475
476 BIND(&if_normal);
477 {
478 GotoIfNot(IsGeneratorStateClosed(var_state.value()), &resume_generator);
479 CallBuiltin(Builtin::kAsyncGeneratorResolve, context, generator,
480 UndefinedConstant(), TrueConstant());
481 var_state = LoadGeneratorState(generator);
482 var_next = 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
492 CallBuiltin(Builtin::kResumeGeneratorTrampoline, context,
493 LoadValueFromAsyncGeneratorRequest(next), generator);
494 var_state = LoadGeneratorState(generator);
495 var_next = LoadFirstAsyncGeneratorRequestFromQueue(generator);
496 Goto(&start);
497 }
498}
499
500TF_BUILTIN(AsyncGeneratorResolve, AsyncGeneratorBuiltinsAssembler) {
501 const auto generator =
502 Parameter<JSAsyncGeneratorObject>(Descriptor::kGenerator);
503 const auto value = Parameter<JSAny>(Descriptor::kValue);
504 const auto done = Parameter<Object>(Descriptor::kDone);
505 const auto context = Parameter<Context>(Descriptor::kContext);
506
507 CSA_DCHECK(this, Word32BinaryNot(IsGeneratorAwaiting(generator)));
508
509 // This operation should be called only when the `value` parameter has been
510 // Await-ed. Typically, this means `value` is not a JSPromise value. However,
511 // it may be a JSPromise value whose "then" method has been overridden to a
512 // non-callable value. This can't be checked with assertions due to being
513 // observable, but keep it in mind.
514
516 TakeFirstAsyncGeneratorRequestFromQueue(generator);
517 const TNode<JSPromise> promise = LoadPromiseFromAsyncGeneratorRequest(next);
518
519 // Let iteratorResult be CreateIterResultObject(value, done).
520 const TNode<HeapObject> iter_result = Allocate(JSIteratorResult::kSize);
521 {
522 TNode<Map> map = CAST(LoadContextElement(
523 LoadNativeContext(context), Context::ITERATOR_RESULT_MAP_INDEX));
524 StoreMapNoWriteBarrier(iter_result, map);
525 StoreObjectFieldRoot(iter_result, JSIteratorResult::kPropertiesOrHashOffset,
526 RootIndex::kEmptyFixedArray);
527 StoreObjectFieldRoot(iter_result, JSIteratorResult::kElementsOffset,
528 RootIndex::kEmptyFixedArray);
529 StoreObjectFieldNoWriteBarrier(iter_result, JSIteratorResult::kValueOffset,
530 value);
531 StoreObjectFieldNoWriteBarrier(iter_result, JSIteratorResult::kDoneOffset,
532 done);
533 }
534
535 // We know that {iter_result} itself doesn't have any "then" property (a
536 // freshly allocated IterResultObject only has "value" and "done" properties)
537 // and we also know that the [[Prototype]] of {iter_result} is the intrinsic
538 // %ObjectPrototype%. So we can skip the [[Resolve]] logic here completely
539 // and directly call into the FulfillPromise operation if we can prove
540 // that the %ObjectPrototype% also doesn't have any "then" property. This
541 // is guarded by the Promise#then() protector.
542 // If the PromiseHooks are enabled, we cannot take the shortcut here, since
543 // the "promiseResolve" hook would not be fired otherwise.
544 Label if_fast(this), if_slow(this, Label::kDeferred), return_promise(this);
545 GotoIfForceSlowPath(&if_slow);
546 GotoIf(IsIsolatePromiseHookEnabledOrHasAsyncEventDelegate(), &if_slow);
547 Branch(IsPromiseThenProtectorCellInvalid(), &if_slow, &if_fast);
548
549 BIND(&if_fast);
550 {
551 // Skip the "then" on {iter_result} and directly fulfill the {promise}
552 // with the {iter_result}.
553 CallBuiltin(Builtin::kFulfillPromise, context, promise, iter_result);
554 Goto(&return_promise);
555 }
556
557 BIND(&if_slow);
558 {
559 // Perform Call(promiseCapability.[[Resolve]], undefined, «iteratorResult»).
560 CallBuiltin(Builtin::kResolvePromise, context, promise, iter_result);
561 Goto(&return_promise);
562 }
563
564 // Per spec, AsyncGeneratorResolve() returns undefined. However, for the
565 // benefit of %TraceExit(), return the Promise.
566 BIND(&return_promise);
567 Return(promise);
568}
569
570TF_BUILTIN(AsyncGeneratorReject, AsyncGeneratorBuiltinsAssembler) {
571 const auto generator =
572 Parameter<JSAsyncGeneratorObject>(Descriptor::kGenerator);
573 const auto value = Parameter<JSAny>(Descriptor::kValue);
574 const auto context = Parameter<Context>(Descriptor::kContext);
575
577 TakeFirstAsyncGeneratorRequestFromQueue(generator);
578 TNode<JSPromise> promise = LoadPromiseFromAsyncGeneratorRequest(next);
579
580 // No debug event needed, there was already a debug event that got us here.
581 Return(CallBuiltin(Builtin::kRejectPromise, context, promise, value,
582 FalseConstant()));
583}
584
585TF_BUILTIN(AsyncGeneratorYieldWithAwait, AsyncGeneratorBuiltinsAssembler) {
586 const auto generator = Parameter<JSGeneratorObject>(Descriptor::kGenerator);
587 const auto value = Parameter<JSAny>(Descriptor::kValue);
588 const auto context = Parameter<Context>(Descriptor::kContext);
589
590 const TNode<AsyncGeneratorRequest> request =
591 CAST(LoadFirstAsyncGeneratorRequestFromQueue(generator));
592 const TNode<JSPromise> outer_promise =
593 LoadPromiseFromAsyncGeneratorRequest(request);
594
595 Await(context, generator, value, outer_promise,
596 RootIndex::kAsyncGeneratorYieldWithAwaitResolveClosureSharedFun,
597 RootIndex::kAsyncGeneratorAwaitRejectClosureSharedFun);
598 SetGeneratorAwaiting(generator);
599 Return(UndefinedConstant());
600}
601
602TF_BUILTIN(AsyncGeneratorYieldWithAwaitResolveClosure,
603 AsyncGeneratorBuiltinsAssembler) {
604 const auto context = Parameter<Context>(Descriptor::kContext);
605 const auto value = Parameter<JSAny>(Descriptor::kValue);
606 const TNode<JSAsyncGeneratorObject> generator =
607 CAST(LoadContextElement(context, Context::EXTENSION_INDEX));
608
609 SetGeneratorNotAwaiting(generator);
610
611 // Per proposal-async-iteration/#sec-asyncgeneratoryield step 9
612 // Return ! AsyncGeneratorResolve(_F_.[[Generator]], _value_, *false*).
613 CallBuiltin(Builtin::kAsyncGeneratorResolve, context, generator, value,
614 FalseConstant());
615
616 TailCallBuiltin(Builtin::kAsyncGeneratorResumeNext, context, generator);
617}
618
619TF_BUILTIN(AsyncGeneratorReturn, AsyncGeneratorBuiltinsAssembler) {
620 // AsyncGeneratorReturn is called when resuming requests with "return" resume
621 // modes. It is similar to AsyncGeneratorAwait(), but selects different
622 // resolve/reject closures depending on whether or not the generator is marked
623 // as closed, and handles exception on Await explicitly.
624 //
625 // In particular, non-closed generators will resume the generator with either
626 // "return" or "throw" resume modes, allowing finally blocks or catch blocks
627 // to be evaluated, as if the `await` were performed within the body of the
628 // generator. (per proposal-async-iteration/#sec-asyncgeneratoryield step 8.b)
629 //
630 // Closed generators do not resume the generator in the resolve/reject
631 // closures, but instead simply perform AsyncGeneratorResolve or
632 // AsyncGeneratorReject with the awaited value
633 // (per proposal-async-iteration/#sec-asyncgeneratorresumenext step 10.b.i)
634 //
635 // In all cases, the final step is to jump back to AsyncGeneratorResumeNext.
636 const auto generator =
637 Parameter<JSAsyncGeneratorObject>(Descriptor::kGenerator);
638 const auto value = Parameter<JSAny>(Descriptor::kValue);
640 CAST(LoadFirstAsyncGeneratorRequestFromQueue(generator));
641
642 const TNode<Smi> state = LoadGeneratorState(generator);
643 auto MakeClosures = [&](TNode<Context> context,
645 TVARIABLE(JSFunction, var_on_resolve);
646 TVARIABLE(JSFunction, var_on_reject);
647 Label closed(this), not_closed(this), done(this);
648 Branch(IsGeneratorStateClosed(state), &closed, &not_closed);
649
650 BIND(&closed);
651 var_on_resolve = AllocateRootFunctionWithContext(
652 RootIndex::kAsyncGeneratorReturnClosedResolveClosureSharedFun, context,
654 var_on_reject = AllocateRootFunctionWithContext(
655 RootIndex::kAsyncGeneratorReturnClosedRejectClosureSharedFun, context,
657 Goto(&done);
658
659 BIND(&not_closed);
660 var_on_resolve = AllocateRootFunctionWithContext(
661 RootIndex::kAsyncGeneratorReturnResolveClosureSharedFun, context,
663 var_on_reject = AllocateRootFunctionWithContext(
664 RootIndex::kAsyncGeneratorAwaitRejectClosureSharedFun, context,
666 Goto(&done);
667
668 BIND(&done);
669 return std::make_pair(var_on_resolve.value(), var_on_reject.value());
670 };
671
672 SetGeneratorAwaiting(generator);
673 auto context = Parameter<Context>(Descriptor::kContext);
674 const TNode<JSPromise> outer_promise =
675 LoadPromiseFromAsyncGeneratorRequest(req);
676
677 Label done(this), await_exception(this, Label::kDeferred),
678 closed_await_exception(this, Label::kDeferred);
679 TVARIABLE(Object, var_exception);
680 {
681 compiler::ScopedExceptionHandler handler(this, &await_exception,
682 &var_exception);
683 Await(context, generator, value, outer_promise, MakeClosures);
684 }
685 Goto(&done);
686
687 BIND(&await_exception);
688 {
689 GotoIf(IsGeneratorStateClosed(state), &closed_await_exception);
690 // Tail call to AsyncGeneratorResumeNext
691 AsyncGeneratorAwaitResume(context, generator, var_exception.value(),
693 }
694
695 BIND(&closed_await_exception);
696 {
697 // Tail call to AsyncGeneratorResumeNext
698 AsyncGeneratorReturnClosedReject(context, generator, var_exception.value());
699 }
700
701 BIND(&done);
702 Return(UndefinedConstant());
703}
704
705// On-resolve closure for Await in AsyncGeneratorReturn
706// Resume the generator with "return" resume_mode, and finally perform
707// AsyncGeneratorResumeNext. Per
708// proposal-async-iteration/#sec-asyncgeneratoryield step 8.e
709TF_BUILTIN(AsyncGeneratorReturnResolveClosure,
710 AsyncGeneratorBuiltinsAssembler) {
711 const auto context = Parameter<Context>(Descriptor::kContext);
712 const auto value = Parameter<JSAny>(Descriptor::kValue);
713 AsyncGeneratorAwaitResumeClosure(context, value, JSGeneratorObject::kReturn);
714}
715
716// On-resolve closure for Await in AsyncGeneratorReturn
717// Perform AsyncGeneratorResolve({awaited_value}, true) and finally perform
718// AsyncGeneratorResumeNext.
719TF_BUILTIN(AsyncGeneratorReturnClosedResolveClosure,
720 AsyncGeneratorBuiltinsAssembler) {
721 const auto context = Parameter<Context>(Descriptor::kContext);
722 const auto value = Parameter<JSAny>(Descriptor::kValue);
723 const TNode<JSAsyncGeneratorObject> generator =
724 CAST(LoadContextElement(context, Context::EXTENSION_INDEX));
725
726 SetGeneratorNotAwaiting(generator);
727
728 // https://tc39.github.io/proposal-async-iteration/
729 // #async-generator-resume-next-return-processor-fulfilled step 2:
730 // Return ! AsyncGeneratorResolve(_F_.[[Generator]], _value_, *true*).
731 CallBuiltin(Builtin::kAsyncGeneratorResolve, context, generator, value,
732 TrueConstant());
733
734 TailCallBuiltin(Builtin::kAsyncGeneratorResumeNext, context, generator);
735}
736
737TF_BUILTIN(AsyncGeneratorReturnClosedRejectClosure,
738 AsyncGeneratorBuiltinsAssembler) {
739 const auto context = Parameter<Context>(Descriptor::kContext);
740 const auto value = Parameter<JSAny>(Descriptor::kValue);
741 const TNode<JSAsyncGeneratorObject> generator =
742 CAST(LoadContextElement(context, Context::EXTENSION_INDEX));
743
744 AsyncGeneratorReturnClosedReject(context, generator, value);
745}
746
748
749} // namespace internal
750} // namespace v8
#define BIND(label)
#define TVARIABLE(...)
#define CSA_SLOW_DCHECK(csa,...)
#define CSA_DCHECK(csa,...)
#define TF_BUILTIN(Name, AssemblerBase)
static const int kGeneratorClosed
static const int kGeneratorExecuting
#define CAST(x)
int start
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
TNode< Context > context
TNode< Object > receiver
MovableLabel continuation
MovableLabel handler
void * Allocate(void *address, size_t size, OS::MemoryPermission access)
TNode< Oddball > UndefinedConstant(JSGraph *jsgraph)
compiler::CodeAssemblerState CodeAssemblerState
!IsContextMap !IsContextMap native_context
Definition map-inl.h:877