• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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