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 : #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 896 : class AsyncGeneratorBuiltinsAssembler : public AsyncBuiltinsAssembler {
22 : public:
23 : explicit AsyncGeneratorBuiltinsAssembler(CodeAssemblerState* state)
24 : : AsyncBuiltinsAssembler(state) {}
25 :
26 168 : inline Node* TaggedIsAsyncGenerator(Node* tagged_object) {
27 168 : TNode<BoolT> if_notsmi = TaggedIsNotSmi(tagged_object);
28 336 : return Select<BoolT>(if_notsmi,
29 : [=] {
30 : return HasInstanceType(
31 504 : tagged_object, JS_ASYNC_GENERATOR_OBJECT_TYPE);
32 168 : },
33 504 : [=] { return if_notsmi; });
34 : }
35 : inline Node* LoadGeneratorState(Node* const generator) {
36 56 : return LoadObjectField(generator, JSGeneratorObject::kContinuationOffset);
37 : }
38 :
39 168 : inline TNode<BoolT> IsGeneratorStateClosed(SloppyTNode<Smi> const state) {
40 168 : 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 56 : inline TNode<BoolT> IsGeneratorStateSuspendedAtStart(
55 : SloppyTNode<Smi> const state) {
56 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 56 : inline TNode<BoolT> IsGeneratorAwaiting(Node* const generator) {
69 : TNode<Object> is_generator_awaiting =
70 56 : LoadObjectField(generator, JSAsyncGeneratorObject::kIsAwaitingOffset);
71 112 : return WordEqual(is_generator_awaiting, SmiConstant(1));
72 : }
73 :
74 224 : inline void SetGeneratorAwaiting(Node* const generator) {
75 : CSA_ASSERT(this, Word32BinaryNot(IsGeneratorAwaiting(generator)));
76 224 : StoreObjectFieldNoWriteBarrier(
77 448 : generator, JSAsyncGeneratorObject::kIsAwaitingOffset, SmiConstant(1));
78 : CSA_ASSERT(this, IsGeneratorAwaiting(generator));
79 224 : }
80 :
81 336 : inline void SetGeneratorNotAwaiting(Node* const generator) {
82 : CSA_ASSERT(this, IsGeneratorAwaiting(generator));
83 336 : StoreObjectFieldNoWriteBarrier(
84 672 : generator, JSAsyncGeneratorObject::kIsAwaitingOffset, SmiConstant(0));
85 : CSA_ASSERT(this, Word32BinaryNot(IsGeneratorAwaiting(generator)));
86 336 : }
87 :
88 56 : inline void CloseGenerator(Node* const generator) {
89 56 : StoreObjectFieldNoWriteBarrier(
90 : generator, JSGeneratorObject::kContinuationOffset,
91 112 : SmiConstant(JSGeneratorObject::kGeneratorClosed));
92 56 : }
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 224 : 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 112 : return LoadObjectField(request, AsyncGeneratorRequest::kPromiseOffset);
112 : }
113 :
114 : inline Node* LoadValueFromAsyncGeneratorRequest(Node* const request) {
115 : return LoadObjectField(request, AsyncGeneratorRequest::kValueOffset);
116 : }
117 :
118 56 : inline TNode<BoolT> IsAbruptResumeType(SloppyTNode<Smi> const resume_type) {
119 112 : 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 168 : 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 168 : Node* promise = AllocateAndInitJSPromise(context);
156 :
157 336 : Label enqueue(this), if_receiverisincompatible(this, Label::kDeferred);
158 :
159 336 : Branch(TaggedIsAsyncGenerator(generator), &enqueue,
160 168 : &if_receiverisincompatible);
161 :
162 168 : BIND(&enqueue);
163 : {
164 168 : Label done(this);
165 : Node* const req =
166 168 : AllocateAsyncGeneratorRequest(resume_mode, value, promise);
167 :
168 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 168 : TNode<Smi> continuation = CAST(LoadGeneratorState(generator));
175 :
176 336 : GotoIf(SmiEqual(continuation,
177 168 : SmiConstant(JSAsyncGeneratorObject::kGeneratorExecuting)),
178 168 : &done);
179 :
180 168 : CallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator);
181 :
182 168 : Goto(&done);
183 168 : BIND(&done);
184 168 : args->PopAndReturn(promise);
185 : }
186 :
187 168 : BIND(&if_receiverisincompatible);
188 : {
189 : Node* const error =
190 168 : MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver, context,
191 168 : StringConstant(method_name), generator);
192 :
193 : CallBuiltin(Builtins::kRejectPromise, context, promise, error,
194 336 : TrueConstant());
195 168 : args->PopAndReturn(promise);
196 : }
197 168 : }
198 :
199 168 : 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 336 : Node* request = Allocate(AsyncGeneratorRequest::kSize);
204 168 : StoreMapNoWriteBarrier(request, RootIndex::kAsyncGeneratorRequestMap);
205 : StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kNextOffset,
206 336 : UndefinedConstant());
207 : StoreObjectFieldNoWriteBarrier(request,
208 : AsyncGeneratorRequest::kResumeModeOffset,
209 336 : SmiConstant(resume_mode));
210 : StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kValueOffset,
211 168 : resume_value);
212 : StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kPromiseOffset,
213 168 : promise);
214 : StoreObjectFieldRoot(request, AsyncGeneratorRequest::kNextOffset,
215 168 : RootIndex::kUndefinedValue);
216 168 : return request;
217 : }
218 :
219 168 : void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwaitResumeClosure(
220 : Node* context, Node* value,
221 : JSAsyncGeneratorObject::ResumeMode resume_mode) {
222 336 : Node* const generator = LoadContextElement(context, Context::EXTENSION_INDEX);
223 : CSA_SLOW_ASSERT(this, TaggedIsAsyncGenerator(generator));
224 :
225 168 : 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 336 : SmiConstant(resume_mode));
233 :
234 336 : CallStub(CodeFactory::ResumeGenerator(isolate()), context, value, generator);
235 :
236 168 : TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator);
237 168 : }
238 :
239 : template <typename Descriptor>
240 112 : void AsyncGeneratorBuiltinsAssembler::AsyncGeneratorAwait(bool is_catchable) {
241 : TNode<JSAsyncGeneratorObject> async_generator_object =
242 112 : CAST(Parameter(Descriptor::kAsyncGeneratorObject));
243 112 : TNode<Object> value = CAST(Parameter(Descriptor::kValue));
244 112 : 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 112 : SetGeneratorAwaiting(async_generator_object);
255 112 : Await(context, async_generator_object, value, outer_promise, resolve_index,
256 : reject_index, is_catchable);
257 224 : Return(UndefinedConstant());
258 112 : }
259 :
260 168 : void AsyncGeneratorBuiltinsAssembler::AddAsyncGeneratorRequestToQueue(
261 : Node* generator, Node* request) {
262 336 : VARIABLE(var_current, MachineRepresentation::kTagged);
263 168 : Label empty(this), loop(this, &var_current), done(this);
264 :
265 : var_current.Bind(
266 336 : LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset));
267 504 : Branch(IsUndefined(var_current.value()), &empty, &loop);
268 :
269 168 : BIND(&empty);
270 : {
271 168 : StoreObjectField(generator, JSAsyncGeneratorObject::kQueueOffset, request);
272 168 : Goto(&done);
273 : }
274 :
275 168 : BIND(&loop);
276 : {
277 168 : Label loop_next(this), next_empty(this);
278 168 : Node* current = var_current.value();
279 : Node* next = LoadObjectField(current, AsyncGeneratorRequest::kNextOffset);
280 :
281 336 : Branch(IsUndefined(next), &next_empty, &loop_next);
282 168 : BIND(&next_empty);
283 : {
284 168 : StoreObjectField(current, AsyncGeneratorRequest::kNextOffset, request);
285 168 : Goto(&done);
286 : }
287 :
288 168 : BIND(&loop_next);
289 : {
290 168 : var_current.Bind(next);
291 168 : Goto(&loop);
292 : }
293 : }
294 168 : BIND(&done);
295 168 : }
296 :
297 112 : 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 112 : LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset);
304 : CSA_ASSERT(this, IsNotUndefined(request));
305 :
306 : Node* next = LoadObjectField(request, AsyncGeneratorRequest::kNextOffset);
307 :
308 112 : StoreObjectField(generator, JSAsyncGeneratorObject::kQueueOffset, next);
309 112 : return request;
310 : }
311 : } // namespace
312 :
313 : // https://tc39.github.io/proposal-async-iteration/
314 : // Section #sec-asyncgenerator-prototype-next
315 280 : TF_BUILTIN(AsyncGeneratorPrototypeNext, AsyncGeneratorBuiltinsAssembler) {
316 : const int kValueArg = 0;
317 :
318 : Node* argc =
319 112 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
320 56 : CodeStubArguments args(this, argc);
321 :
322 112 : Node* generator = args.GetReceiver();
323 112 : Node* value = args.GetOptionalArgumentValue(kValueArg);
324 : Node* context = Parameter(Descriptor::kContext);
325 :
326 56 : AsyncGeneratorEnqueue(&args, context, generator, value,
327 : JSAsyncGeneratorObject::kNext,
328 56 : "[AsyncGenerator].prototype.next");
329 56 : }
330 :
331 : // https://tc39.github.io/proposal-async-iteration/
332 : // Section #sec-asyncgenerator-prototype-return
333 280 : TF_BUILTIN(AsyncGeneratorPrototypeReturn, AsyncGeneratorBuiltinsAssembler) {
334 : const int kValueArg = 0;
335 :
336 : Node* argc =
337 112 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
338 56 : CodeStubArguments args(this, argc);
339 :
340 112 : Node* generator = args.GetReceiver();
341 112 : Node* value = args.GetOptionalArgumentValue(kValueArg);
342 : Node* context = Parameter(Descriptor::kContext);
343 :
344 56 : AsyncGeneratorEnqueue(&args, context, generator, value,
345 : JSAsyncGeneratorObject::kReturn,
346 56 : "[AsyncGenerator].prototype.return");
347 56 : }
348 :
349 : // https://tc39.github.io/proposal-async-iteration/
350 : // Section #sec-asyncgenerator-prototype-throw
351 280 : TF_BUILTIN(AsyncGeneratorPrototypeThrow, AsyncGeneratorBuiltinsAssembler) {
352 : const int kValueArg = 0;
353 :
354 : Node* argc =
355 112 : ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
356 56 : CodeStubArguments args(this, argc);
357 :
358 112 : Node* generator = args.GetReceiver();
359 112 : Node* value = args.GetOptionalArgumentValue(kValueArg);
360 : Node* context = Parameter(Descriptor::kContext);
361 :
362 56 : AsyncGeneratorEnqueue(&args, context, generator, value,
363 : JSAsyncGeneratorObject::kThrow,
364 56 : "[AsyncGenerator].prototype.throw");
365 56 : }
366 :
367 224 : TF_BUILTIN(AsyncGeneratorAwaitResolveClosure, AsyncGeneratorBuiltinsAssembler) {
368 : Node* value = Parameter(Descriptor::kValue);
369 : Node* context = Parameter(Descriptor::kContext);
370 56 : AsyncGeneratorAwaitResumeClosure(context, value,
371 56 : JSAsyncGeneratorObject::kNext);
372 56 : }
373 :
374 224 : TF_BUILTIN(AsyncGeneratorAwaitRejectClosure, AsyncGeneratorBuiltinsAssembler) {
375 : Node* value = Parameter(Descriptor::kValue);
376 : Node* context = Parameter(Descriptor::kContext);
377 56 : AsyncGeneratorAwaitResumeClosure(context, value,
378 56 : JSAsyncGeneratorObject::kThrow);
379 56 : }
380 :
381 168 : TF_BUILTIN(AsyncGeneratorAwaitUncaught, AsyncGeneratorBuiltinsAssembler) {
382 : const bool kIsCatchable = false;
383 56 : AsyncGeneratorAwait<Descriptor>(kIsCatchable);
384 0 : }
385 :
386 168 : TF_BUILTIN(AsyncGeneratorAwaitCaught, AsyncGeneratorBuiltinsAssembler) {
387 : const bool kIsCatchable = true;
388 56 : AsyncGeneratorAwait<Descriptor>(kIsCatchable);
389 0 : }
390 :
391 224 : 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 112 : VARIABLE(var_state, MachineRepresentation::kTaggedSigned,
405 : LoadGeneratorState(generator));
406 112 : VARIABLE(var_next, MachineRepresentation::kTagged,
407 : LoadFirstAsyncGeneratorRequestFromQueue(generator));
408 56 : Variable* loop_variables[] = {&var_state, &var_next};
409 112 : Label start(this, 2, loop_variables);
410 56 : Goto(&start);
411 56 : BIND(&start);
412 :
413 : CSA_ASSERT(this, IsGeneratorNotExecuting(generator));
414 :
415 : // Stop resuming if suspended for Await.
416 168 : ReturnIf(IsGeneratorAwaiting(generator), UndefinedConstant());
417 :
418 : // Stop resuming if request queue is empty.
419 224 : ReturnIf(IsUndefined(var_next.value()), UndefinedConstant());
420 :
421 56 : Node* const next = var_next.value();
422 : TNode<Smi> const resume_type =
423 : CAST(LoadResumeTypeFromAsyncGeneratorRequest(next));
424 :
425 56 : Label if_abrupt(this), if_normal(this), resume_generator(this);
426 112 : Branch(IsAbruptResumeType(resume_type), &if_abrupt, &if_normal);
427 56 : BIND(&if_abrupt);
428 : {
429 56 : Label settle_promise(this), if_return(this), if_throw(this);
430 168 : GotoIfNot(IsGeneratorStateSuspendedAtStart(var_state.value()),
431 56 : &settle_promise);
432 56 : CloseGenerator(generator);
433 112 : var_state.Bind(SmiConstant(JSGeneratorObject::kGeneratorClosed));
434 56 : Goto(&settle_promise);
435 :
436 56 : BIND(&settle_promise);
437 : Node* next_value = LoadValueFromAsyncGeneratorRequest(next);
438 112 : Branch(SmiEqual(resume_type, SmiConstant(JSGeneratorObject::kReturn)),
439 56 : &if_return, &if_throw);
440 :
441 56 : 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 56 : TailCallBuiltin(Builtins::kAsyncGeneratorReturn, context, generator,
452 56 : next_value, is_caught);
453 :
454 56 : BIND(&if_throw);
455 168 : GotoIfNot(IsGeneratorStateClosed(var_state.value()), &resume_generator);
456 : CallBuiltin(Builtins::kAsyncGeneratorReject, context, generator,
457 56 : next_value);
458 56 : var_next.Bind(LoadFirstAsyncGeneratorRequestFromQueue(generator));
459 56 : Goto(&start);
460 : }
461 :
462 56 : BIND(&if_normal);
463 : {
464 168 : GotoIfNot(IsGeneratorStateClosed(var_state.value()), &resume_generator);
465 : CallBuiltin(Builtins::kAsyncGeneratorResolve, context, generator,
466 112 : UndefinedConstant(), TrueConstant());
467 56 : var_state.Bind(LoadGeneratorState(generator));
468 56 : var_next.Bind(LoadFirstAsyncGeneratorRequestFromQueue(generator));
469 56 : Goto(&start);
470 : }
471 :
472 56 : BIND(&resume_generator);
473 : {
474 : // Remember the {resume_type} for the {generator}.
475 : StoreObjectFieldNoWriteBarrier(
476 56 : generator, JSGeneratorObject::kResumeModeOffset, resume_type);
477 112 : CallStub(CodeFactory::ResumeGenerator(isolate()), context,
478 112 : LoadValueFromAsyncGeneratorRequest(next), generator);
479 56 : var_state.Bind(LoadGeneratorState(generator));
480 56 : var_next.Bind(LoadFirstAsyncGeneratorRequestFromQueue(generator));
481 56 : Goto(&start);
482 : }
483 56 : }
484 :
485 224 : 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 56 : Node* const next = TakeFirstAsyncGeneratorRequestFromQueue(generator);
501 : Node* const promise = LoadPromiseFromAsyncGeneratorRequest(next);
502 :
503 : // Let iteratorResult be CreateIterResultObject(value, done).
504 112 : Node* const iter_result = Allocate(JSIteratorResult::kSize);
505 : {
506 224 : Node* map = LoadContextElement(LoadNativeContext(context),
507 56 : Context::ITERATOR_RESULT_MAP_INDEX);
508 56 : StoreMapNoWriteBarrier(iter_result, map);
509 : StoreObjectFieldRoot(iter_result, JSIteratorResult::kPropertiesOrHashOffset,
510 56 : RootIndex::kEmptyFixedArray);
511 : StoreObjectFieldRoot(iter_result, JSIteratorResult::kElementsOffset,
512 56 : RootIndex::kEmptyFixedArray);
513 : StoreObjectFieldNoWriteBarrier(iter_result, JSIteratorResult::kValueOffset,
514 56 : value);
515 : StoreObjectFieldNoWriteBarrier(iter_result, JSIteratorResult::kDoneOffset,
516 56 : 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 56 : Label if_fast(this), if_slow(this, Label::kDeferred), return_promise(this);
529 56 : GotoIfForceSlowPath(&if_slow);
530 112 : GotoIf(IsPromiseHookEnabled(), &if_slow);
531 112 : Branch(IsPromiseThenProtectorCellInvalid(), &if_slow, &if_fast);
532 :
533 56 : BIND(&if_fast);
534 : {
535 : // Skip the "then" on {iter_result} and directly fulfill the {promise}
536 : // with the {iter_result}.
537 56 : CallBuiltin(Builtins::kFulfillPromise, context, promise, iter_result);
538 56 : Goto(&return_promise);
539 : }
540 :
541 56 : BIND(&if_slow);
542 : {
543 : // Perform Call(promiseCapability.[[Resolve]], undefined, «iteratorResult»).
544 56 : CallBuiltin(Builtins::kResolvePromise, context, promise, iter_result);
545 56 : Goto(&return_promise);
546 : }
547 :
548 : // Per spec, AsyncGeneratorResolve() returns undefined. However, for the
549 : // benefit of %TraceExit(), return the Promise.
550 56 : BIND(&return_promise);
551 56 : Return(promise);
552 56 : }
553 :
554 224 : 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 56 : Node* const next = TakeFirstAsyncGeneratorRequestFromQueue(generator);
561 : Node* const promise = LoadPromiseFromAsyncGeneratorRequest(next);
562 :
563 112 : Return(CallBuiltin(Builtins::kRejectPromise, context, promise, value,
564 168 : TrueConstant()));
565 56 : }
566 :
567 224 : 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 56 : SetGeneratorAwaiting(generator);
580 56 : Await(context, generator, value, outer_promise, on_resolve, on_reject,
581 56 : is_caught);
582 112 : Return(UndefinedConstant());
583 56 : }
584 :
585 224 : TF_BUILTIN(AsyncGeneratorYieldResolveClosure, AsyncGeneratorBuiltinsAssembler) {
586 : Node* const context = Parameter(Descriptor::kContext);
587 : Node* const value = Parameter(Descriptor::kValue);
588 112 : Node* const generator = LoadContextElement(context, Context::EXTENSION_INDEX);
589 :
590 56 : 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 112 : FalseConstant());
596 :
597 56 : TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator);
598 56 : }
599 :
600 280 : 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 56 : Label perform_await(this);
624 168 : VARIABLE(var_on_resolve, MachineType::PointerRepresentation(),
625 : IntPtrConstant(
626 : Context::ASYNC_GENERATOR_RETURN_CLOSED_RESOLVE_SHARED_FUN));
627 168 : 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 112 : GotoIf(IsGeneratorStateClosed(state), &perform_await);
633 : var_on_resolve.Bind(
634 112 : IntPtrConstant(Context::ASYNC_GENERATOR_RETURN_RESOLVE_SHARED_FUN));
635 : var_on_reject.Bind(
636 112 : IntPtrConstant(Context::ASYNC_GENERATOR_AWAIT_REJECT_SHARED_FUN));
637 56 : Goto(&perform_await);
638 :
639 56 : BIND(&perform_await);
640 :
641 56 : SetGeneratorAwaiting(generator);
642 : Node* const context = Parameter(Descriptor::kContext);
643 : Node* const outer_promise = LoadPromiseFromAsyncGeneratorRequest(req);
644 56 : Await(context, generator, value, outer_promise, var_on_resolve.value(),
645 56 : var_on_reject.value(), is_caught);
646 :
647 112 : Return(UndefinedConstant());
648 56 : }
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 224 : TF_BUILTIN(AsyncGeneratorReturnResolveClosure,
655 : AsyncGeneratorBuiltinsAssembler) {
656 : Node* const context = Parameter(Descriptor::kContext);
657 : Node* const value = Parameter(Descriptor::kValue);
658 56 : AsyncGeneratorAwaitResumeClosure(context, value, JSGeneratorObject::kReturn);
659 56 : }
660 :
661 : // On-resolve closure for Await in AsyncGeneratorReturn
662 : // Perform AsyncGeneratorResolve({awaited_value}, true) and finally perform
663 : // AsyncGeneratorResumeNext.
664 224 : TF_BUILTIN(AsyncGeneratorReturnClosedResolveClosure,
665 : AsyncGeneratorBuiltinsAssembler) {
666 : Node* const context = Parameter(Descriptor::kContext);
667 : Node* const value = Parameter(Descriptor::kValue);
668 112 : Node* const generator = LoadContextElement(context, Context::EXTENSION_INDEX);
669 :
670 56 : 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 112 : TrueConstant());
677 :
678 56 : TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator);
679 56 : }
680 :
681 224 : TF_BUILTIN(AsyncGeneratorReturnClosedRejectClosure,
682 : AsyncGeneratorBuiltinsAssembler) {
683 : Node* const context = Parameter(Descriptor::kContext);
684 : Node* const value = Parameter(Descriptor::kValue);
685 112 : Node* const generator = LoadContextElement(context, Context::EXTENSION_INDEX);
686 :
687 56 : 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 56 : CallBuiltin(Builtins::kAsyncGeneratorReject, context, generator, value);
693 :
694 56 : TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator);
695 56 : }
696 :
697 : } // namespace internal
698 59456 : } // namespace v8
|