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