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
14 namespace v8 {
15 namespace internal {
16
17 namespace {
18
19 class AsyncGeneratorBuiltinsAssembler : public AsyncBuiltinsAssembler {
20 public:
AsyncGeneratorBuiltinsAssembler(CodeAssemblerState * state)21 explicit AsyncGeneratorBuiltinsAssembler(CodeAssemblerState* state)
22 : AsyncBuiltinsAssembler(state) {}
23
LoadGeneratorState(const TNode<JSGeneratorObject> generator)24 inline TNode<Smi> LoadGeneratorState(
25 const TNode<JSGeneratorObject> generator) {
26 return LoadObjectField<Smi>(generator,
27 JSGeneratorObject::kContinuationOffset);
28 }
29
IsGeneratorStateClosed(const TNode<Smi> state)30 inline TNode<BoolT> IsGeneratorStateClosed(const TNode<Smi> state) {
31 return SmiEqual(state, SmiConstant(JSGeneratorObject::kGeneratorClosed));
32 }
IsGeneratorClosed(const TNode<JSGeneratorObject> generator)33 inline TNode<BoolT> IsGeneratorClosed(
34 const TNode<JSGeneratorObject> generator) {
35 return IsGeneratorStateClosed(LoadGeneratorState(generator));
36 }
37
IsGeneratorStateSuspended(const TNode<Smi> state)38 inline TNode<BoolT> IsGeneratorStateSuspended(const TNode<Smi> state) {
39 return SmiGreaterThanOrEqual(state, SmiConstant(0));
40 }
41
IsGeneratorSuspended(const TNode<JSGeneratorObject> generator)42 inline TNode<BoolT> IsGeneratorSuspended(
43 const TNode<JSGeneratorObject> generator) {
44 return IsGeneratorStateSuspended(LoadGeneratorState(generator));
45 }
46
IsGeneratorStateSuspendedAtStart(const TNode<Smi> state)47 inline TNode<BoolT> IsGeneratorStateSuspendedAtStart(const TNode<Smi> state) {
48 return SmiEqual(state, SmiConstant(0));
49 }
50
IsGeneratorStateNotExecuting(const TNode<Smi> state)51 inline TNode<BoolT> IsGeneratorStateNotExecuting(const TNode<Smi> state) {
52 return SmiNotEqual(state,
53 SmiConstant(JSGeneratorObject::kGeneratorExecuting));
54 }
IsGeneratorNotExecuting(const TNode<JSGeneratorObject> generator)55 inline TNode<BoolT> IsGeneratorNotExecuting(
56 const TNode<JSGeneratorObject> generator) {
57 return IsGeneratorStateNotExecuting(LoadGeneratorState(generator));
58 }
59
IsGeneratorAwaiting(const TNode<JSGeneratorObject> generator)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
SetGeneratorAwaiting(const TNode<JSGeneratorObject> generator)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
SetGeneratorNotAwaiting(const TNode<JSGeneratorObject> generator)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
CloseGenerator(const TNode<JSGeneratorObject> generator)82 inline void CloseGenerator(const TNode<JSGeneratorObject> generator) {
83 StoreObjectFieldNoWriteBarrier(
84 generator, JSGeneratorObject::kContinuationOffset,
85 SmiConstant(JSGeneratorObject::kGeneratorClosed));
86 }
87
LoadFirstAsyncGeneratorRequestFromQueue(const TNode<JSGeneratorObject> generator)88 inline TNode<HeapObject> LoadFirstAsyncGeneratorRequestFromQueue(
89 const TNode<JSGeneratorObject> generator) {
90 return LoadObjectField<HeapObject>(generator,
91 JSAsyncGeneratorObject::kQueueOffset);
92 }
93
LoadResumeTypeFromAsyncGeneratorRequest(const TNode<AsyncGeneratorRequest> request)94 inline TNode<Smi> LoadResumeTypeFromAsyncGeneratorRequest(
95 const TNode<AsyncGeneratorRequest> request) {
96 return LoadObjectField<Smi>(request,
97 AsyncGeneratorRequest::kResumeModeOffset);
98 }
99
LoadPromiseFromAsyncGeneratorRequest(const TNode<AsyncGeneratorRequest> request)100 inline TNode<JSPromise> LoadPromiseFromAsyncGeneratorRequest(
101 const TNode<AsyncGeneratorRequest> request) {
102 return LoadObjectField<JSPromise>(request,
103 AsyncGeneratorRequest::kPromiseOffset);
104 }
105
LoadValueFromAsyncGeneratorRequest(const TNode<AsyncGeneratorRequest> request)106 inline TNode<Object> LoadValueFromAsyncGeneratorRequest(
107 const TNode<AsyncGeneratorRequest> request) {
108 return LoadObjectField(request, AsyncGeneratorRequest::kValueOffset);
109 }
110
IsAbruptResumeType(const TNode<Smi> resume_type)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.
AsyncGeneratorEnqueue(CodeStubArguments * args,TNode<Context> context,TNode<Object> receiver,TNode<Object> value,JSAsyncGeneratorObject::ResumeMode resume_mode,const char * method_name)140 void 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
191 TNode<AsyncGeneratorRequest>
AllocateAsyncGeneratorRequest(JSAsyncGeneratorObject::ResumeMode resume_mode,TNode<Object> resume_value,TNode<JSPromise> promise)192 AsyncGeneratorBuiltinsAssembler::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
AsyncGeneratorAwaitResumeClosure(TNode<Context> context,TNode<Object> value,JSAsyncGeneratorObject::ResumeMode resume_mode)211 void 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
250 template <typename Descriptor>
AsyncGeneratorAwait(bool is_catchable)251 void 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
AddAsyncGeneratorRequestToQueue(TNode<JSAsyncGeneratorObject> generator,TNode<AsyncGeneratorRequest> request)269 void 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
308 TNode<AsyncGeneratorRequest>
TakeFirstAsyncGeneratorRequestFromQueue(TNode<JSAsyncGeneratorObject> generator)309 AsyncGeneratorBuiltinsAssembler::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
TF_BUILTIN(AsyncGeneratorPrototypeNext,AsyncGeneratorBuiltinsAssembler)326 TF_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
TF_BUILTIN(AsyncGeneratorPrototypeReturn,AsyncGeneratorBuiltinsAssembler)344 TF_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
TF_BUILTIN(AsyncGeneratorPrototypeThrow,AsyncGeneratorBuiltinsAssembler)362 TF_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
TF_BUILTIN(AsyncGeneratorAwaitResolveClosure,AsyncGeneratorBuiltinsAssembler)378 TF_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
TF_BUILTIN(AsyncGeneratorAwaitRejectClosure,AsyncGeneratorBuiltinsAssembler)385 TF_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
TF_BUILTIN(AsyncGeneratorAwaitUncaught,AsyncGeneratorBuiltinsAssembler)392 TF_BUILTIN(AsyncGeneratorAwaitUncaught, AsyncGeneratorBuiltinsAssembler) {
393 const bool kIsCatchable = false;
394 AsyncGeneratorAwait<Descriptor>(kIsCatchable);
395 }
396
TF_BUILTIN(AsyncGeneratorAwaitCaught,AsyncGeneratorBuiltinsAssembler)397 TF_BUILTIN(AsyncGeneratorAwaitCaught, AsyncGeneratorBuiltinsAssembler) {
398 const bool kIsCatchable = true;
399 AsyncGeneratorAwait<Descriptor>(kIsCatchable);
400 }
401
TF_BUILTIN(AsyncGeneratorResumeNext,AsyncGeneratorBuiltinsAssembler)402 TF_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
TF_BUILTIN(AsyncGeneratorResolve,AsyncGeneratorBuiltinsAssembler)492 TF_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
TF_BUILTIN(AsyncGeneratorReject,AsyncGeneratorBuiltinsAssembler)562 TF_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
TF_BUILTIN(AsyncGeneratorYield,AsyncGeneratorBuiltinsAssembler)576 TF_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
TF_BUILTIN(AsyncGeneratorYieldResolveClosure,AsyncGeneratorBuiltinsAssembler)594 TF_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
TF_BUILTIN(AsyncGeneratorReturn,AsyncGeneratorBuiltinsAssembler)610 TF_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
TF_BUILTIN(AsyncGeneratorReturnResolveClosure,AsyncGeneratorBuiltinsAssembler)663 TF_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.
TF_BUILTIN(AsyncGeneratorReturnClosedResolveClosure,AsyncGeneratorBuiltinsAssembler)673 TF_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
TF_BUILTIN(AsyncGeneratorReturnClosedRejectClosure,AsyncGeneratorBuiltinsAssembler)691 TF_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