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