• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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-gen.h"
6 #include "src/builtins/builtins-utils-gen.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 using compiler::Node;
16 
17 namespace {
18 class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler {
19  public:
AsyncFromSyncBuiltinsAssembler(compiler::CodeAssemblerState * state)20   explicit AsyncFromSyncBuiltinsAssembler(compiler::CodeAssemblerState* state)
21       : AsyncBuiltinsAssembler(state) {}
22 
23   void ThrowIfNotAsyncFromSyncIterator(Node* const context, Node* const object,
24                                        Label* if_exception,
25                                        Variable* var_exception,
26                                        const char* method_name);
27 
28   typedef std::function<void(Node* const context, Node* const promise,
29                              Label* if_exception)>
30       UndefinedMethodHandler;
31   typedef std::function<Node*(Node*)> SyncIteratorNodeGenerator;
32   void Generate_AsyncFromSyncIteratorMethod(
33       Node* const context, Node* const iterator, Node* const sent_value,
34       const SyncIteratorNodeGenerator& get_method,
35       const UndefinedMethodHandler& if_method_undefined,
36       const char* operation_name,
37       Label::Type reject_label_type = Label::kDeferred,
38       Node* const initial_exception_value = nullptr);
39   void Generate_AsyncFromSyncIteratorMethodOptimized(
40       Node* const context, Node* const iterator, Node* const sent_value,
41       const SyncIteratorNodeGenerator& get_method,
42       const UndefinedMethodHandler& if_method_undefined,
43       const char* operation_name,
44       Label::Type reject_label_type = Label::kDeferred,
45       Node* const initial_exception_value = nullptr);
46 
Generate_AsyncFromSyncIteratorMethod(Node * const context,Node * const iterator,Node * const sent_value,Handle<String> name,const UndefinedMethodHandler & if_method_undefined,const char * operation_name,Label::Type reject_label_type=Label::kDeferred,Node * const initial_exception_value=nullptr)47   void Generate_AsyncFromSyncIteratorMethod(
48       Node* const context, Node* const iterator, Node* const sent_value,
49       Handle<String> name, const UndefinedMethodHandler& if_method_undefined,
50       const char* operation_name,
51       Label::Type reject_label_type = Label::kDeferred,
52       Node* const initial_exception_value = nullptr) {
53     auto get_method = [=](Node* const sync_iterator) {
54       return GetProperty(context, sync_iterator, name);
55     };
56     return Generate_AsyncFromSyncIteratorMethod(
57         context, iterator, sent_value, get_method, if_method_undefined,
58         operation_name, reject_label_type, initial_exception_value);
59   }
Generate_AsyncFromSyncIteratorMethodOptimized(Node * const context,Node * const iterator,Node * const sent_value,Handle<String> name,const UndefinedMethodHandler & if_method_undefined,const char * operation_name,Label::Type reject_label_type=Label::kDeferred,Node * const initial_exception_value=nullptr)60   void Generate_AsyncFromSyncIteratorMethodOptimized(
61       Node* const context, Node* const iterator, Node* const sent_value,
62       Handle<String> name, const UndefinedMethodHandler& if_method_undefined,
63       const char* operation_name,
64       Label::Type reject_label_type = Label::kDeferred,
65       Node* const initial_exception_value = nullptr) {
66     auto get_method = [=](Node* const sync_iterator) {
67       return GetProperty(context, sync_iterator, name);
68     };
69     return Generate_AsyncFromSyncIteratorMethodOptimized(
70         context, iterator, sent_value, get_method, if_method_undefined,
71         operation_name, reject_label_type, initial_exception_value);
72   }
73 
74   // Load "value" and "done" from an iterator result object. If an exception
75   // is thrown at any point, jumps to te `if_exception` label with exception
76   // stored in `var_exception`.
77   //
78   // Returns a Pair of Nodes, whose first element is the value of the "value"
79   // property, and whose second element is the value of the "done" property,
80   // converted to a Boolean if needed.
81   std::pair<Node*, Node*> LoadIteratorResult(Node* const context,
82                                              Node* const native_context,
83                                              Node* const iter_result,
84                                              Label* if_exception,
85                                              Variable* var_exception);
86 };
87 
ThrowIfNotAsyncFromSyncIterator(Node * const context,Node * const object,Label * if_exception,Variable * var_exception,const char * method_name)88 void AsyncFromSyncBuiltinsAssembler::ThrowIfNotAsyncFromSyncIterator(
89     Node* const context, Node* const object, Label* if_exception,
90     Variable* var_exception, const char* method_name) {
91   Label if_receiverisincompatible(this, Label::kDeferred), done(this);
92 
93   GotoIf(TaggedIsSmi(object), &if_receiverisincompatible);
94   Branch(HasInstanceType(object, JS_ASYNC_FROM_SYNC_ITERATOR_TYPE), &done,
95          &if_receiverisincompatible);
96 
97   BIND(&if_receiverisincompatible);
98   {
99     // If Type(O) is not Object, or if O does not have a [[SyncIterator]]
100     // internal slot, then
101 
102     // Let badIteratorError be a new TypeError exception.
103     Node* const error =
104         MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver, context,
105                       StringConstant(method_name), object);
106 
107     // Perform ! Call(promiseCapability.[[Reject]], undefined,
108     //                « badIteratorError »).
109     var_exception->Bind(error);
110     Goto(if_exception);
111   }
112 
113   BIND(&done);
114 }
115 
Generate_AsyncFromSyncIteratorMethod(Node * const context,Node * const iterator,Node * const sent_value,const SyncIteratorNodeGenerator & get_method,const UndefinedMethodHandler & if_method_undefined,const char * operation_name,Label::Type reject_label_type,Node * const initial_exception_value)116 void AsyncFromSyncBuiltinsAssembler::Generate_AsyncFromSyncIteratorMethod(
117     Node* const context, Node* const iterator, Node* const sent_value,
118     const SyncIteratorNodeGenerator& get_method,
119     const UndefinedMethodHandler& if_method_undefined,
120     const char* operation_name, Label::Type reject_label_type,
121     Node* const initial_exception_value) {
122   Node* const native_context = LoadNativeContext(context);
123   Node* const promise = AllocateAndInitJSPromise(context);
124 
125   VARIABLE(var_exception, MachineRepresentation::kTagged,
126            initial_exception_value == nullptr ? UndefinedConstant()
127                                               : initial_exception_value);
128   Label reject_promise(this, reject_label_type);
129 
130   ThrowIfNotAsyncFromSyncIterator(context, iterator, &reject_promise,
131                                   &var_exception, operation_name);
132 
133   Node* const sync_iterator =
134       LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset);
135 
136   Node* const method = get_method(sync_iterator);
137 
138   if (if_method_undefined) {
139     Label if_isnotundefined(this);
140 
141     GotoIfNot(IsUndefined(method), &if_isnotundefined);
142     if_method_undefined(native_context, promise, &reject_promise);
143 
144     BIND(&if_isnotundefined);
145   }
146 
147   Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context,
148                                    method, sync_iterator, sent_value);
149   GotoIfException(iter_result, &reject_promise, &var_exception);
150 
151   Node* value;
152   Node* done;
153   std::tie(value, done) = LoadIteratorResult(
154       context, native_context, iter_result, &reject_promise, &var_exception);
155   Node* const wrapper = AllocateAndInitJSPromise(context);
156 
157   // Perform ! Call(valueWrapperCapability.[[Resolve]], undefined, «
158   // throwValue »).
159   CallBuiltin(Builtins::kResolvePromise, context, wrapper, value);
160 
161   // Let onFulfilled be a new built-in function object as defined in
162   // Async Iterator Value Unwrap Functions.
163   // Set onFulfilled.[[Done]] to throwDone.
164   Node* const on_fulfilled = CreateUnwrapClosure(native_context, done);
165 
166   // Perform ! PerformPromiseThen(valueWrapperCapability.[[Promise]],
167   //     onFulfilled, undefined, promiseCapability).
168   Return(CallBuiltin(Builtins::kPerformPromiseThen, context, wrapper,
169                      on_fulfilled, UndefinedConstant(), promise));
170 
171   BIND(&reject_promise);
172   {
173     Node* const exception = var_exception.value();
174     CallBuiltin(Builtins::kRejectPromise, context, promise, exception,
175                 TrueConstant());
176     Return(promise);
177   }
178 }
179 
180 void AsyncFromSyncBuiltinsAssembler::
Generate_AsyncFromSyncIteratorMethodOptimized(Node * const context,Node * const iterator,Node * const sent_value,const SyncIteratorNodeGenerator & get_method,const UndefinedMethodHandler & if_method_undefined,const char * operation_name,Label::Type reject_label_type,Node * const initial_exception_value)181     Generate_AsyncFromSyncIteratorMethodOptimized(
182         Node* const context, Node* const iterator, Node* const sent_value,
183         const SyncIteratorNodeGenerator& get_method,
184         const UndefinedMethodHandler& if_method_undefined,
185         const char* operation_name, Label::Type reject_label_type,
186         Node* const initial_exception_value) {
187   Node* const native_context = LoadNativeContext(context);
188   Node* const promise = AllocateAndInitJSPromise(context);
189 
190   VARIABLE(var_exception, MachineRepresentation::kTagged,
191            initial_exception_value == nullptr ? UndefinedConstant()
192                                               : initial_exception_value);
193   Label reject_promise(this, reject_label_type);
194 
195   ThrowIfNotAsyncFromSyncIterator(context, iterator, &reject_promise,
196                                   &var_exception, operation_name);
197 
198   Node* const sync_iterator =
199       LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset);
200 
201   Node* const method = get_method(sync_iterator);
202 
203   if (if_method_undefined) {
204     Label if_isnotundefined(this);
205 
206     GotoIfNot(IsUndefined(method), &if_isnotundefined);
207     if_method_undefined(native_context, promise, &reject_promise);
208 
209     BIND(&if_isnotundefined);
210   }
211 
212   Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context,
213                                    method, sync_iterator, sent_value);
214   GotoIfException(iter_result, &reject_promise, &var_exception);
215 
216   Node* value;
217   Node* done;
218   std::tie(value, done) = LoadIteratorResult(
219       context, native_context, iter_result, &reject_promise, &var_exception);
220 
221   Node* const promise_fun =
222       LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
223   CSA_ASSERT(this, IsConstructor(promise_fun));
224 
225   // Let valueWrapper be ? PromiseResolve(« value »).
226   Node* const valueWrapper = CallBuiltin(Builtins::kPromiseResolve,
227                                          native_context, promise_fun, value);
228 
229   // Let onFulfilled be a new built-in function object as defined in
230   // Async Iterator Value Unwrap Functions.
231   // Set onFulfilled.[[Done]] to throwDone.
232   Node* const on_fulfilled = CreateUnwrapClosure(native_context, done);
233 
234   // Perform ! PerformPromiseThen(valueWrapper,
235   //     onFulfilled, undefined, promiseCapability).
236   Return(CallBuiltin(Builtins::kPerformPromiseThen, context, valueWrapper,
237                      on_fulfilled, UndefinedConstant(), promise));
238 
239   BIND(&reject_promise);
240   {
241     Node* const exception = var_exception.value();
242     CallBuiltin(Builtins::kRejectPromise, context, promise, exception,
243                 TrueConstant());
244     Return(promise);
245   }
246 }
LoadIteratorResult(Node * const context,Node * const native_context,Node * const iter_result,Label * if_exception,Variable * var_exception)247 std::pair<Node*, Node*> AsyncFromSyncBuiltinsAssembler::LoadIteratorResult(
248     Node* const context, Node* const native_context, Node* const iter_result,
249     Label* if_exception, Variable* var_exception) {
250   Label if_fastpath(this), if_slowpath(this), merge(this), to_boolean(this),
251       done(this), if_notanobject(this, Label::kDeferred);
252   GotoIf(TaggedIsSmi(iter_result), &if_notanobject);
253 
254   Node* const iter_result_map = LoadMap(iter_result);
255   GotoIfNot(IsJSReceiverMap(iter_result_map), &if_notanobject);
256 
257   Node* const fast_iter_result_map =
258       LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
259 
260   VARIABLE(var_value, MachineRepresentation::kTagged);
261   VARIABLE(var_done, MachineRepresentation::kTagged);
262   Branch(WordEqual(iter_result_map, fast_iter_result_map), &if_fastpath,
263          &if_slowpath);
264 
265   BIND(&if_fastpath);
266   {
267     var_done.Bind(LoadObjectField(iter_result, JSIteratorResult::kDoneOffset));
268     var_value.Bind(
269         LoadObjectField(iter_result, JSIteratorResult::kValueOffset));
270     Goto(&merge);
271   }
272 
273   BIND(&if_slowpath);
274   {
275     // Let nextDone be IteratorComplete(nextResult).
276     // IfAbruptRejectPromise(nextDone, promiseCapability).
277     Node* const done =
278         GetProperty(context, iter_result, factory()->done_string());
279     GotoIfException(done, if_exception, var_exception);
280 
281     // Let nextValue be IteratorValue(nextResult).
282     // IfAbruptRejectPromise(nextValue, promiseCapability).
283     Node* const value =
284         GetProperty(context, iter_result, factory()->value_string());
285     GotoIfException(value, if_exception, var_exception);
286 
287     var_value.Bind(value);
288     var_done.Bind(done);
289     Goto(&merge);
290   }
291 
292   BIND(&if_notanobject);
293   {
294     // Sync iterator result is not an object --- Produce a TypeError and jump
295     // to the `if_exception` path.
296     Node* const error = MakeTypeError(
297         MessageTemplate::kIteratorResultNotAnObject, context, iter_result);
298     var_exception->Bind(error);
299     Goto(if_exception);
300   }
301 
302   BIND(&merge);
303   // Ensure `iterResult.done` is a Boolean.
304   GotoIf(TaggedIsSmi(var_done.value()), &to_boolean);
305   Branch(IsBoolean(var_done.value()), &done, &to_boolean);
306 
307   BIND(&to_boolean);
308   {
309     Node* const result =
310         CallBuiltin(Builtins::kToBoolean, context, var_done.value());
311     var_done.Bind(result);
312     Goto(&done);
313   }
314 
315   BIND(&done);
316   return std::make_pair(var_value.value(), var_done.value());
317 }
318 
319 }  // namespace
320 
321 // https://tc39.github.io/proposal-async-iteration/
322 // Section #sec-%asyncfromsynciteratorprototype%.next
TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext,AsyncFromSyncBuiltinsAssembler)323 TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext, AsyncFromSyncBuiltinsAssembler) {
324   Node* const iterator = Parameter(Descriptor::kReceiver);
325   Node* const value = Parameter(Descriptor::kValue);
326   Node* const context = Parameter(Descriptor::kContext);
327 
328   auto get_method = [=](Node* const unused) {
329     return LoadObjectField(iterator, JSAsyncFromSyncIterator::kNextOffset);
330   };
331   Generate_AsyncFromSyncIteratorMethod(
332       context, iterator, value, get_method, UndefinedMethodHandler(),
333       "[Async-from-Sync Iterator].prototype.next");
334 }
335 
TF_BUILTIN(AsyncFromSyncIteratorPrototypeNextOptimized,AsyncFromSyncBuiltinsAssembler)336 TF_BUILTIN(AsyncFromSyncIteratorPrototypeNextOptimized,
337            AsyncFromSyncBuiltinsAssembler) {
338   Node* const iterator = Parameter(Descriptor::kReceiver);
339   Node* const value = Parameter(Descriptor::kValue);
340   Node* const context = Parameter(Descriptor::kContext);
341 
342   auto get_method = [=](Node* const unused) {
343     return LoadObjectField(iterator, JSAsyncFromSyncIterator::kNextOffset);
344   };
345   Generate_AsyncFromSyncIteratorMethodOptimized(
346       context, iterator, value, get_method, UndefinedMethodHandler(),
347       "[Async-from-Sync Iterator].prototype.next");
348 }
349 
350 // https://tc39.github.io/proposal-async-iteration/
351 // Section #sec-%asyncfromsynciteratorprototype%.return
TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn,AsyncFromSyncBuiltinsAssembler)352 TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn,
353            AsyncFromSyncBuiltinsAssembler) {
354   Node* const iterator = Parameter(Descriptor::kReceiver);
355   Node* const value = Parameter(Descriptor::kValue);
356   Node* const context = Parameter(Descriptor::kContext);
357 
358   auto if_return_undefined = [=](Node* const native_context,
359                                  Node* const promise, Label* if_exception) {
360     // If return is undefined, then
361     // Let iterResult be ! CreateIterResultObject(value, true)
362     Node* const iter_result = CallBuiltin(Builtins::kCreateIterResultObject,
363                                           context, value, TrueConstant());
364 
365     // Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »).
366     // IfAbruptRejectPromise(nextDone, promiseCapability).
367     // Return promiseCapability.[[Promise]].
368     CallBuiltin(Builtins::kResolvePromise, context, promise, iter_result);
369     Return(promise);
370   };
371 
372   Generate_AsyncFromSyncIteratorMethod(
373       context, iterator, value, factory()->return_string(), if_return_undefined,
374       "[Async-from-Sync Iterator].prototype.return");
375 }
376 
TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturnOptimized,AsyncFromSyncBuiltinsAssembler)377 TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturnOptimized,
378            AsyncFromSyncBuiltinsAssembler) {
379   Node* const iterator = Parameter(Descriptor::kReceiver);
380   Node* const value = Parameter(Descriptor::kValue);
381   Node* const context = Parameter(Descriptor::kContext);
382 
383   auto if_return_undefined = [=](Node* const native_context,
384                                  Node* const promise, Label* if_exception) {
385     // If return is undefined, then
386     // Let iterResult be ! CreateIterResultObject(value, true)
387     Node* const iter_result = CallBuiltin(Builtins::kCreateIterResultObject,
388                                           context, value, TrueConstant());
389 
390     // Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »).
391     // IfAbruptRejectPromise(nextDone, promiseCapability).
392     // Return promiseCapability.[[Promise]].
393     CallBuiltin(Builtins::kResolvePromise, context, promise, iter_result);
394     Return(promise);
395   };
396 
397   Generate_AsyncFromSyncIteratorMethodOptimized(
398       context, iterator, value, factory()->return_string(), if_return_undefined,
399       "[Async-from-Sync Iterator].prototype.return");
400 }
401 
402 // https://tc39.github.io/proposal-async-iteration/
403 // Section #sec-%asyncfromsynciteratorprototype%.throw
TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow,AsyncFromSyncBuiltinsAssembler)404 TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow,
405            AsyncFromSyncBuiltinsAssembler) {
406   Node* const iterator = Parameter(Descriptor::kReceiver);
407   Node* const reason = Parameter(Descriptor::kReason);
408   Node* const context = Parameter(Descriptor::kContext);
409 
410   auto if_throw_undefined = [=](Node* const native_context, Node* const promise,
411                                 Label* if_exception) { Goto(if_exception); };
412 
413   Generate_AsyncFromSyncIteratorMethod(
414       context, iterator, reason, factory()->throw_string(), if_throw_undefined,
415       "[Async-from-Sync Iterator].prototype.throw", Label::kNonDeferred,
416       reason);
417 }
418 
TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrowOptimized,AsyncFromSyncBuiltinsAssembler)419 TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrowOptimized,
420            AsyncFromSyncBuiltinsAssembler) {
421   Node* const iterator = Parameter(Descriptor::kReceiver);
422   Node* const reason = Parameter(Descriptor::kReason);
423   Node* const context = Parameter(Descriptor::kContext);
424 
425   auto if_throw_undefined = [=](Node* const native_context, Node* const promise,
426                                 Label* if_exception) { Goto(if_exception); };
427 
428   Generate_AsyncFromSyncIteratorMethodOptimized(
429       context, iterator, reason, factory()->throw_string(), if_throw_undefined,
430       "[Async-from-Sync Iterator].prototype.throw", Label::kNonDeferred,
431       reason);
432 }
433 
434 }  // namespace internal
435 }  // namespace v8
436