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