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