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 15 TNode<Object> IteratorBuiltinsAssembler::GetIteratorMethod(Node* context, 16 Node* object) { 17 return GetProperty(context, object, factory()->iterator_symbol()); 18 } 19 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 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 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 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 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 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 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 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