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

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

Issue 2645313003: [async-iteration] implement Async-from-Sync Iterator (Closed)
Patch Set: Move closure creation to a helper func Created 3 years, 10 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.h ('k') | src/code-stub-assembler.h » ('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 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.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 // Describe fields of Context associated with the AsyncIterator unwrap closure.
16 class ValueUnwrapContext {
Benedikt Meurer 2017/02/16 05:16:38 Can we wrap these helpers into an anonymous namesp
caitp 2017/02/16 14:25:11 Done.
17 public:
18 enum Fields { kDoneSlot = Context::MIN_CONTEXT_SLOTS, kLength };
19 };
20
21 class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler {
22 public:
23 explicit AsyncFromSyncBuiltinsAssembler(CodeAssemblerState* state)
24 : AsyncBuiltinsAssembler(state) {}
25
26 std::pair<Node*, Node*> LoadIteratorResult(Node* const context,
27 Node* const native_context,
28 Node* const iter_result,
29 Label* if_exception,
30 Variable* var_exception);
31
32 Node* CreateUnwrapClosure(Node* native_context, Node* done);
33 };
34
35 std::pair<Node*, Node*> AsyncFromSyncBuiltinsAssembler::LoadIteratorResult(
36 Node* const context, Node* const native_context, Node* const iter_result,
37 Label* if_exception, Variable* var_exception) {
38 Label if_fastpath(this), if_slowpath(this), merge(this), to_boolean(this),
39 done(this);
40 GotoIf(TaggedIsSmi(iter_result), &if_slowpath);
41
42 Node* const fast_iter_result_map =
43 LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
44 Node* const iter_result_map = LoadMap(iter_result);
45
46 Variable var_value(this, MachineRepresentation::kTagged);
47 Variable var_done(this, MachineRepresentation::kTagged);
48 Branch(WordEqual(iter_result_map, fast_iter_result_map), &if_fastpath,
49 &if_slowpath);
50
51 Bind(&if_fastpath);
52 {
53 var_value.Bind(
54 LoadObjectField(iter_result, JSIteratorResult::kValueOffset));
55 var_done.Bind(LoadObjectField(iter_result, JSIteratorResult::kDoneOffset));
56 Goto(&merge);
57 }
58
59 Bind(&if_slowpath);
60 {
61 // Let nextValue be IteratorValue(nextResult).
62 // IfAbruptRejectPromise(nextValue, promiseCapability).
63 Node* const value =
64 CallStub(CodeFactory::GetProperty(isolate()), context, iter_result,
65 HeapConstant(factory()->value_string()));
66 GotoIfException(value, if_exception, var_exception);
67
68 // Let nextDone be IteratorComplete(nextResult).
69 // IfAbruptRejectPromise(nextDone, promiseCapability).
70 Node* const done =
71 CallStub(CodeFactory::GetProperty(isolate()), context, iter_result,
72 HeapConstant(factory()->done_string()));
73 GotoIfException(done, if_exception, var_exception);
74
75 var_value.Bind(value);
76 var_done.Bind(done);
77 Goto(&merge);
78 }
79
80 Bind(&merge);
81 // Ensure `iterResult.done` is a Boolean.
82 GotoIf(TaggedIsSmi(var_done.value()), &to_boolean);
83 Branch(IsBooleanMap(LoadMap(var_done.value())), &done, &to_boolean);
84
85 Bind(&to_boolean);
86 {
87 Node* const result =
88 CallStub(CodeFactory::ToBoolean(isolate()), context, var_done.value());
89 var_done.Bind(result);
90 Goto(&done);
91 }
92
93 Bind(&done);
94 return std::make_pair(var_value.value(), var_done.value());
95 }
96
97 Node* AsyncFromSyncBuiltinsAssembler::CreateUnwrapClosure(Node* native_context,
98 Node* done) {
99 Node* const map = LoadContextElement(
100 native_context, Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX);
101 Node* const on_resolve_shared = LoadContextElement(
102 native_context, Context::ASYNC_ITERATOR_VALUE_UNWRAP_SHARED_FUN);
103 CSA_ASSERT(this,
104 HasInstanceType(on_resolve_shared, SHARED_FUNCTION_INFO_TYPE));
105 Node* const closure_context =
106 AllocateAsyncIteratorValueUnwrapContext(native_context, done);
107 return AllocateFunctionWithMapAndContext(map, on_resolve_shared,
108 closure_context);
109 }
110
111 // https://tc39.github.io/proposal-async-iteration/
112 // Section #sec-%asyncfromsynciteratorprototype%.next
113 TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext, AsyncFromSyncBuiltinsAssembler) {
114 const char* method_name = "[Async-from-Sync Iterator].prototype.next";
115
116 Node* const iterator = Parameter(0);
117 Node* const parameter = Parameter(1);
118 Node* const context = Parameter(4);
119
120 Node* const native_context = LoadNativeContext(context);
121 Node* const promise = AllocateAndInitJSPromise(context);
122
123 Variable var_parent(this, MachineRepresentation::kTagged);
124 Variable var_exception(this, MachineRepresentation::kTagged);
125 var_exception.Bind(UndefinedConstant());
126 Label reject_promise(this, Label::kDeferred);
127 Label if_receiverisincompatible(this, Label::kDeferred);
128
129 GotoIf(TaggedIsSmi(iterator), &if_receiverisincompatible);
130 GotoUnless(HasInstanceType(iterator, JS_ASYNC_FROM_SYNC_ITERATOR_TYPE),
131 &if_receiverisincompatible);
132
133 Node* const sync_iterator =
134 LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset);
135
136 // Let nextResult be IteratorNext(syncIterator, value).
137 // IfAbruptRejectPromise(nextResult, promiseCapability).
138 Node* next_method =
139 CallStub(CodeFactory::GetProperty(isolate()), context, sync_iterator,
140 HeapConstant(factory()->next_string()));
141 Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context,
142 next_method, sync_iterator, parameter);
143 GotoIfException(iter_result, &reject_promise, &var_exception);
144
145 Node* value;
146 Node* done;
147 std::tie(value, done) = LoadIteratorResult(
148 context, native_context, iter_result, &reject_promise, &var_exception);
149
150 // Let valueWrapperCapability be ! NewPromiseCapability(%Promise%).
151 Node* const wrapper = AllocateAndInitJSPromise(context);
152
153 // Perform ! Call(valueWrapperCapability.[[Resolve]], undefined,
154 // « nextValue »).
155 InternalResolvePromise(context, wrapper, value);
156
157 // Let onFulfilled be a new built-in function object as defined in
158 // Async Iterator Value Unwrap Functions.
159 // Set onFulfilled.[[Done]] to nextDone.
160 Node* const on_resolve = CreateUnwrapClosure(native_context, done);
161
162 // Perform ! PerformPromiseThen(valueWrapperCapability.[[Promise]],
163 // onFulfilled, undefined, promiseCapability).
164 Node* const undefined = UndefinedConstant();
165 InternalPerformPromiseThen(context, wrapper, on_resolve, undefined, promise,
166 undefined, undefined);
167
168 // TODO(caitp): Use TailCallStub once it's possible for a JS-linkage builtin
Benedikt Meurer 2017/02/16 05:16:38 Is the TailCall required by the specification? Bec
caitp 2017/02/16 14:25:11 Not required by the spec. My reasoning is, it shou
169 // to tailcall a CS-linkage builtin when an arguments adaptor frame is used.
170 Return(promise);
171
172 Bind(&if_receiverisincompatible);
173 {
174 // If Type(O) is not Object, or if O does not have a [[SyncIterator]]
175 // internal slot, then
176
177 // Let badIteratorError be a new TypeError exception.
178 Node* const error =
179 MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver, context,
180 CStringConstant(method_name), iterator);
181
182 // Perform ! Call(promiseCapability.[[Reject]], undefined,
183 // « badIteratorError »).
184 var_exception.Bind(error);
185 Goto(&reject_promise);
186 }
187
188 Bind(&reject_promise);
189 {
190 Node* const promise = AllocateAndInitJSPromise(context);
191 Node* const exception = var_exception.value();
192 InternalPromiseReject(context, promise, exception, TrueConstant());
193
194 // Return promiseCapability.[[Promise]].
195 Return(promise);
196 }
197 }
198
199 // https://tc39.github.io/proposal-async-iteration/
200 // Section #sec-%asyncfromsynciteratorprototype%.return
201 TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn,
202 AsyncFromSyncBuiltinsAssembler) {
203 const char* method_name = "[Async-from-Sync Iterator].prototype.return";
204
205 Node* const iterator = Parameter(0);
206 Node* const parameter = Parameter(1);
207 Node* const context = Parameter(4);
208
209 Node* const native_context = LoadNativeContext(context);
210 Node* const promise = AllocateAndInitJSPromise(context);
211
212 Variable var_exception(this, MachineRepresentation::kTagged);
213 var_exception.Bind(UndefinedConstant());
214 Label reject_promise(this, Label::kDeferred);
215 Label if_receiverisincompatible(this, Label::kDeferred);
216
217 GotoIf(TaggedIsSmi(iterator), &if_receiverisincompatible);
218 GotoUnless(HasInstanceType(iterator, JS_ASYNC_FROM_SYNC_ITERATOR_TYPE),
219 &if_receiverisincompatible);
220
221 Node* const sync_iterator =
222 LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset);
223
224 // Let return be GetMethod(syncIterator, "return").
225 // IfAbruptRejectPromise(return, promiseCapability).
226 Node* const return_method =
227 CallStub(CodeFactory::GetProperty(isolate()), context, sync_iterator,
228 HeapConstant(factory()->return_string()));
229 GotoIfException(return_method, &reject_promise, &var_exception);
230
231 Variable var_value(this, MachineRepresentation::kTagged);
232 Variable var_done(this, MachineRepresentation::kTagged);
233
234 Label if_isundefined(this), if_isnotundefined(this), resolve_promise(this);
235
236 Branch(IsUndefined(return_method), &if_isundefined, &if_isnotundefined);
237 Bind(&if_isundefined);
238 {
239 // If return is undefined, then
240 // Let iterResult be ! CreateIterResultObject(value, true)
241 Node* const iter_result =
242 CallStub(CodeFactory::CreateIterResultObject(isolate()), context,
243 parameter, TrueConstant());
244
245 // Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »).
246 // IfAbruptRejectPromise(nextDone, promiseCapability).
247 // Return promiseCapability.[[Promise]].
248 PromiseFulfill(context, promise, iter_result, v8::Promise::kFulfilled);
249 Return(promise);
250 }
251
252 Bind(&if_isnotundefined);
253 {
254 // Let returnResult be Call(return, syncIterator, « value »).
255 Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context,
256 return_method, sync_iterator, parameter);
257 // IfAbruptRejectPromise(returnResult, promiseCapability).
258 GotoIfException(iter_result, &reject_promise, &var_exception);
259
260 Node* value;
261 Node* done;
262 std::tie(value, done) = LoadIteratorResult(
263 context, native_context, iter_result, &reject_promise, &var_exception);
264
265 var_value.Bind(value);
266 var_done.Bind(done);
267 Goto(&resolve_promise);
268 }
269
270 Bind(&resolve_promise);
271 {
272 // Let valueWrapperCapability be ! NewPromiseCapability(%Promise%).
273 Node* const wrapper = AllocateAndInitJSPromise(context);
274 Node* const value = var_value.value();
275 Node* const done = var_done.value();
276
277 // Perform ! Call(valueWrapperCapability.[[Resolve]], undefined,
278 // « nextValue »).
279 InternalResolvePromise(context, wrapper, value);
280
281 // Let onFulfilled be a new built-in function object as defined in
282 // Async Iterator Value Unwrap Functions.
283 // Set onFulfilled.[[Done]] to nextDone.
284 Node* const on_resolve = CreateUnwrapClosure(native_context, done);
285
286 // Perform ! PerformPromiseThen(valueWrapperCapability.[[Promise]],
287 // onFulfilled, undefined, promiseCapability).
288 Node* const undefined = UndefinedConstant();
289 InternalPerformPromiseThen(context, wrapper, on_resolve, undefined, promise,
290 undefined, undefined);
291 Return(promise);
292 }
293
294 Bind(&if_receiverisincompatible);
295 {
296 // If Type(O) is not Object, or if O does not have a [[SyncIterator]]
297 // internal slot, then
298
299 // Let badIteratorError be a new TypeError exception.
300 Node* const error =
301 MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver, context,
302 CStringConstant(method_name), iterator);
303
304 // Perform ! Call(promiseCapability.[[Reject]], undefined,
305 // « badIteratorError »).
306 var_exception.Bind(error);
307 Goto(&reject_promise);
308 }
309
310 Bind(&reject_promise);
311 {
312 Node* const exception = var_exception.value();
313 InternalPromiseReject(context, promise, exception, TrueConstant());
314
315 // Return promiseCapability.[[Promise]].
316 Return(promise);
317 }
318 }
319
320 // https://tc39.github.io/proposal-async-iteration/
321 // Section #sec-%asyncfromsynciteratorprototype%.throw
322 TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow,
323 AsyncFromSyncBuiltinsAssembler) {
324 const char* method_name = "[Async-from-Sync Iterator].prototype.throw";
325
326 Node* const iterator = Parameter(0);
327 Node* const parameter = Parameter(1);
328 Node* const context = Parameter(4);
329
330 Node* const native_context = LoadNativeContext(context);
331 Node* const promise = AllocateAndInitJSPromise(context);
332
333 Variable var_exception(this, MachineRepresentation::kTagged);
334 var_exception.Bind(parameter);
335 Variable var_value(this, MachineRepresentation::kTagged);
336 Variable var_done(this, MachineRepresentation::kTagged);
337 Label reject_promise(this, Label::kDeferred);
338 Label if_receiverisincompatible(this, Label::kDeferred);
339
340 GotoIf(TaggedIsSmi(iterator), &if_receiverisincompatible);
341 GotoUnless(HasInstanceType(iterator, JS_ASYNC_FROM_SYNC_ITERATOR_TYPE),
342 &if_receiverisincompatible);
343
344 Node* const sync_iterator =
345 LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset);
346
347 // Let throw be GetMethod(syncIterator, "throw").
348 // IfAbruptRejectPromise(nextResult, promiseCapability).
349 // IfAbruptRejectPromise(throw, promiseCapability).
350 Node* const throw_method =
351 CallStub(CodeFactory::GetProperty(isolate()), context, sync_iterator,
Benedikt Meurer 2017/02/16 05:16:38 How about introducing a helper method on the CSA f
caitp 2017/02/16 14:25:11 Done.
352 HeapConstant(factory()->throw_string()));
353 GotoIfException(throw_method, &reject_promise, &var_exception);
354
355 Label if_isnotundefined(this), resolve_promise(this);
356
357 // If throw is undefined, then
358 // Perform ! Call(promiseCapability.[[Reject]], undefined, « value »).
359 Branch(IsUndefined(throw_method), &reject_promise, &if_isnotundefined);
360
361 Bind(&if_isnotundefined);
362 {
363 // Let throwResult be Call(throw, syncIterator, « value »).
364 Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context,
365 throw_method, sync_iterator, parameter);
366 GotoIfException(iter_result, &reject_promise, &var_exception);
367
368 Node* value;
369 Node* done;
370 std::tie(value, done) = LoadIteratorResult(
371 context, native_context, iter_result, &reject_promise, &var_exception);
372
373 // Let valueWrapperCapability be ! NewPromiseCapability(%Promise%).
374 Node* const wrapper = AllocateAndInitJSPromise(context);
375
376 // Perform ! Call(valueWrapperCapability.[[Resolve]], undefined, «
377 // throwValue »).
378 InternalResolvePromise(context, wrapper, value);
379
380 // Let onFulfilled be a new built-in function object as defined in
381 // Async Iterator Value Unwrap Functions.
382 // Set onFulfilled.[[Done]] to throwDone.
383 Node* const on_resolve = CreateUnwrapClosure(native_context, done);
384
385 // Perform ! PerformPromiseThen(valueWrapperCapability.[[Promise]],
386 // onFulfilled, undefined, promiseCapability).
387 Node* const undefined = UndefinedConstant();
388 InternalPerformPromiseThen(context, wrapper, on_resolve, undefined, promise,
389 undefined, undefined);
390 Return(promise);
391 }
392
393 Bind(&if_receiverisincompatible);
394 {
395 // If Type(O) is not Object, or if O does not have a [[SyncIterator]]
396 // internal slot, then
397
398 // Let badIteratorError be a new TypeError exception.
399 Node* const error =
400 MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver, context,
401 CStringConstant(method_name), iterator);
402
403 // Perform ! Call(promiseCapability.[[Reject]], undefined,
404 // « badIteratorError »).
405 var_exception.Bind(error);
406 Goto(&reject_promise);
407 }
408
409 Bind(&reject_promise);
410 {
411 Node* const exception = var_exception.value();
412 InternalPromiseReject(context, promise, exception, TrueConstant());
413
414 // Return promiseCapability.[[Promise]].
415 Return(promise);
416 }
417 }
418
419 TF_BUILTIN(AsyncIteratorValueUnwrap, AsyncBuiltinsAssembler) {
420 Node* const value = Parameter(1);
421 Node* const context = Parameter(4);
422
423 Node* const done = LoadContextElement(context, ValueUnwrapContext::kDoneSlot);
424 Node* const unwrapped_value = CallStub(
425 CodeFactory::CreateIterResultObject(isolate()), context, value, done);
426
427 Return(unwrapped_value);
428 }
429
430 Node* AsyncBuiltinsAssembler::AllocateAsyncIteratorValueUnwrapContext(
431 Node* native_context, Node* done) {
432 Node* const context =
433 CreatePromiseContext(native_context, ValueUnwrapContext::kLength);
434 StoreContextElementNoWriteBarrier(context, ValueUnwrapContext::kDoneSlot,
435 done);
436 return context;
437 }
438
439 } // namespace internal
440 } // namespace v8
OLDNEW
« no previous file with comments | « src/builtins/builtins-async.h ('k') | src/code-stub-assembler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698