Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(587)

Side by Side Diff: src/builtins/builtins-async-generator.cc

Issue 2622833002: WIP [esnext] implement async iteration proposal (Closed)
Patch Set: simplify AsyncIteratorValueUnwrap Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/builtins/builtins-async.cc ('k') | src/builtins/builtins-async-iterator.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 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.h"
6 #include "src/builtins/builtins-utils.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 namespace {
16
17 // Describe fields of Context associated with AsyncGeneratorAwait resume
18 // closures.
19 class AwaitContext {
20 public:
21 enum Fields { kGeneratorSlot = Context::MIN_CONTEXT_SLOTS, kLength };
22 };
23
24 } // anonymous namespace
25
26 // https://tc39.github.io/proposal-async-iteration/
27 // Section #sec-asyncgenerator-prototype-next
28 TF_BUILTIN(AsyncGeneratorPrototypeNext, AsyncBuiltinsAssembler) {
29 Node* const generator = Parameter(0);
30 Node* const value = Parameter(1);
31 Node* const context = Parameter(4);
32 AsyncGeneratorEnqueue(context, generator, value,
33 JSAsyncGeneratorObject::kNext,
34 "[AsyncGenerator].prototype.next");
35 }
36
37 // https://tc39.github.io/proposal-async-iteration/
38 // Section #sec-asyncgenerator-prototype-return
39 TF_BUILTIN(AsyncGeneratorPrototypeReturn, AsyncBuiltinsAssembler) {
40 Node* generator = Parameter(0);
41 Node* value = Parameter(1);
42 Node* context = Parameter(4);
43 AsyncGeneratorEnqueue(context, generator, value,
44 JSAsyncGeneratorObject::kReturn,
45 "[AsyncGenerator].prototype.return");
46 }
47
48 // https://tc39.github.io/proposal-async-iteration/
49 // Section #sec-asyncgenerator-prototype-throw
50 TF_BUILTIN(AsyncGeneratorPrototypeThrow, AsyncBuiltinsAssembler) {
51 Node* generator = Parameter(0);
52 Node* value = Parameter(1);
53 Node* context = Parameter(4);
54 AsyncGeneratorEnqueue(context, generator, value,
55 JSAsyncGeneratorObject::kThrow,
56 "[AsyncGenerator].prototype.throw");
57 }
58
59 TF_BUILTIN(AsyncGeneratorYield, AsyncBuiltinsAssembler) {
60 Node* const generator = Parameter(0);
61 Node* const value = Parameter(1);
62 Node* const context = Parameter(4);
63
64 CSA_ASSERT_JS_ARGC_EQ(this, 1);
65 CSA_SLOW_ASSERT(this,
66 HasInstanceType(generator, JS_ASYNC_GENERATOR_OBJECT_TYPE));
67
68 Node* request = TakeFirstAsyncGeneratorRequestFromQueue(generator);
69
70 Node* const promise =
71 LoadObjectField(request, AsyncGeneratorRequest::kPromiseOffset);
72
73 // The request Promise must still be pending at this point.
74 CSA_SLOW_ASSERT(this,
75 SmiEqual(LoadObjectField(promise, JSPromise::kStatusOffset),
76 SmiConstant(v8::Promise::kPending)));
77
78 // If a yield expression is reached, we must not be awaiting any value.
79 CSA_SLOW_ASSERT(
80 this, IsUndefined(LoadObjectField(
81 generator, JSAsyncGeneratorObject::kAwaitedPromiseOffset)));
82
83 Node* const iter_result =
84 CallStub(CodeFactory::CreateIterResultObject(isolate()), context, value,
85 FalseConstant());
86
87 InternalResolvePromise(context, promise, iter_result);
88
89 AsyncGeneratorResumeNext(context, generator);
90
91 Return(UndefinedConstant());
92 }
93
94 TF_BUILTIN(AsyncGeneratorRawYield, AsyncBuiltinsAssembler) {
95 Node* const generator = Parameter(0);
96 Node* const value = Parameter(1);
97 Node* const context = Parameter(4);
98
99 CSA_ASSERT_JS_ARGC_EQ(this, 1);
100 CSA_SLOW_ASSERT(this,
101 HasInstanceType(generator, JS_ASYNC_GENERATOR_OBJECT_TYPE));
102
103 Node* request = TakeFirstAsyncGeneratorRequestFromQueue(generator);
104
105 Node* const promise =
106 LoadObjectField(request, AsyncGeneratorRequest::kPromiseOffset);
107
108 // The request Promise must still be pending at this point.
109 CSA_SLOW_ASSERT(this,
110 SmiEqual(LoadObjectField(promise, JSPromise::kStatusOffset),
111 SmiConstant(v8::Promise::kPending)));
112
113 // If a yield expression is reached, we must not be awaiting any value.
114 CSA_SLOW_ASSERT(
115 this, IsUndefined(LoadObjectField(
116 generator, JSAsyncGeneratorObject::kAwaitedPromiseOffset)));
117
118 InternalResolvePromise(context, promise, value);
119
120 AsyncGeneratorResumeNext(context, generator);
121
122 Return(UndefinedConstant());
123 }
124
125 TF_BUILTIN(AsyncGeneratorAwaitResolveClosure, AsyncBuiltinsAssembler) {
126 Node* value = Parameter(1);
127 Node* context = Parameter(4);
128
129 AsyncGeneratorAwaitResumeClosure(context, value,
130 JSAsyncGeneratorObject::kNext);
131 Return(UndefinedConstant());
132 }
133
134 TF_BUILTIN(AsyncGeneratorAwaitRejectClosure, AsyncBuiltinsAssembler) {
135 Node* value = Parameter(1);
136 Node* context = Parameter(4);
137
138 AsyncGeneratorAwaitResumeClosure(context, value,
139 JSAsyncGeneratorObject::kThrow);
140 Return(UndefinedConstant());
141 }
142
143 TF_BUILTIN(AsyncGeneratorAwaitUncaught, AsyncBuiltinsAssembler) {
144 const bool kIsCatchable = false;
145 AsyncGeneratorAwait(kIsCatchable);
146 }
147
148 TF_BUILTIN(AsyncGeneratorAwaitCaught, AsyncBuiltinsAssembler) {
149 const bool kIsCatchable = true;
150 AsyncGeneratorAwait(kIsCatchable);
151 }
152
153 // Shared implementation for the 3 Async Iterator protocol methods of Async
154 // Generators.
155 void AsyncBuiltinsAssembler::AsyncGeneratorEnqueue(
156 Node* context, Node* generator, Node* value,
157 JSAsyncGeneratorObject::ResumeMode resume_mode, const char* method_name) {
158 // AsyncGeneratorEnqueue produces a new Promise, and appends it to the list
159 // of async generator requests to be executed. If the generator is not
160 // presently executing, then this method will loop through, processing each
161 // request from front to back.
162 // This loop resides in AsyncGeneratorResumeNext.
163 Node* promise = AllocateAndInitJSPromise(context);
164
165 Label enqueue(this), if_receiverisincompatible(this, Label::kDeferred);
166
167 GotoIf(TaggedIsSmi(generator), &if_receiverisincompatible);
168 Branch(HasInstanceType(generator, JS_ASYNC_GENERATOR_OBJECT_TYPE), &enqueue,
169 &if_receiverisincompatible);
170
171 Bind(&enqueue);
172 {
173 Label done(this);
174 Node* const req =
175 AllocateAsyncGeneratorRequest(resume_mode, value, promise);
176
177 AddAsyncGeneratorRequestToQueue(generator, req);
178
179 // Let state be generator.[[AsyncGeneratorState]]
180 // If state is not "executing", then
181 // Perform AsyncGeneratorResumeNext(Generator)
182 // Check if the {receiver} is running or already closed.
183 Node* continuation =
184 LoadObjectField(generator, JSAsyncGeneratorObject::kContinuationOffset);
185
186 GotoIf(SmiEqual(continuation,
187 SmiConstant(JSAsyncGeneratorObject::kGeneratorExecuting)),
188 &done);
189
190 AsyncGeneratorResumeNext(context, generator, continuation);
191
192 Goto(&done);
193 Bind(&done);
194 Return(promise);
195 }
196
197 Bind(&if_receiverisincompatible);
198 {
199 Node* native_context = LoadNativeContext(context);
200 Node* make_type_error =
201 LoadContextElement(native_context, Context::MAKE_TYPE_ERROR_INDEX);
202 Handle<String> method =
203 factory()->NewStringFromAsciiChecked(method_name, TENURED);
204 Node* error =
205 CallJS(CodeFactory::Call(isolate()), context, make_type_error,
206 UndefinedConstant(),
207 SmiConstant(MessageTemplate::kIncompatibleMethodReceiver),
208 HeapConstant(method), generator);
209
210 CallRuntime(Runtime::kPromiseReject, context, promise, error,
211 TrueConstant());
212 Return(promise);
213 }
214 }
215
216 void AsyncBuiltinsAssembler::AsyncGeneratorResumeNext(Node* context,
217 Node* generator,
218 Node* continuation) {
219 // Loop through each queued request, from first to last, and resuming the
220 // generator. Will not resume generator if resumed with an abrupt completion
221 // such as via the .throw() or .return() methods, or if an Await is
222 // in progress.
223 Variable var_request(this, MachineRepresentation::kTagged);
224 Variable var_continuation(this, MachineRepresentation::kTaggedSigned);
225 var_continuation.Bind(continuation);
226
227 Variable* loop_vars[] = {&var_request, &var_continuation};
228 Label loop(this, arraysize(loop_vars),
229 reinterpret_cast<Variable **>(loop_vars)),
230 done(this);
231
232 var_request.Bind(
233 LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset));
234 Branch(IsUndefined(var_request.value()), &done, &loop);
235
236 Bind(&loop);
237 {
238 // Stop consuming the AsyncGeneratorRequest queue and resuming the generator
239 // if an Await is in progress.
240 Node* awaited = LoadObjectField(
241 generator, JSAsyncGeneratorObject::kAwaitedPromiseOffset);
242 GotoUnless(IsUndefined(awaited), &done);
243
244 AsyncGeneratorResumeNextStep(context, generator, var_request.value(),
245 var_continuation.value());
246
247 var_continuation.Bind(LoadObjectField(
248 generator, JSAsyncGeneratorObject::kContinuationOffset));
249 var_request.Bind(
250 LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset));
251
252 // Continue loop if there is another request queued up.
253 Branch(IsUndefined(var_request.value()), &done, &loop);
254 }
255
256 Bind(&done);
257 }
258
259 void AsyncBuiltinsAssembler::AsyncGeneratorResumeNextStep(Node* context,
260 Node* generator,
261 Node* request,
262 Node* continuation) {
263 Variable var_return(this, MachineRepresentation::kTagged);
264 var_return.Bind(UndefinedConstant());
265 CSA_SLOW_ASSERT(this, HasInstanceType(request, ASYNC_GENERATOR_REQUEST_TYPE));
266
267 Label loop_next(this);
268
269 // Let completion be next.[[Completion]]
270 Node* resume_mode =
271 LoadObjectField(request, AsyncGeneratorRequest::kResumeModeOffset);
272 Node* value = LoadObjectField(request, AsyncGeneratorRequest::kValueOffset);
273
274 // If completion is an abrupt completion, then
275 Label if_abruptcompletion(this), check_ifcompleted(this),
276 resume_generator(this), if_return(this, &var_return), if_throw(this);
277 Branch(SmiEqual(resume_mode, SmiConstant(JSAsyncGeneratorObject::kNext)),
278 &check_ifcompleted, &if_abruptcompletion);
279
280 Bind(&if_abruptcompletion);
281 {
282 Label check_completed(this), if_completed(this);
283
284 // If state is "suspendedStart", then
285 // Set generator.[[AsyncGeneratorState]] to "completed"
286 // Set state to "completed"
287 GotoUnless(SmiEqual(continuation, SmiConstant(0)), &check_completed);
288 StoreObjectFieldNoWriteBarrier(
289 generator, JSAsyncGeneratorObject::kContinuationOffset,
290 SmiConstant(JSAsyncGeneratorObject::kGeneratorClosed));
291 Goto(&if_completed);
292
293 Bind(&check_completed);
294 Branch(SmiEqual(continuation,
295 SmiConstant(JSAsyncGeneratorObject::kGeneratorClosed)),
296 &if_completed, &resume_generator);
297
298 Bind(&if_completed);
299 {
300 // If state is "completed", then
301 // If completion.[[Type]] is return, then return
302 // ! AsyncGeneratorResolve(generator, completion.[[Value]], true).
303 // Else, return ! AsyncGeneratorReject(generator,
304 // completion.[[Value]])
305 var_return.Bind(value);
306 Branch(
307 SmiEqual(resume_mode, SmiConstant(JSAsyncGeneratorObject::kReturn)),
308 &if_return, &if_throw);
309 }
310 }
311
312 Bind(&check_ifcompleted);
313 {
314 // Else if state is "completed", then return !
315 // AsyncGeneratorResolve(generator, undefined, true).
316 Branch(SmiEqual(continuation,
317 SmiConstant(JSAsyncGeneratorObject::kGeneratorClosed)),
318 &if_return, &resume_generator);
319 }
320
321 Bind(&resume_generator);
322 {
323 CSA_ASSERT(this, SmiGreaterThanOrEqual(continuation, SmiConstant(0)));
324 Node* result = CallStub(CodeFactory::ResumeGenerator(isolate()), context,
325 value, generator, resume_mode);
326 Goto(&loop_next);
327 }
328
329 Bind(&if_return);
330 {
331 Node* const request = TakeFirstAsyncGeneratorRequestFromQueue(generator);
332 Node* const promise =
333 LoadObjectField(request, AsyncGeneratorRequest::kPromiseOffset);
334 Node* const result =
335 CallStub(CodeFactory::CreateIterResultObject(isolate()), context,
336 var_return.value(), TrueConstant());
337 PromiseFulfill(context, promise, result, v8::Promise::kFulfilled);
338 Goto(&loop_next);
339 }
340
341 Bind(&if_throw);
342 {
343 Node* request = TakeFirstAsyncGeneratorRequestFromQueue(generator);
344
345 // Let promiseCapability be next.[[Capability]].
346 Node* promise =
347 LoadObjectField(request, AsyncGeneratorRequest::kPromiseOffset);
348
349 // Perform ! Call(promiseCapability.[[Reject]], undefined, « exception »).
350 CallRuntime(Runtime::kPromiseReject, context, promise, value,
351 TrueConstant());
352 Goto(&loop_next);
353 }
354
355 Bind(&loop_next);
356 }
357
358 Node* AsyncBuiltinsAssembler::AllocateAsyncGeneratorRequest(
359 JSAsyncGeneratorObject::ResumeMode resume_mode, Node* resume_value,
360 Node* promise) {
361 CSA_SLOW_ASSERT(this, HasInstanceType(promise, JS_PROMISE_TYPE));
362 Node* request = Allocate(AsyncGeneratorRequest::kSize);
363 StoreMapNoWriteBarrier(request, Heap::kAsyncGeneratorRequestMapRootIndex);
364 StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kNextOffset,
365 UndefinedConstant());
366 StoreObjectFieldNoWriteBarrier(request,
367 AsyncGeneratorRequest::kResumeModeOffset,
368 SmiConstant(resume_mode));
369 StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kValueOffset,
370 resume_value);
371 StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kPromiseOffset,
372 promise);
373 return request;
374 }
375
376 void AsyncBuiltinsAssembler::AsyncGeneratorAwaitResumeClosure(
377 Node* context, Node* value,
378 JSAsyncGeneratorObject::ResumeMode resume_mode) {
379 Label done(this);
380 Node* const generator =
381 LoadContextElement(context, AwaitContext::kGeneratorSlot);
382 CSA_SLOW_ASSERT(this,
383 HasInstanceType(generator, JS_ASYNC_GENERATOR_OBJECT_TYPE));
384
385 Node* const promise =
386 LoadObjectField(generator, JSAsyncGeneratorObject::kAwaitedPromiseOffset);
387 CSA_SLOW_ASSERT(this, HasInstanceType(promise, JS_PROMISE_TYPE));
388 CSA_SLOW_ASSERT(this,
389 SmiAbove(LoadObjectField(promise, JSPromise::kStatusOffset),
390 SmiConstant(v8::Promise::kPending)));
391
392 StoreObjectFieldNoWriteBarrier(generator,
393 JSAsyncGeneratorObject::kAwaitedPromiseOffset,
394 UndefinedConstant());
395
396 Node* const continuation =
397 LoadObjectField(generator, JSAsyncGeneratorObject::kContinuationOffset);
398
399 GotoUnless(SmiGreaterThanOrEqual(continuation, SmiConstant(0)), &done);
400 CallStub(CodeFactory::ResumeAwaitedGenerator(isolate()), context, value,
401 generator, SmiConstant(resume_mode));
402
403 Goto(&done);
404 Bind(&done);
405 AsyncGeneratorResumeNext(context, generator);
406 }
407
408 void AsyncBuiltinsAssembler::AsyncGeneratorAwait(bool is_catchable) {
409 Node* generator = Parameter(1);
410 Node* value = Parameter(2);
411 Node* context = Parameter(5);
412
413 CSA_ASSERT_JS_ARGC_EQ(this, 2);
414
415 CSA_SLOW_ASSERT(this,
416 HasInstanceType(generator, JS_ASYNC_GENERATOR_OBJECT_TYPE));
417
418 Node* const request =
419 LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset);
420 CSA_SLOW_ASSERT(this, WordNotEqual(request, UndefinedConstant()));
421
422 NodeGenerator1 closure_context = [&](Node* native_context) -> Node* {
423 Node* const context =
424 CreatePromiseContext(native_context, AwaitContext::kLength);
425 StoreContextElementNoWriteBarrier(context, AwaitContext::kGeneratorSlot,
426 generator);
427 return context;
428 };
429
430 Node* outer_promise =
431 LoadObjectField(request, AsyncGeneratorRequest::kPromiseOffset);
432
433 const int resolve_index = Context::ASYNC_GENERATOR_AWAIT_RESOLVE_SHARED_FUN;
434 const int reject_index = Context::ASYNC_GENERATOR_AWAIT_REJECT_SHARED_FUN;
435
436 Node* promise =
437 Await(context, generator, value, outer_promise, closure_context,
438 resolve_index, reject_index, is_catchable);
439
440 CSA_SLOW_ASSERT(
441 this, IsUndefined(LoadObjectField(
442 generator, JSAsyncGeneratorObject::kAwaitedPromiseOffset)));
443 StoreObjectField(generator, JSAsyncGeneratorObject::kAwaitedPromiseOffset,
444 promise);
445 Return(UndefinedConstant());
446 }
447
448 void AsyncBuiltinsAssembler::AddAsyncGeneratorRequestToQueue(Node* generator,
449 Node* request) {
450 Variable var_current(this, MachineRepresentation::kTagged);
451 Label empty(this), loop(this, &var_current), done(this);
452
453 var_current.Bind(
454 LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset));
455 Branch(IsUndefined(var_current.value()), &empty, &loop);
456
457 Bind(&empty);
458 {
459 StoreObjectField(generator, JSAsyncGeneratorObject::kQueueOffset, request);
460 Goto(&done);
461 }
462
463 Bind(&loop);
464 {
465 Label loop_next(this), next_empty(this);
466 Node* current = var_current.value();
467 Node* next = LoadObjectField(current, AsyncGeneratorRequest::kNextOffset);
468
469 Branch(IsUndefined(next), &next_empty, &loop_next);
470 Bind(&next_empty);
471 {
472 StoreObjectField(current, AsyncGeneratorRequest::kNextOffset, request);
473 Goto(&done);
474 }
475
476 Bind(&loop_next);
477 {
478 var_current.Bind(next);
479 Goto(&loop);
480 }
481 }
482 Bind(&done);
483 }
484
485 Node* AsyncBuiltinsAssembler::TakeFirstAsyncGeneratorRequestFromQueue(
486 Node* generator) {
487 // Removes and returns the first AsyncGeneratorRequest from a
488 // JSAsyncGeneratorObject's queue. Asserts that the queue is not empty.
489 CSA_SLOW_ASSERT(this,
490 HasInstanceType(generator, JS_ASYNC_GENERATOR_OBJECT_TYPE));
491
492 Node* request =
493 LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset);
494 CSA_SLOW_ASSERT(this, WordNotEqual(request, UndefinedConstant()));
495
496 Node* next = LoadObjectField(request, AsyncGeneratorRequest::kNextOffset);
497
498 StoreObjectField(generator, JSAsyncGeneratorObject::kQueueOffset, next);
499 return request;
500 }
501
502 } // namespace internal
503 } // namespace v8
OLDNEW
« no previous file with comments | « src/builtins/builtins-async.cc ('k') | src/builtins/builtins-async-iterator.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698