• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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