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

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

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

Powered by Google App Engine
This is Rietveld 408576698