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

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

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

Powered by Google App Engine
This is Rietveld 408576698