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 
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)40   void Generate_AsyncFromSyncIteratorMethod(
41       Node* const context, Node* const iterator, Node* const sent_value,
42       Handle<String> name, 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     auto get_method = [=](Node* const sync_iterator) {
47       return GetProperty(context, sync_iterator, name);
48     };
49     return Generate_AsyncFromSyncIteratorMethod(
50         context, iterator, sent_value, get_method, if_method_undefined,
51         operation_name, reject_label_type, initial_exception_value);
52   }
53 
54   // Load "value" and "done" from an iterator result object. If an exception
55   // is thrown at any point, jumps to te `if_exception` label with exception
56   // stored in `var_exception`.
57   //
58   // Returns a Pair of Nodes, whose first element is the value of the "value"
59   // property, and whose second element is the value of the "done" property,
60   // converted to a Boolean if needed.
61   std::pair<Node*, Node*> LoadIteratorResult(Node* const context,
62                                              Node* const native_context,
63                                              Node* const iter_result,
64                                              Label* if_exception,
65                                              Variable* var_exception);
66 };
67 
ThrowIfNotAsyncFromSyncIterator(Node * const context,Node * const object,Label * if_exception,Variable * var_exception,const char * method_name)68 void AsyncFromSyncBuiltinsAssembler::ThrowIfNotAsyncFromSyncIterator(
69     Node* const context, Node* const object, Label* if_exception,
70     Variable* var_exception, const char* method_name) {
71   Label if_receiverisincompatible(this, Label::kDeferred), done(this);
72 
73   GotoIf(TaggedIsSmi(object), &if_receiverisincompatible);
74   Branch(HasInstanceType(object, JS_ASYNC_FROM_SYNC_ITERATOR_TYPE), &done,
75          &if_receiverisincompatible);
76 
77   BIND(&if_receiverisincompatible);
78   {
79     // If Type(O) is not Object, or if O does not have a [[SyncIterator]]
80     // internal slot, then
81 
82     // Let badIteratorError be a new TypeError exception.
83     Node* const error =
84         MakeTypeError(MessageTemplate::kIncompatibleMethodReceiver, context,
85                       StringConstant(method_name), object);
86 
87     // Perform ! Call(promiseCapability.[[Reject]], undefined,
88     //                « badIteratorError »).
89     var_exception->Bind(error);
90     Goto(if_exception);
91   }
92 
93   BIND(&done);
94 }
95 
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)96 void AsyncFromSyncBuiltinsAssembler::Generate_AsyncFromSyncIteratorMethod(
97     Node* const context, Node* const iterator, Node* const sent_value,
98     const SyncIteratorNodeGenerator& get_method,
99     const UndefinedMethodHandler& if_method_undefined,
100     const char* operation_name, Label::Type reject_label_type,
101     Node* const initial_exception_value) {
102   Node* const native_context = LoadNativeContext(context);
103   Node* const promise = AllocateAndInitJSPromise(context);
104 
105   VARIABLE(var_exception, MachineRepresentation::kTagged,
106            initial_exception_value == nullptr ? UndefinedConstant()
107                                               : initial_exception_value);
108   Label reject_promise(this, reject_label_type);
109 
110   ThrowIfNotAsyncFromSyncIterator(context, iterator, &reject_promise,
111                                   &var_exception, operation_name);
112 
113   Node* const sync_iterator =
114       LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset);
115 
116   Node* const method = get_method(sync_iterator);
117 
118   if (if_method_undefined) {
119     Label if_isnotundefined(this);
120 
121     GotoIfNot(IsUndefined(method), &if_isnotundefined);
122     if_method_undefined(native_context, promise, &reject_promise);
123 
124     BIND(&if_isnotundefined);
125   }
126 
127   Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context,
128                                    method, sync_iterator, sent_value);
129   GotoIfException(iter_result, &reject_promise, &var_exception);
130 
131   Node* value;
132   Node* done;
133   std::tie(value, done) = LoadIteratorResult(
134       context, native_context, iter_result, &reject_promise, &var_exception);
135   Node* const wrapper = AllocateAndInitJSPromise(context);
136 
137   // Perform ! Call(valueWrapperCapability.[[Resolve]], undefined, «
138   // throwValue »).
139   CallBuiltin(Builtins::kResolvePromise, 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 throwDone.
144   Node* const on_fulfilled = CreateUnwrapClosure(native_context, done);
145 
146   // Perform ! PerformPromiseThen(valueWrapperCapability.[[Promise]],
147   //     onFulfilled, undefined, promiseCapability).
148   Return(CallBuiltin(Builtins::kPerformPromiseThen, context, wrapper,
149                      on_fulfilled, UndefinedConstant(), promise));
150 
151   BIND(&reject_promise);
152   {
153     Node* const exception = var_exception.value();
154     CallBuiltin(Builtins::kRejectPromise, context, promise, exception,
155                 TrueConstant());
156     Return(promise);
157   }
158 }
159 
LoadIteratorResult(Node * const context,Node * const native_context,Node * const iter_result,Label * if_exception,Variable * var_exception)160 std::pair<Node*, Node*> AsyncFromSyncBuiltinsAssembler::LoadIteratorResult(
161     Node* const context, Node* const native_context, Node* const iter_result,
162     Label* if_exception, Variable* var_exception) {
163   Label if_fastpath(this), if_slowpath(this), merge(this), to_boolean(this),
164       done(this), if_notanobject(this, Label::kDeferred);
165   GotoIf(TaggedIsSmi(iter_result), &if_notanobject);
166 
167   Node* const iter_result_map = LoadMap(iter_result);
168   GotoIfNot(IsJSReceiverMap(iter_result_map), &if_notanobject);
169 
170   Node* const fast_iter_result_map =
171       LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
172 
173   VARIABLE(var_value, MachineRepresentation::kTagged);
174   VARIABLE(var_done, MachineRepresentation::kTagged);
175   Branch(WordEqual(iter_result_map, fast_iter_result_map), &if_fastpath,
176          &if_slowpath);
177 
178   BIND(&if_fastpath);
179   {
180     var_done.Bind(LoadObjectField(iter_result, JSIteratorResult::kDoneOffset));
181     var_value.Bind(
182         LoadObjectField(iter_result, JSIteratorResult::kValueOffset));
183     Goto(&merge);
184   }
185 
186   BIND(&if_slowpath);
187   {
188     // Let nextDone be IteratorComplete(nextResult).
189     // IfAbruptRejectPromise(nextDone, promiseCapability).
190     Node* const done =
191         GetProperty(context, iter_result, factory()->done_string());
192     GotoIfException(done, if_exception, var_exception);
193 
194     // Let nextValue be IteratorValue(nextResult).
195     // IfAbruptRejectPromise(nextValue, promiseCapability).
196     Node* const value =
197         GetProperty(context, iter_result, factory()->value_string());
198     GotoIfException(value, if_exception, var_exception);
199 
200     var_value.Bind(value);
201     var_done.Bind(done);
202     Goto(&merge);
203   }
204 
205   BIND(&if_notanobject);
206   {
207     // Sync iterator result is not an object --- Produce a TypeError and jump
208     // to the `if_exception` path.
209     Node* const error = MakeTypeError(
210         MessageTemplate::kIteratorResultNotAnObject, context, iter_result);
211     var_exception->Bind(error);
212     Goto(if_exception);
213   }
214 
215   BIND(&merge);
216   // Ensure `iterResult.done` is a Boolean.
217   GotoIf(TaggedIsSmi(var_done.value()), &to_boolean);
218   Branch(IsBoolean(var_done.value()), &done, &to_boolean);
219 
220   BIND(&to_boolean);
221   {
222     Node* const result =
223         CallBuiltin(Builtins::kToBoolean, context, var_done.value());
224     var_done.Bind(result);
225     Goto(&done);
226   }
227 
228   BIND(&done);
229   return std::make_pair(var_value.value(), var_done.value());
230 }
231 
232 }  // namespace
233 
234 // https://tc39.github.io/proposal-async-iteration/
235 // Section #sec-%asyncfromsynciteratorprototype%.next
TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext,AsyncFromSyncBuiltinsAssembler)236 TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext, AsyncFromSyncBuiltinsAssembler) {
237   Node* const iterator = Parameter(Descriptor::kReceiver);
238   Node* const value = Parameter(Descriptor::kValue);
239   Node* const context = Parameter(Descriptor::kContext);
240 
241   auto get_method = [=](Node* const unused) {
242     return LoadObjectField(iterator, JSAsyncFromSyncIterator::kNextOffset);
243   };
244   Generate_AsyncFromSyncIteratorMethod(
245       context, iterator, value, get_method, UndefinedMethodHandler(),
246       "[Async-from-Sync Iterator].prototype.next");
247 }
248 
249 // https://tc39.github.io/proposal-async-iteration/
250 // Section #sec-%asyncfromsynciteratorprototype%.return
TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn,AsyncFromSyncBuiltinsAssembler)251 TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn,
252            AsyncFromSyncBuiltinsAssembler) {
253   Node* const iterator = Parameter(Descriptor::kReceiver);
254   Node* const value = Parameter(Descriptor::kValue);
255   Node* const context = Parameter(Descriptor::kContext);
256 
257   auto if_return_undefined = [=](Node* const native_context,
258                                  Node* const promise, Label* if_exception) {
259     // If return is undefined, then
260     // Let iterResult be ! CreateIterResultObject(value, true)
261     Node* const iter_result = CallBuiltin(Builtins::kCreateIterResultObject,
262                                           context, value, TrueConstant());
263 
264     // Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »).
265     // IfAbruptRejectPromise(nextDone, promiseCapability).
266     // Return promiseCapability.[[Promise]].
267     CallBuiltin(Builtins::kResolvePromise, context, promise, iter_result);
268     Return(promise);
269   };
270 
271   Generate_AsyncFromSyncIteratorMethod(
272       context, iterator, value, factory()->return_string(), if_return_undefined,
273       "[Async-from-Sync Iterator].prototype.return");
274 }
275 
276 // https://tc39.github.io/proposal-async-iteration/
277 // Section #sec-%asyncfromsynciteratorprototype%.throw
TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow,AsyncFromSyncBuiltinsAssembler)278 TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow,
279            AsyncFromSyncBuiltinsAssembler) {
280   Node* const iterator = Parameter(Descriptor::kReceiver);
281   Node* const reason = Parameter(Descriptor::kReason);
282   Node* const context = Parameter(Descriptor::kContext);
283 
284   auto if_throw_undefined = [=](Node* const native_context, Node* const promise,
285                                 Label* if_exception) { Goto(if_exception); };
286 
287   Generate_AsyncFromSyncIteratorMethod(
288       context, iterator, reason, factory()->throw_string(), if_throw_undefined,
289       "[Async-from-Sync Iterator].prototype.throw", Label::kNonDeferred,
290       reason);
291 }
292 
293 }  // namespace internal
294 }  // namespace v8
295