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

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

Issue 2622833002: WIP [esnext] implement async iteration proposal (Closed)
Patch Set: fix async tests 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
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 WordEqual(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(this,
80 IsUndefined(LoadObjectField(
81 request, AsyncGeneratorRequest::kAwaitedPromiseOffset)));
82
83 Node* const iter_result = AllocateJSIteratorResult(context, value, false);
84
85 InternalResolvePromise(context, promise, iter_result);
86
87 AsyncGeneratorResumeNext(context, generator);
88
89 Return(UndefinedConstant());
90 }
91
92 TF_BUILTIN(AsyncGeneratorRawYield, AsyncBuiltinsAssembler) {
93 Node* const generator = Parameter(0);
94 Node* const value = Parameter(1);
95 Node* const context = Parameter(4);
96
97 CSA_ASSERT_JS_ARGC_EQ(this, 1);
98 CSA_SLOW_ASSERT(this,
99 HasInstanceType(generator, JS_ASYNC_GENERATOR_OBJECT_TYPE));
100
101 Node* request = TakeFirstAsyncGeneratorRequestFromQueue(generator);
102
103 Node* const promise =
104 LoadObjectField(request, AsyncGeneratorRequest::kPromiseOffset);
105
106 // The request Promise must still be pending at this point.
107 CSA_SLOW_ASSERT(this,
108 WordEqual(LoadObjectField(promise, JSPromise::kStatusOffset),
109 SmiConstant(v8::Promise::kPending)));
110
111 // If a yield expression is reached, we must not be awaiting any value.
112 CSA_SLOW_ASSERT(this,
113 IsUndefined(LoadObjectField(
114 request, AsyncGeneratorRequest::kAwaitedPromiseOffset)));
115
116 InternalResolvePromise(context, promise, value);
117
118 AsyncGeneratorResumeNext(context, generator);
119
120 Return(UndefinedConstant());
121 }
122
123 TF_BUILTIN(AsyncGeneratorAwaitResolveClosure, AsyncBuiltinsAssembler) {
124 Node* value = Parameter(1);
125 Node* context = Parameter(4);
126
127 AsyncGeneratorAwaitResumeClosure(context, value,
128 JSAsyncGeneratorObject::kNext);
129 Return(UndefinedConstant());
130 }
131
132 TF_BUILTIN(AsyncGeneratorAwaitRejectClosure, AsyncBuiltinsAssembler) {
133 Node* value = Parameter(1);
134 Node* context = Parameter(4);
135
136 AsyncGeneratorAwaitResumeClosure(context, value,
137 JSAsyncGeneratorObject::kThrow);
138 Return(UndefinedConstant());
139 }
140
141 TF_BUILTIN(AsyncGeneratorAwaitUncaught, AsyncBuiltinsAssembler) {
142 const bool kIsCatchable = false;
143 AsyncGeneratorAwait(kIsCatchable);
144 }
145
146 TF_BUILTIN(AsyncGeneratorAwaitCaught, AsyncBuiltinsAssembler) {
147 const bool kIsCatchable = true;
148 AsyncGeneratorAwait(kIsCatchable);
149 }
150
151 // Shared implementation for the 3 Async Iterator protocol methods of Async
152 // Generators.
153 void AsyncBuiltinsAssembler::AsyncGeneratorEnqueue(
154 Node* context, Node* generator, Node* value,
155 JSAsyncGeneratorObject::ResumeMode resume_mode, const char* method_name) {
156 // AsyncGeneratorEnqueue produces a new Promise, and appends it to the list
157 // of async generator requests to be executed. If the generator is not
158 // presently executing, then this method will loop through, processing each
159 // request from front to back.
160 // This loop is resides in AsyncGeneratorResumeNext.
161 Node* promise = AllocateAndInitJSPromise(context);
162
163 Label enqueue(this), if_receiverisincompatible(this, Label::kDeferred);
164
165 GotoIf(TaggedIsSmi(generator), &if_receiverisincompatible);
166 Branch(HasInstanceType(generator, JS_ASYNC_GENERATOR_OBJECT_TYPE), &enqueue,
167 &if_receiverisincompatible);
168
169 Bind(&enqueue);
170 {
171 Label done(this);
172 Node* const req =
173 AllocateAsyncGeneratorRequest(resume_mode, value, promise);
174
175 AddAsyncGeneratorRequestToQueue(generator, req);
176
177 // Let state be generator.[[AsyncGeneratorState]]
178 // If state is not "executing", then
179 // Perform AsyncGeneratorResumeNext(Generator)
180 // Check if the {receiver} is running or already closed.
181 Node* continuation =
182 LoadObjectField(generator, JSAsyncGeneratorObject::kContinuationOffset);
183
184 GotoIf(SmiEqual(continuation,
185 SmiConstant(JSAsyncGeneratorObject::kGeneratorExecuting)),
186 &done);
187
188 AsyncGeneratorResumeNext(context, generator, continuation);
189
190 Goto(&done);
191 Bind(&done);
192 Return(promise);
193 }
194
195 Bind(&if_receiverisincompatible);
196 {
197 Node* native_context = LoadNativeContext(context);
198 Node* make_type_error =
199 LoadContextElement(native_context, Context::MAKE_TYPE_ERROR_INDEX);
200 Handle<String> method =
201 factory()->NewStringFromAsciiChecked(method_name, TENURED);
202 Node* error =
203 CallJS(CodeFactory::Call(isolate()), context, make_type_error,
204 UndefinedConstant(),
205 SmiConstant(MessageTemplate::kIncompatibleMethodReceiver),
206 HeapConstant(method), generator);
207
208 CallRuntime(Runtime::kPromiseReject, context, promise, error,
209 TrueConstant());
210 Return(promise);
211 }
212 }
213
214 void AsyncBuiltinsAssembler::AsyncGeneratorResumeNext(Node* context,
215 Node* generator,
216 Node* continuation) {
217 // Loop through each queued request, from first to last, and resuming the
218 // generator. Will not resume generator if resumed with an abrupt completion
219 // such as via the .throw() or .return() methods, or if an Await is
220 // in progress.
221 Variable var_request(this, MachineRepresentation::kTagged);
222 Variable var_continuation(this, MachineRepresentation::kTaggedSigned);
223 var_continuation.Bind(continuation);
224
225 Variable* loop_vars[] = {&var_request, &var_continuation};
226 Label loop(this, arraysize(loop_vars),
227 reinterpret_cast<Variable **>(loop_vars)),
228 done(this);
229
230 var_request.Bind(
231 LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset));
232 Branch(WordEqual(var_request.value(), UndefinedConstant()), &done, &loop);
233 Bind(&loop);
234 {
235 AsyncGeneratorResumeNextStep(context, generator, var_request.value(),
236 var_continuation.value(), &done);
237
238 var_continuation.Bind(LoadObjectField(
239 generator, JSAsyncGeneratorObject::kContinuationOffset));
240 var_request.Bind(
241 LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset));
242
243 // Continue loop if there is another request queued up.
244 Branch(WordEqual(var_request.value(), UndefinedConstant()), &done, &loop);
245 }
246
247 Bind(&done);
248 }
249
250 void AsyncBuiltinsAssembler::AsyncGeneratorResumeNextStep(Node* context,
251 Node* generator,
252 Node* request,
253 Node* continuation,
254 Label* exit_loop) {
255 Variable var_return(this, MachineRepresentation::kTagged);
256 var_return.Bind(UndefinedConstant());
257 CSA_SLOW_ASSERT(this, HasInstanceType(request, ASYNC_GENERATOR_REQUEST_TYPE));
258
259 Label start(this), loop_next(this);
260
261 // Stop consuming the AsyncGeneratorRequest queue and resuming the generator
262 // if an Await is in progress.
263 Node* awaited =
264 LoadObjectField(request, AsyncGeneratorRequest::kAwaitedPromiseOffset);
265 Branch(IsUndefined(awaited), &start, exit_loop);
266
267 Bind(&start);
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 GotoIf(WordNotEqual(continuation, SmiConstant(0)), &check_completed);
288 StoreObjectField(generator, JSAsyncGeneratorObject::kContinuationOffset,
289 SmiConstant(JSAsyncGeneratorObject::kGeneratorClosed));
290 Goto(&if_completed);
291
292 Bind(&check_completed);
293 Branch(WordEqual(continuation,
294 SmiConstant(JSAsyncGeneratorObject::kGeneratorClosed)),
295 &if_completed, &resume_generator);
296
297 Bind(&if_completed);
298 {
299 // If state is "completed", then
300 // If completion.[[Type]] is return, then return
301 // ! AsyncGeneratorResolve(generator, completion.[[Value]], true).
302 // Else, return ! AsyncGeneratorReject(generator,
303 // completion.[[Value]])
304 var_return.Bind(value);
305 Branch(
306 WordEqual(resume_mode, SmiConstant(JSAsyncGeneratorObject::kReturn)),
307 &if_return, &if_throw);
308 }
309 }
310
311 Bind(&check_ifcompleted);
312 {
313 // Else if state is "completed", then return !
314 // AsyncGeneratorResolve(generator, undefined, true).
315 Branch(WordEqual(continuation,
316 SmiConstant(JSAsyncGeneratorObject::kGeneratorClosed)),
317 &if_return, &resume_generator);
318 }
319
320 Bind(&resume_generator);
321 {
322 CSA_ASSERT(this, SmiAboveOrEqual(continuation, SmiConstant(0)));
323 Node* result = CallStub(CodeFactory::ResumeGenerator(isolate()), context,
324 value, generator, resume_mode);
325 Goto(&loop_next);
326 }
327
328 Bind(&if_return);
329 {
330 Node* const request = TakeFirstAsyncGeneratorRequestFromQueue(generator);
331 Node* const promise =
332 LoadObjectField(request, AsyncGeneratorRequest::kPromiseOffset);
333 Node* const result =
334 AllocateJSIteratorResult(context, var_return.value(), true);
335 PromiseFulfill(context, promise, result, v8::Promise::kFulfilled);
336 Goto(&loop_next);
337 }
338
339 Bind(&if_throw);
340 {
341 Node* request = TakeFirstAsyncGeneratorRequestFromQueue(generator);
342
343 // Let promiseCapability be next.[[Capability]].
344 Node* promise =
345 LoadObjectField(request, AsyncGeneratorRequest::kPromiseOffset);
346
347 // Perform ! Call(promiseCapability.[[Reject]], undefined, « exception »).
348 CallRuntime(Runtime::kPromiseReject, context, promise, value,
349 TrueConstant());
350 Goto(&loop_next);
351 }
352
353 Bind(&loop_next);
354 }
355
356 Node* AsyncBuiltinsAssembler::AllocateAsyncGeneratorRequest(
357 JSAsyncGeneratorObject::ResumeMode resume_mode, Node* resume_value,
358 Node* promise) {
359 CSA_SLOW_ASSERT(this, HasInstanceType(promise, JS_PROMISE_TYPE));
360 Node* request = Allocate(AsyncGeneratorRequest::kSize);
361 StoreMapNoWriteBarrier(request, Heap::kAsyncGeneratorRequestMapRootIndex);
362 StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kNextOffset,
363 UndefinedConstant());
364 StoreObjectFieldNoWriteBarrier(request,
365 AsyncGeneratorRequest::kResumeModeOffset,
366 SmiConstant(resume_mode));
367 StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kValueOffset,
368 resume_value);
369 StoreObjectFieldNoWriteBarrier(request, AsyncGeneratorRequest::kPromiseOffset,
370 promise);
371 StoreObjectFieldNoWriteBarrier(request,
372 AsyncGeneratorRequest::kAwaitedPromiseOffset,
373 UndefinedConstant());
374 return request;
375 }
376
377 void AsyncBuiltinsAssembler::AsyncGeneratorAwaitResumeClosure(
378 Node* context, Node* value,
379 JSAsyncGeneratorObject::ResumeMode resume_mode) {
380 Label done(this);
381 Node* const generator =
382 LoadContextElement(context, AwaitContext::kGeneratorSlot);
383 CSA_SLOW_ASSERT(this,
384 HasInstanceType(generator, JS_ASYNC_GENERATOR_OBJECT_TYPE));
385
386 Node* const request =
387 LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset);
388 CSA_SLOW_ASSERT(this, HasInstanceType(request, ASYNC_GENERATOR_REQUEST_TYPE));
389
390 Node* const promise =
391 LoadObjectField(request, AsyncGeneratorRequest::kAwaitedPromiseOffset);
392 CSA_SLOW_ASSERT(this, HasInstanceType(promise, JS_PROMISE_TYPE));
393 CSA_SLOW_ASSERT(
394 this, WordNotEqual(LoadObjectField(promise, JSPromise::kStatusOffset),
395 SmiConstant(v8::Promise::kPending)));
396
397 StoreObjectField(request, AsyncGeneratorRequest::kAwaitedPromiseOffset,
398 UndefinedConstant());
399
400 Node* const continuation =
401 LoadObjectField(generator, JSAsyncGeneratorObject::kContinuationOffset);
402
403 GotoUnless(SmiAboveOrEqual(continuation, SmiConstant(0)), &done);
404 CallStub(CodeFactory::ResumeAwaitedGenerator(isolate()), context, value,
405 generator, SmiConstant(resume_mode));
406
407 Goto(&done);
408 Bind(&done);
409 AsyncGeneratorResumeNext(context, generator);
410 }
411
412 void AsyncBuiltinsAssembler::AsyncGeneratorAwait(bool is_catchable) {
413 Node* generator = Parameter(1);
414 Node* value = Parameter(2);
415 Node* context = Parameter(5);
416
417 CSA_ASSERT_JS_ARGC_EQ(this, 2);
418
419 CSA_SLOW_ASSERT(this,
420 HasInstanceType(generator, JS_ASYNC_GENERATOR_OBJECT_TYPE));
421
422 Node* const request =
423 LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset);
424 CSA_SLOW_ASSERT(this, WordNotEqual(request, UndefinedConstant()));
425
426 NodeGenerator1 closure_context = [&](Node* native_context) -> Node* {
427 Node* const context =
428 CreatePromiseContext(native_context, AwaitContext::kLength);
429 StoreContextElementNoWriteBarrier(context, AwaitContext::kGeneratorSlot,
430 generator);
431 return context;
432 };
433
434 Node* outer_promise =
435 LoadObjectField(request, AsyncGeneratorRequest::kPromiseOffset);
436
437 const int reject_index = Context::ASYNC_GENERATOR_AWAIT_REJECT_SHARED_FUN;
438 STATIC_ASSERT((Context::ASYNC_GENERATOR_AWAIT_REJECT_SHARED_FUN + 1) ==
439 Context::ASYNC_GENERATOR_AWAIT_RESOLVE_SHARED_FUN);
Dan Ehrenberg 2017/01/14 00:48:38 Oh sorry I had previously missed this--I guess you
440
441 Node* promise = Await(context, generator, value, outer_promise,
442 closure_context, reject_index, is_catchable);
443
444 CSA_SLOW_ASSERT(this,
445 IsUndefined(LoadObjectField(
446 request, AsyncGeneratorRequest::kAwaitedPromiseOffset)));
447 StoreObjectField(request, AsyncGeneratorRequest::kAwaitedPromiseOffset,
448 promise);
449 Return(UndefinedConstant());
450 }
451
452 void AsyncBuiltinsAssembler::AddAsyncGeneratorRequestToQueue(Node* generator,
453 Node* request) {
Dan Ehrenberg 2017/01/14 00:48:38 +gsathya would it make sense to share this (admitt
454 Variable var_current(this, MachineRepresentation::kTagged);
455 Label empty(this), loop(this, &var_current), done(this);
456
457 var_current.Bind(
458 LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset));
459 Branch(IsUndefined(var_current.value()), &empty, &loop);
460
461 Bind(&empty);
462 {
463 StoreObjectField(generator, JSAsyncGeneratorObject::kQueueOffset, request);
464 Goto(&done);
465 }
466
467 Bind(&loop);
468 {
469 Label loop_next(this), next_empty(this);
470 Node* current = var_current.value();
471 Node* next = LoadObjectField(current, AsyncGeneratorRequest::kNextOffset);
472
473 Branch(IsUndefined(next), &next_empty, &loop_next);
474 Bind(&next_empty);
475 {
476 StoreObjectField(current, AsyncGeneratorRequest::kNextOffset, request);
477 Goto(&done);
478 }
479
480 Bind(&loop_next);
481 {
482 var_current.Bind(next);
483 Goto(&loop);
484 }
485 }
486 Bind(&done);
487 }
488
489 Node* AsyncBuiltinsAssembler::TakeFirstAsyncGeneratorRequestFromQueue(
490 Node* generator) {
491 // Removes and returns the first AsyncGeneratorRequest from a
492 // JSAsyncGeneratorObject's queue. Asserts that the queue is not empty.
Dan Ehrenberg 2017/01/14 00:48:38 Ignorable style suggestion: For a comment that des
493 CSA_SLOW_ASSERT(this,
494 HasInstanceType(generator, JS_ASYNC_GENERATOR_OBJECT_TYPE));
495
496 Node* request =
497 LoadObjectField(generator, JSAsyncGeneratorObject::kQueueOffset);
498 CSA_SLOW_ASSERT(this, WordNotEqual(request, UndefinedConstant()));
499
500 Node* next = LoadObjectField(request, AsyncGeneratorRequest::kNextOffset);
501
502 StoreObjectField(generator, JSAsyncGeneratorObject::kQueueOffset, next);
503 return request;
504 }
505
506 } // namespace internal
507 } // namespace v8
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698