Line data Source code
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 168 : class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler {
19 : public:
20 168 : explicit AsyncFromSyncBuiltinsAssembler(compiler::CodeAssemblerState* state)
21 168 : : 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 :
47 112 : 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 112 : auto get_method = [=](Node* const sync_iterator) {
54 224 : return GetProperty(context, sync_iterator, name);
55 336 : };
56 : return Generate_AsyncFromSyncIteratorMethod(
57 : context, iterator, sent_value, get_method, if_method_undefined,
58 112 : operation_name, reject_label_type, initial_exception_value);
59 : }
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 0 : auto get_method = [=](Node* const sync_iterator) {
67 0 : return GetProperty(context, sync_iterator, name);
68 0 : };
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 :
88 168 : void AsyncFromSyncBuiltinsAssembler::ThrowIfNotAsyncFromSyncIterator(
89 : Node* const context, Node* const object, Label* if_exception,
90 : Variable* var_exception, const char* method_name) {
91 336 : Label if_receiverisincompatible(this, Label::kDeferred), done(this);
92 :
93 168 : GotoIf(TaggedIsSmi(object), &if_receiverisincompatible);
94 336 : Branch(HasInstanceType(object, JS_ASYNC_FROM_SYNC_ITERATOR_TYPE), &done,
95 168 : &if_receiverisincompatible);
96 :
97 168 : 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 168 : StringConstant(method_name), object);
106 :
107 : // Perform ! Call(promiseCapability.[[Reject]], undefined,
108 : // « badIteratorError »).
109 168 : var_exception->Bind(error);
110 168 : Goto(if_exception);
111 : }
112 :
113 336 : BIND(&done);
114 168 : }
115 :
116 168 : 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 168 : Node* const native_context = LoadNativeContext(context);
123 168 : Node* const promise = AllocateAndInitJSPromise(context);
124 :
125 168 : VARIABLE(var_exception, MachineRepresentation::kTagged,
126 : initial_exception_value == nullptr ? UndefinedConstant()
127 : : initial_exception_value);
128 336 : Label reject_promise(this, reject_label_type);
129 :
130 : ThrowIfNotAsyncFromSyncIterator(context, iterator, &reject_promise,
131 168 : &var_exception, operation_name);
132 :
133 : Node* const sync_iterator =
134 168 : LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset);
135 :
136 168 : Node* const method = get_method(sync_iterator);
137 :
138 168 : if (if_method_undefined) {
139 112 : Label if_isnotundefined(this);
140 :
141 112 : GotoIfNot(IsUndefined(method), &if_isnotundefined);
142 112 : if_method_undefined(native_context, promise, &reject_promise);
143 :
144 112 : BIND(&if_isnotundefined);
145 : }
146 :
147 : Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context,
148 168 : method, sync_iterator, sent_value);
149 168 : GotoIfException(iter_result, &reject_promise, &var_exception);
150 :
151 : Node* value;
152 : Node* done;
153 336 : std::tie(value, done) = LoadIteratorResult(
154 168 : context, native_context, iter_result, &reject_promise, &var_exception);
155 168 : Node* const wrapper = AllocateAndInitJSPromise(context);
156 :
157 : // Perform ! Call(valueWrapperCapability.[[Resolve]], undefined, «
158 : // throwValue »).
159 168 : 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 168 : 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 168 : on_fulfilled, UndefinedConstant(), promise));
170 :
171 168 : BIND(&reject_promise);
172 : {
173 168 : Node* const exception = var_exception.value();
174 : CallBuiltin(Builtins::kRejectPromise, context, promise, exception,
175 168 : TrueConstant());
176 168 : Return(promise);
177 168 : }
178 168 : }
179 :
180 0 : void AsyncFromSyncBuiltinsAssembler::
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 0 : Node* const native_context = LoadNativeContext(context);
188 0 : Node* const promise = AllocateAndInitJSPromise(context);
189 :
190 0 : VARIABLE(var_exception, MachineRepresentation::kTagged,
191 : initial_exception_value == nullptr ? UndefinedConstant()
192 : : initial_exception_value);
193 0 : Label reject_promise(this, reject_label_type);
194 :
195 : ThrowIfNotAsyncFromSyncIterator(context, iterator, &reject_promise,
196 0 : &var_exception, operation_name);
197 :
198 : Node* const sync_iterator =
199 0 : LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset);
200 :
201 0 : Node* const method = get_method(sync_iterator);
202 :
203 0 : if (if_method_undefined) {
204 0 : Label if_isnotundefined(this);
205 :
206 0 : GotoIfNot(IsUndefined(method), &if_isnotundefined);
207 0 : if_method_undefined(native_context, promise, &reject_promise);
208 :
209 0 : BIND(&if_isnotundefined);
210 : }
211 :
212 : Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context,
213 0 : method, sync_iterator, sent_value);
214 0 : GotoIfException(iter_result, &reject_promise, &var_exception);
215 :
216 : Node* value;
217 : Node* done;
218 0 : std::tie(value, done) = LoadIteratorResult(
219 0 : context, native_context, iter_result, &reject_promise, &var_exception);
220 :
221 : Node* const promise_fun =
222 0 : 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 0 : 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 0 : 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 0 : on_fulfilled, UndefinedConstant(), promise));
238 :
239 0 : BIND(&reject_promise);
240 : {
241 0 : Node* const exception = var_exception.value();
242 : CallBuiltin(Builtins::kRejectPromise, context, promise, exception,
243 0 : TrueConstant());
244 0 : Return(promise);
245 0 : }
246 0 : }
247 168 : 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 336 : Label if_fastpath(this), if_slowpath(this), merge(this), to_boolean(this),
251 336 : done(this), if_notanobject(this, Label::kDeferred);
252 168 : GotoIf(TaggedIsSmi(iter_result), &if_notanobject);
253 :
254 168 : Node* const iter_result_map = LoadMap(iter_result);
255 168 : GotoIfNot(IsJSReceiverMap(iter_result_map), &if_notanobject);
256 :
257 : Node* const fast_iter_result_map =
258 168 : LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
259 :
260 336 : VARIABLE(var_value, MachineRepresentation::kTagged);
261 336 : VARIABLE(var_done, MachineRepresentation::kTagged);
262 336 : Branch(WordEqual(iter_result_map, fast_iter_result_map), &if_fastpath,
263 168 : &if_slowpath);
264 :
265 168 : BIND(&if_fastpath);
266 : {
267 168 : var_done.Bind(LoadObjectField(iter_result, JSIteratorResult::kDoneOffset));
268 : var_value.Bind(
269 168 : LoadObjectField(iter_result, JSIteratorResult::kValueOffset));
270 168 : Goto(&merge);
271 : }
272 :
273 168 : BIND(&if_slowpath);
274 : {
275 : // Let nextDone be IteratorComplete(nextResult).
276 : // IfAbruptRejectPromise(nextDone, promiseCapability).
277 : Node* const done =
278 336 : GetProperty(context, iter_result, factory()->done_string());
279 168 : GotoIfException(done, if_exception, var_exception);
280 :
281 : // Let nextValue be IteratorValue(nextResult).
282 : // IfAbruptRejectPromise(nextValue, promiseCapability).
283 : Node* const value =
284 336 : GetProperty(context, iter_result, factory()->value_string());
285 168 : GotoIfException(value, if_exception, var_exception);
286 :
287 168 : var_value.Bind(value);
288 168 : var_done.Bind(done);
289 168 : Goto(&merge);
290 : }
291 :
292 168 : 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 168 : MessageTemplate::kIteratorResultNotAnObject, context, iter_result);
298 168 : var_exception->Bind(error);
299 168 : Goto(if_exception);
300 : }
301 :
302 168 : BIND(&merge);
303 : // Ensure `iterResult.done` is a Boolean.
304 168 : GotoIf(TaggedIsSmi(var_done.value()), &to_boolean);
305 168 : Branch(IsBoolean(var_done.value()), &done, &to_boolean);
306 :
307 168 : BIND(&to_boolean);
308 : {
309 : Node* const result =
310 168 : CallBuiltin(Builtins::kToBoolean, context, var_done.value());
311 168 : var_done.Bind(result);
312 168 : Goto(&done);
313 : }
314 :
315 168 : BIND(&done);
316 336 : 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
323 392 : TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext, AsyncFromSyncBuiltinsAssembler) {
324 56 : Node* const iterator = Parameter(Descriptor::kReceiver);
325 56 : Node* const value = Parameter(Descriptor::kValue);
326 56 : Node* const context = Parameter(Descriptor::kContext);
327 :
328 56 : auto get_method = [=](Node* const unused) {
329 112 : return LoadObjectField(iterator, JSAsyncFromSyncIterator::kNextOffset);
330 168 : };
331 : Generate_AsyncFromSyncIteratorMethod(
332 : context, iterator, value, get_method, UndefinedMethodHandler(),
333 56 : "[Async-from-Sync Iterator].prototype.next");
334 56 : }
335 :
336 : // https://tc39.github.io/proposal-async-iteration/
337 : // Section #sec-%asyncfromsynciteratorprototype%.return
338 392 : TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn,
339 : AsyncFromSyncBuiltinsAssembler) {
340 56 : Node* const iterator = Parameter(Descriptor::kReceiver);
341 56 : Node* const value = Parameter(Descriptor::kValue);
342 56 : Node* const context = Parameter(Descriptor::kContext);
343 :
344 : auto if_return_undefined = [=](Node* const native_context,
345 56 : Node* const promise, Label* if_exception) {
346 : // If return is undefined, then
347 : // Let iterResult be ! CreateIterResultObject(value, true)
348 : Node* const iter_result = CallBuiltin(Builtins::kCreateIterResultObject,
349 56 : context, value, TrueConstant());
350 :
351 : // Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »).
352 : // IfAbruptRejectPromise(nextDone, promiseCapability).
353 : // Return promiseCapability.[[Promise]].
354 56 : CallBuiltin(Builtins::kResolvePromise, context, promise, iter_result);
355 56 : Return(promise);
356 112 : };
357 :
358 : Generate_AsyncFromSyncIteratorMethod(
359 : context, iterator, value, factory()->return_string(), if_return_undefined,
360 56 : "[Async-from-Sync Iterator].prototype.return");
361 56 : }
362 :
363 : // https://tc39.github.io/proposal-async-iteration/
364 : // Section #sec-%asyncfromsynciteratorprototype%.throw
365 392 : TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow,
366 : AsyncFromSyncBuiltinsAssembler) {
367 56 : Node* const iterator = Parameter(Descriptor::kReceiver);
368 56 : Node* const reason = Parameter(Descriptor::kReason);
369 56 : Node* const context = Parameter(Descriptor::kContext);
370 :
371 : auto if_throw_undefined = [=](Node* const native_context, Node* const promise,
372 112 : Label* if_exception) { Goto(if_exception); };
373 :
374 : Generate_AsyncFromSyncIteratorMethod(
375 : context, iterator, reason, factory()->throw_string(), if_throw_undefined,
376 : "[Async-from-Sync Iterator].prototype.throw", Label::kNonDeferred,
377 56 : reason);
378 56 : }
379 :
380 : } // namespace internal
381 86739 : } // namespace v8
|