V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
builtins-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-iterator-gen.h"
6 #include "src/builtins/growable-fixed-array-gen.h"
7 
8 #include "src/builtins/builtins-collections-gen.h"
9 #include "src/builtins/builtins-string-gen.h"
10 #include "src/builtins/builtins-utils-gen.h"
11 #include "src/builtins/builtins.h"
12 #include "src/code-stub-assembler.h"
13 #include "src/heap/factory-inl.h"
14 #include "torque-generated/builtins-base-from-dsl-gen.h"
15 
16 namespace v8 {
17 namespace internal {
18 
19 typedef IteratorBuiltinsFromDSLAssembler::IteratorRecord IteratorRecord;
20 
21 using compiler::Node;
22 
23 TNode<Object> IteratorBuiltinsAssembler::GetIteratorMethod(Node* context,
24  Node* object) {
25  return GetProperty(context, object, factory()->iterator_symbol());
26 }
27 
28 IteratorRecord IteratorBuiltinsAssembler::GetIterator(Node* context,
29  Node* object,
30  Label* if_exception,
31  Variable* exception) {
32  Node* method = GetIteratorMethod(context, object);
33  return GetIterator(context, object, method, if_exception, exception);
34 }
35 
36 IteratorRecord IteratorBuiltinsAssembler::GetIterator(Node* context,
37  Node* object,
38  Node* method,
39  Label* if_exception,
40  Variable* exception) {
41  GotoIfException(method, if_exception, exception);
42 
43  Label if_not_callable(this, Label::kDeferred), if_callable(this);
44  GotoIf(TaggedIsSmi(method), &if_not_callable);
45  Branch(IsCallable(method), &if_callable, &if_not_callable);
46 
47  BIND(&if_not_callable);
48  {
49  Node* ret = CallRuntime(Runtime::kThrowIteratorError, context, object);
50  GotoIfException(ret, if_exception, exception);
51  Unreachable();
52  }
53 
54  BIND(&if_callable);
55  {
56  Callable callable = CodeFactory::Call(isolate());
57  Node* iterator = CallJS(callable, context, method, object);
58  GotoIfException(iterator, if_exception, exception);
59 
60  Label get_next(this), if_notobject(this, Label::kDeferred);
61  GotoIf(TaggedIsSmi(iterator), &if_notobject);
62  Branch(IsJSReceiver(iterator), &get_next, &if_notobject);
63 
64  BIND(&if_notobject);
65  {
66  Node* ret = CallRuntime(Runtime::kThrowSymbolIteratorInvalid, context);
67  GotoIfException(ret, if_exception, exception);
68  Unreachable();
69  }
70 
71  BIND(&get_next);
72  Node* const next = GetProperty(context, iterator, factory()->next_string());
73  GotoIfException(next, if_exception, exception);
74 
75  return IteratorRecord{TNode<JSReceiver>::UncheckedCast(iterator),
76  TNode<Object>::UncheckedCast(next)};
77  }
78 }
79 
80 TNode<Object> IteratorBuiltinsAssembler::IteratorStep(
81  Node* context, const IteratorRecord& iterator, Label* if_done,
82  Node* fast_iterator_result_map, Label* if_exception, Variable* exception) {
83  DCHECK_NOT_NULL(if_done);
84  // 1. a. Let result be ? Invoke(iterator, "next", « »).
85  Callable callable = CodeFactory::Call(isolate());
86  Node* result = CallJS(callable, context, iterator.next, iterator.object);
87  GotoIfException(result, if_exception, exception);
88 
89  // 3. If Type(result) is not Object, throw a TypeError exception.
90  Label if_notobject(this, Label::kDeferred), return_result(this);
91  GotoIf(TaggedIsSmi(result), &if_notobject);
92  Node* result_map = LoadMap(result);
93 
94  if (fast_iterator_result_map != nullptr) {
95  // Fast iterator result case:
96  Label if_generic(this);
97 
98  // 4. Return result.
99  GotoIfNot(WordEqual(result_map, fast_iterator_result_map), &if_generic);
100 
101  // IteratorComplete
102  // 2. Return ToBoolean(? Get(iterResult, "done")).
103  Node* done = LoadObjectField(result, JSIteratorResult::kDoneOffset);
104  BranchIfToBooleanIsTrue(done, if_done, &return_result);
105 
106  BIND(&if_generic);
107  }
108 
109  // Generic iterator result case:
110  {
111  // 3. If Type(result) is not Object, throw a TypeError exception.
112  GotoIfNot(IsJSReceiverMap(result_map), &if_notobject);
113 
114  // IteratorComplete
115  // 2. Return ToBoolean(? Get(iterResult, "done")).
116  Node* done = GetProperty(context, result, factory()->done_string());
117  GotoIfException(done, if_exception, exception);
118  BranchIfToBooleanIsTrue(done, if_done, &return_result);
119  }
120 
121  BIND(&if_notobject);
122  {
123  Node* ret =
124  CallRuntime(Runtime::kThrowIteratorResultNotAnObject, context, result);
125  GotoIfException(ret, if_exception, exception);
126  Unreachable();
127  }
128 
129  BIND(&return_result);
130  return UncheckedCast<Object>(result);
131 }
132 
133 Node* IteratorBuiltinsAssembler::IteratorValue(Node* context, Node* result,
134  Node* fast_iterator_result_map,
135  Label* if_exception,
136  Variable* exception) {
137  CSA_ASSERT(this, IsJSReceiver(result));
138 
139  Label exit(this);
140  VARIABLE(var_value, MachineRepresentation::kTagged);
141  if (fast_iterator_result_map != nullptr) {
142  // Fast iterator result case:
143  Label if_generic(this);
144  Node* map = LoadMap(result);
145  GotoIfNot(WordEqual(map, fast_iterator_result_map), &if_generic);
146  var_value.Bind(LoadObjectField(result, JSIteratorResult::kValueOffset));
147  Goto(&exit);
148 
149  BIND(&if_generic);
150  }
151 
152  // Generic iterator result case:
153  {
154  Node* value = GetProperty(context, result, factory()->value_string());
155  GotoIfException(value, if_exception, exception);
156  var_value.Bind(value);
157  Goto(&exit);
158  }
159 
160  BIND(&exit);
161  return var_value.value();
162 }
163 
164 void IteratorBuiltinsAssembler::IteratorCloseOnException(
165  Node* context, const IteratorRecord& iterator, Label* if_exception,
166  Variable* exception) {
167  // Perform ES #sec-iteratorclose when an exception occurs. This simpler
168  // algorithm does not include redundant steps which are never reachable from
169  // the spec IteratorClose algorithm.
170  DCHECK((if_exception != nullptr && exception != nullptr));
171  CSA_ASSERT(this, IsNotTheHole(exception->value()));
172  CSA_ASSERT(this, IsJSReceiver(iterator.object));
173 
174  // Let return be ? GetMethod(iterator, "return").
175  Node* method =
176  GetProperty(context, iterator.object, factory()->return_string());
177  GotoIfException(method, if_exception, exception);
178 
179  // If return is undefined, return Completion(completion).
180  GotoIf(Word32Or(IsUndefined(method), IsNull(method)), if_exception);
181 
182  {
183  // Let innerResult be Call(return, iterator, « »).
184  // If an exception occurs, the original exception remains bound
185  Node* inner_result =
186  CallJS(CodeFactory::Call(isolate()), context, method, iterator.object);
187  GotoIfException(inner_result, if_exception, nullptr);
188 
189  // (If completion.[[Type]] is throw) return Completion(completion).
190  Goto(if_exception);
191  }
192 }
193 
194 void IteratorBuiltinsAssembler::IteratorCloseOnException(
195  Node* context, const IteratorRecord& iterator, TNode<Object> exception) {
196  Label rethrow(this, Label::kDeferred);
197  TVARIABLE(Object, exception_variable, exception);
198  IteratorCloseOnException(context, iterator, &rethrow, &exception_variable);
199 
200  BIND(&rethrow);
201  CallRuntime(Runtime::kReThrow, context, exception_variable.value());
202  Unreachable();
203 }
204 
205 TNode<JSArray> IteratorBuiltinsAssembler::IterableToList(
206  TNode<Context> context, TNode<Object> iterable, TNode<Object> iterator_fn) {
207  // 1. Let iteratorRecord be ? GetIterator(items, method).
208  IteratorRecord iterator_record = GetIterator(context, iterable, iterator_fn);
209 
210  // 2. Let values be a new empty List.
211  GrowableFixedArray values(state());
212 
213  Variable* vars[] = {values.var_array(), values.var_length(),
214  values.var_capacity()};
215  Label loop_start(this, 3, vars), done(this);
216  Goto(&loop_start);
217  // 3. Let next be true.
218  // 4. Repeat, while next is not false
219  BIND(&loop_start);
220  {
221  // a. Set next to ? IteratorStep(iteratorRecord).
222  TNode<Object> next = IteratorStep(context, iterator_record, &done);
223  // b. If next is not false, then
224  // i. Let nextValue be ? IteratorValue(next).
225  TNode<Object> next_value = CAST(IteratorValue(context, next));
226  // ii. Append nextValue to the end of the List values.
227  values.Push(next_value);
228  Goto(&loop_start);
229  }
230 
231  BIND(&done);
232  return values.ToJSArray(context);
233 }
234 
235 TF_BUILTIN(IterableToList, IteratorBuiltinsAssembler) {
236  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
237  TNode<Object> iterable = CAST(Parameter(Descriptor::kIterable));
238  TNode<Object> iterator_fn = CAST(Parameter(Descriptor::kIteratorFn));
239 
240  Return(IterableToList(context, iterable, iterator_fn));
241 }
242 
243 // This builtin always returns a new JSArray and is thus safe to use even in the
244 // presence of code that may call back into user-JS. This builtin will take the
245 // fast path if the iterable is a fast array and the Array prototype and the
246 // Symbol.iterator is untouched. The fast path skips the iterator and copies the
247 // backing store to the new array. Note that if the array has holes, the holes
248 // will be copied to the new array, which is inconsistent with the behavior of
249 // an actual iteration, where holes should be replaced with undefined (if the
250 // prototype has no elements). To maintain the correct behavior for holey
251 // arrays, use the builtins IterableToList or IterableToListWithSymbolLookup.
252 TF_BUILTIN(IterableToListMayPreserveHoles, IteratorBuiltinsAssembler) {
253  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
254  TNode<Object> iterable = CAST(Parameter(Descriptor::kIterable));
255  TNode<Object> iterator_fn = CAST(Parameter(Descriptor::kIteratorFn));
256 
257  Label slow_path(this);
258 
259  GotoIfNot(IsFastJSArrayWithNoCustomIteration(context, iterable), &slow_path);
260 
261  // The fast path will copy holes to the new array.
262  TailCallBuiltin(Builtins::kCloneFastJSArray, context, iterable);
263 
264  BIND(&slow_path);
265  TailCallBuiltin(Builtins::kIterableToList, context, iterable, iterator_fn);
266 }
267 
268 void IteratorBuiltinsAssembler::FastIterableToList(
269  TNode<Context> context, TNode<Object> iterable,
270  TVariable<Object>* var_result, Label* slow) {
271  Label done(this), check_string(this), check_map(this), check_set(this);
272 
273  GotoIfNot(IsFastJSArrayWithNoCustomIteration(context, iterable),
274  &check_string);
275 
276  // Fast path for fast JSArray.
277  *var_result =
278  CallBuiltin(Builtins::kCloneFastJSArrayFillingHoles, context, iterable);
279  Goto(&done);
280 
281  BIND(&check_string);
282  {
283  Label string_maybe_fast_call(this);
284  StringBuiltinsAssembler string_assembler(state());
285  string_assembler.BranchIfStringPrimitiveWithNoCustomIteration(
286  iterable, context, &string_maybe_fast_call, &check_map);
287 
288  BIND(&string_maybe_fast_call);
289  TNode<IntPtrT> const length = LoadStringLengthAsWord(CAST(iterable));
290  // Use string length as conservative approximation of number of codepoints.
291  GotoIf(
292  IntPtrGreaterThan(length, IntPtrConstant(JSArray::kMaxFastArrayLength)),
293  slow);
294  *var_result = CallBuiltin(Builtins::kStringToList, context, iterable);
295  Goto(&done);
296  }
297 
298  BIND(&check_map);
299  {
300  Label map_fast_call(this);
301  BranchIfIterableWithOriginalKeyOrValueMapIterator(
302  state(), iterable, context, &map_fast_call, &check_set);
303 
304  BIND(&map_fast_call);
305  *var_result = CallBuiltin(Builtins::kMapIteratorToList, context, iterable);
306  Goto(&done);
307  }
308 
309  BIND(&check_set);
310  {
311  Label set_fast_call(this);
312  BranchIfIterableWithOriginalValueSetIterator(state(), iterable, context,
313  &set_fast_call, slow);
314 
315  BIND(&set_fast_call);
316  *var_result =
317  CallBuiltin(Builtins::kSetOrSetIteratorToList, context, iterable);
318  Goto(&done);
319  }
320 
321  BIND(&done);
322 }
323 
324 // This builtin loads the property Symbol.iterator as the iterator, and has fast
325 // paths for fast arrays, for primitive strings, for sets and set iterators, and
326 // for map iterators. These fast paths will only be taken if Symbol.iterator and
327 // the Iterator prototype are not modified in a way that changes the original
328 // iteration behavior.
329 // * In case of fast holey arrays, holes will be converted to undefined to
330 // reflect iteration semantics. Note that replacement by undefined is only
331 // correct when the NoElements protector is valid.
332 // * In case of map/set iterators, there is an additional requirement that the
333 // iterator is not partially consumed. To be spec-compliant, after spreading
334 // the iterator is set to be exhausted.
335 TF_BUILTIN(IterableToListWithSymbolLookup, IteratorBuiltinsAssembler) {
336  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
337  TNode<Object> iterable = CAST(Parameter(Descriptor::kIterable));
338 
339  Label slow_path(this);
340 
341  GotoIfForceSlowPath(&slow_path);
342 
343  TVARIABLE(Object, var_result);
344  FastIterableToList(context, iterable, &var_result, &slow_path);
345  Return(var_result.value());
346 
347  BIND(&slow_path);
348  {
349  TNode<Object> iterator_fn = GetIteratorMethod(context, iterable);
350  TailCallBuiltin(Builtins::kIterableToList, context, iterable, iterator_fn);
351  }
352 }
353 
354 } // namespace internal
355 } // namespace v8
Definition: libplatform.h:13