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/heap/factory-inl.h"
9
10 namespace v8 {
11 namespace internal {
12
13 using compiler::Node;
14
GetIteratorMethod(Node * context,Node * object)15 TNode<Object> IteratorBuiltinsAssembler::GetIteratorMethod(Node* context,
16 Node* object) {
17 return GetProperty(context, object, factory()->iterator_symbol());
18 }
19
GetIterator(Node * context,Node * object,Label * if_exception,Variable * exception)20 IteratorRecord IteratorBuiltinsAssembler::GetIterator(Node* context,
21 Node* object,
22 Label* if_exception,
23 Variable* exception) {
24 Node* method = GetIteratorMethod(context, object);
25 return GetIterator(context, object, method, if_exception, exception);
26 }
27
GetIterator(Node * context,Node * object,Node * method,Label * if_exception,Variable * exception)28 IteratorRecord IteratorBuiltinsAssembler::GetIterator(Node* context,
29 Node* object,
30 Node* method,
31 Label* if_exception,
32 Variable* exception) {
33 GotoIfException(method, if_exception, exception);
34
35 Label if_not_callable(this, Label::kDeferred), if_callable(this);
36 GotoIf(TaggedIsSmi(method), &if_not_callable);
37 Branch(IsCallable(method), &if_callable, &if_not_callable);
38
39 BIND(&if_not_callable);
40 {
41 Node* ret = CallRuntime(Runtime::kThrowTypeError, context,
42 SmiConstant(MessageTemplate::kNotIterable), object);
43 GotoIfException(ret, if_exception, exception);
44 Unreachable();
45 }
46
47 BIND(&if_callable);
48 {
49 Callable callable = CodeFactory::Call(isolate());
50 Node* iterator = CallJS(callable, context, method, object);
51 GotoIfException(iterator, if_exception, exception);
52
53 Label get_next(this), if_notobject(this, Label::kDeferred);
54 GotoIf(TaggedIsSmi(iterator), &if_notobject);
55 Branch(IsJSReceiver(iterator), &get_next, &if_notobject);
56
57 BIND(&if_notobject);
58 {
59 Node* ret = CallRuntime(Runtime::kThrowSymbolIteratorInvalid, context);
60 GotoIfException(ret, if_exception, exception);
61 Unreachable();
62 }
63
64 BIND(&get_next);
65 Node* const next = GetProperty(context, iterator, factory()->next_string());
66 GotoIfException(next, if_exception, exception);
67
68 return IteratorRecord{TNode<JSReceiver>::UncheckedCast(iterator),
69 TNode<Object>::UncheckedCast(next)};
70 }
71 }
72
IteratorStep(Node * context,const IteratorRecord & iterator,Label * if_done,Node * fast_iterator_result_map,Label * if_exception,Variable * exception)73 Node* IteratorBuiltinsAssembler::IteratorStep(
74 Node* context, const IteratorRecord& iterator, Label* if_done,
75 Node* fast_iterator_result_map, Label* if_exception, Variable* exception) {
76 DCHECK_NOT_NULL(if_done);
77 // 1. a. Let result be ? Invoke(iterator, "next", « »).
78 Callable callable = CodeFactory::Call(isolate());
79 Node* result = CallJS(callable, context, iterator.next, iterator.object);
80 GotoIfException(result, if_exception, exception);
81
82 // 3. If Type(result) is not Object, throw a TypeError exception.
83 Label if_notobject(this, Label::kDeferred), return_result(this);
84 GotoIf(TaggedIsSmi(result), &if_notobject);
85 Node* result_map = LoadMap(result);
86
87 if (fast_iterator_result_map != nullptr) {
88 // Fast iterator result case:
89 Label if_generic(this);
90
91 // 4. Return result.
92 GotoIfNot(WordEqual(result_map, fast_iterator_result_map), &if_generic);
93
94 // IteratorComplete
95 // 2. Return ToBoolean(? Get(iterResult, "done")).
96 Node* done = LoadObjectField(result, JSIteratorResult::kDoneOffset);
97 BranchIfToBooleanIsTrue(done, if_done, &return_result);
98
99 BIND(&if_generic);
100 }
101
102 // Generic iterator result case:
103 {
104 // 3. If Type(result) is not Object, throw a TypeError exception.
105 GotoIfNot(IsJSReceiverMap(result_map), &if_notobject);
106
107 // IteratorComplete
108 // 2. Return ToBoolean(? Get(iterResult, "done")).
109 Node* done = GetProperty(context, result, factory()->done_string());
110 GotoIfException(done, if_exception, exception);
111 BranchIfToBooleanIsTrue(done, if_done, &return_result);
112 }
113
114 BIND(&if_notobject);
115 {
116 Node* ret =
117 CallRuntime(Runtime::kThrowIteratorResultNotAnObject, context, result);
118 GotoIfException(ret, if_exception, exception);
119 Unreachable();
120 }
121
122 BIND(&return_result);
123 return result;
124 }
125
IteratorValue(Node * context,Node * result,Node * fast_iterator_result_map,Label * if_exception,Variable * exception)126 Node* IteratorBuiltinsAssembler::IteratorValue(Node* context, Node* result,
127 Node* fast_iterator_result_map,
128 Label* if_exception,
129 Variable* exception) {
130 CSA_ASSERT(this, IsJSReceiver(result));
131
132 Label exit(this);
133 VARIABLE(var_value, MachineRepresentation::kTagged);
134 if (fast_iterator_result_map != nullptr) {
135 // Fast iterator result case:
136 Label if_generic(this);
137 Node* map = LoadMap(result);
138 GotoIfNot(WordEqual(map, fast_iterator_result_map), &if_generic);
139 var_value.Bind(LoadObjectField(result, JSIteratorResult::kValueOffset));
140 Goto(&exit);
141
142 BIND(&if_generic);
143 }
144
145 // Generic iterator result case:
146 {
147 Node* value = GetProperty(context, result, factory()->value_string());
148 GotoIfException(value, if_exception, exception);
149 var_value.Bind(value);
150 Goto(&exit);
151 }
152
153 BIND(&exit);
154 return var_value.value();
155 }
156
IteratorCloseOnException(Node * context,const IteratorRecord & iterator,Label * if_exception,Variable * exception)157 void IteratorBuiltinsAssembler::IteratorCloseOnException(
158 Node* context, const IteratorRecord& iterator, Label* if_exception,
159 Variable* exception) {
160 // Perform ES #sec-iteratorclose when an exception occurs. This simpler
161 // algorithm does not include redundant steps which are never reachable from
162 // the spec IteratorClose algorithm.
163 DCHECK_NOT_NULL(if_exception);
164 DCHECK_NOT_NULL(exception);
165 CSA_ASSERT(this, IsNotTheHole(exception->value()));
166 CSA_ASSERT(this, IsJSReceiver(iterator.object));
167
168 // Let return be ? GetMethod(iterator, "return").
169 Node* method =
170 GetProperty(context, iterator.object, factory()->return_string());
171 GotoIfException(method, if_exception, exception);
172
173 // If return is undefined, return Completion(completion).
174 GotoIf(Word32Or(IsUndefined(method), IsNull(method)), if_exception);
175
176 {
177 // Let innerResult be Call(return, iterator, « »).
178 // If an exception occurs, the original exception remains bound
179 Node* inner_result =
180 CallJS(CodeFactory::Call(isolate()), context, method, iterator.object);
181 GotoIfException(inner_result, if_exception, nullptr);
182
183 // (If completion.[[Type]] is throw) return Completion(completion).
184 Goto(if_exception);
185 }
186 }
187
IteratorCloseOnException(Node * context,const IteratorRecord & iterator,Variable * exception)188 void IteratorBuiltinsAssembler::IteratorCloseOnException(
189 Node* context, const IteratorRecord& iterator, Variable* exception) {
190 Label rethrow(this, Label::kDeferred);
191 IteratorCloseOnException(context, iterator, &rethrow, exception);
192
193 BIND(&rethrow);
194 CallRuntime(Runtime::kReThrow, context, exception->value());
195 Unreachable();
196 }
197
IterableToList(TNode<Context> context,TNode<Object> iterable,TNode<Object> iterator_fn)198 TNode<JSArray> IteratorBuiltinsAssembler::IterableToList(
199 TNode<Context> context, TNode<Object> iterable, TNode<Object> iterator_fn) {
200 Label fast_path(this), slow_path(this), done(this);
201
202 TVARIABLE(JSArray, created_list);
203
204 Branch(IsFastJSArrayWithNoCustomIteration(iterable, context), &fast_path,
205 &slow_path);
206
207 // This is a fast-path for ignoring the iterator.
208 BIND(&fast_path);
209 {
210 TNode<JSArray> input_array = CAST(iterable);
211 created_list = CAST(CloneFastJSArray(context, input_array));
212 Goto(&done);
213 }
214
215 BIND(&slow_path);
216 {
217 // 1. Let iteratorRecord be ? GetIterator(items, method).
218 IteratorRecord iterator_record =
219 GetIterator(context, iterable, iterator_fn);
220
221 // 2. Let values be a new empty List.
222 GrowableFixedArray values(state());
223
224 Variable* vars[] = {values.var_array(), values.var_length(),
225 values.var_capacity()};
226 Label loop_start(this, 3, vars), loop_end(this);
227 Goto(&loop_start);
228 // 3. Let next be true.
229 // 4. Repeat, while next is not false
230 BIND(&loop_start);
231 {
232 // a. Set next to ? IteratorStep(iteratorRecord).
233 TNode<Object> next =
234 CAST(IteratorStep(context, iterator_record, &loop_end));
235 // b. If next is not false, then
236 // i. Let nextValue be ? IteratorValue(next).
237 TNode<Object> next_value = CAST(IteratorValue(context, next));
238 // ii. Append nextValue to the end of the List values.
239 values.Push(next_value);
240 Goto(&loop_start);
241 }
242 BIND(&loop_end);
243
244 created_list = values.ToJSArray(context);
245 Goto(&done);
246 }
247
248 BIND(&done);
249 return created_list.value();
250 }
251
IterableToList(TNode<Context> context,TNode<Object> iterable)252 TNode<JSArray> IteratorBuiltinsAssembler::IterableToList(
253 TNode<Context> context, TNode<Object> iterable) {
254 TNode<Object> method = GetIteratorMethod(context, iterable);
255 return IterableToList(context, iterable, method);
256 }
257
258 } // namespace internal
259 } // namespace v8
260