V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
builtins-async-iterator-gen.cc
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:
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 
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  }
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 
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 
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::
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 }
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
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 
336 // https://tc39.github.io/proposal-async-iteration/
337 // Section #sec-%asyncfromsynciteratorprototype%.return
338 TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn,
339  AsyncFromSyncBuiltinsAssembler) {
340  Node* const iterator = Parameter(Descriptor::kReceiver);
341  Node* const value = Parameter(Descriptor::kValue);
342  Node* const context = Parameter(Descriptor::kContext);
343 
344  auto if_return_undefined = [=](Node* const native_context,
345  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  context, value, TrueConstant());
350 
351  // Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »).
352  // IfAbruptRejectPromise(nextDone, promiseCapability).
353  // Return promiseCapability.[[Promise]].
354  CallBuiltin(Builtins::kResolvePromise, context, promise, iter_result);
355  Return(promise);
356  };
357 
358  Generate_AsyncFromSyncIteratorMethod(
359  context, iterator, value, factory()->return_string(), if_return_undefined,
360  "[Async-from-Sync Iterator].prototype.return");
361 }
362 
363 // https://tc39.github.io/proposal-async-iteration/
364 // Section #sec-%asyncfromsynciteratorprototype%.throw
365 TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow,
366  AsyncFromSyncBuiltinsAssembler) {
367  Node* const iterator = Parameter(Descriptor::kReceiver);
368  Node* const reason = Parameter(Descriptor::kReason);
369  Node* const context = Parameter(Descriptor::kContext);
370 
371  auto if_throw_undefined = [=](Node* const native_context, Node* const promise,
372  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  reason);
378 }
379 
380 } // namespace internal
381 } // namespace v8
Definition: libplatform.h:13