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