| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 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-forin.h" | |
| 6 | |
| 7 #include "src/builtins/builtins-utils.h" | |
| 8 #include "src/builtins/builtins.h" | |
| 9 #include "src/code-factory.h" | |
| 10 #include "src/code-stub-assembler.h" | |
| 11 #include "src/counters.h" | |
| 12 #include "src/keys.h" | |
| 13 #include "src/lookup.h" | |
| 14 #include "src/objects-inl.h" | |
| 15 #include "src/property-descriptor.h" | |
| 16 | |
| 17 namespace v8 { | |
| 18 namespace internal { | |
| 19 | |
| 20 typedef compiler::Node Node; | |
| 21 | |
| 22 Node* ForInBuiltinsAssembler::ForInFilter(Node* key, Node* object, | |
| 23 Node* context) { | |
| 24 Label return_undefined(this, Label::kDeferred), return_to_name(this), | |
| 25 end(this); | |
| 26 | |
| 27 Variable var_result(this, MachineRepresentation::kTagged); | |
| 28 | |
| 29 Node* has_property = | |
| 30 HasProperty(object, key, context, Runtime::kForInHasProperty); | |
| 31 | |
| 32 Branch(WordEqual(has_property, BooleanConstant(true)), &return_to_name, | |
| 33 &return_undefined); | |
| 34 | |
| 35 Bind(&return_to_name); | |
| 36 { | |
| 37 var_result.Bind(ToName(context, key)); | |
| 38 Goto(&end); | |
| 39 } | |
| 40 | |
| 41 Bind(&return_undefined); | |
| 42 { | |
| 43 var_result.Bind(UndefinedConstant()); | |
| 44 Goto(&end); | |
| 45 } | |
| 46 | |
| 47 Bind(&end); | |
| 48 return var_result.value(); | |
| 49 } | |
| 50 | |
| 51 std::tuple<Node*, Node*, Node*> ForInBuiltinsAssembler::EmitForInPrepare( | |
| 52 Node* object, Node* context, Label* call_runtime, | |
| 53 Label* nothing_to_iterate) { | |
| 54 Label use_cache(this); | |
| 55 CSA_ASSERT(this, IsJSReceiver(object)); | |
| 56 | |
| 57 CheckEnumCache(object, &use_cache, nothing_to_iterate, call_runtime); | |
| 58 | |
| 59 Bind(&use_cache); | |
| 60 Node* map = LoadMap(object); | |
| 61 Node* enum_length = EnumLength(map); | |
| 62 GotoIf(WordEqual(enum_length, SmiConstant(0)), nothing_to_iterate); | |
| 63 Node* descriptors = LoadMapDescriptors(map); | |
| 64 Node* cache_offset = | |
| 65 LoadObjectField(descriptors, DescriptorArray::kEnumCacheOffset); | |
| 66 Node* enum_cache = LoadObjectField( | |
| 67 cache_offset, DescriptorArray::kEnumCacheBridgeCacheOffset); | |
| 68 | |
| 69 return std::make_tuple(map, enum_cache, enum_length); | |
| 70 } | |
| 71 | |
| 72 Node* ForInBuiltinsAssembler::EnumLength(Node* map) { | |
| 73 CSA_ASSERT(this, IsMap(map)); | |
| 74 Node* bitfield_3 = LoadMapBitField3(map); | |
| 75 Node* enum_length = DecodeWordFromWord32<Map::EnumLengthBits>(bitfield_3); | |
| 76 return SmiTag(enum_length); | |
| 77 } | |
| 78 | |
| 79 void ForInBuiltinsAssembler::CheckPrototypeEnumCache(Node* receiver, Node* map, | |
| 80 Label* use_cache, | |
| 81 Label* use_runtime) { | |
| 82 Variable current_js_object(this, MachineRepresentation::kTagged, receiver); | |
| 83 Variable current_map(this, MachineRepresentation::kTagged, map); | |
| 84 | |
| 85 // These variables are updated in the loop below. | |
| 86 Variable* loop_vars[2] = {¤t_js_object, ¤t_map}; | |
| 87 Label loop(this, 2, loop_vars), next(this); | |
| 88 | |
| 89 Goto(&loop); | |
| 90 // Check that there are no elements. |current_js_object| contains | |
| 91 // the current JS object we've reached through the prototype chain. | |
| 92 Bind(&loop); | |
| 93 { | |
| 94 Label if_elements(this), if_no_elements(this); | |
| 95 Node* elements = LoadElements(current_js_object.value()); | |
| 96 Node* empty_fixed_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex); | |
| 97 // Check that there are no elements. | |
| 98 Branch(WordEqual(elements, empty_fixed_array), &if_no_elements, | |
| 99 &if_elements); | |
| 100 Bind(&if_elements); | |
| 101 { | |
| 102 // Second chance, the object may be using the empty slow element | |
| 103 // dictionary. | |
| 104 Node* slow_empty_dictionary = | |
| 105 LoadRoot(Heap::kEmptySlowElementDictionaryRootIndex); | |
| 106 Branch(WordNotEqual(elements, slow_empty_dictionary), use_runtime, | |
| 107 &if_no_elements); | |
| 108 } | |
| 109 | |
| 110 Bind(&if_no_elements); | |
| 111 { | |
| 112 // Update map prototype. | |
| 113 current_js_object.Bind(LoadMapPrototype(current_map.value())); | |
| 114 Branch(WordEqual(current_js_object.value(), NullConstant()), use_cache, | |
| 115 &next); | |
| 116 } | |
| 117 } | |
| 118 | |
| 119 Bind(&next); | |
| 120 { | |
| 121 // For all objects but the receiver, check that the cache is empty. | |
| 122 current_map.Bind(LoadMap(current_js_object.value())); | |
| 123 Node* enum_length = EnumLength(current_map.value()); | |
| 124 Node* zero_constant = SmiConstant(Smi::kZero); | |
| 125 Branch(WordEqual(enum_length, zero_constant), &loop, use_runtime); | |
| 126 } | |
| 127 } | |
| 128 | |
| 129 void ForInBuiltinsAssembler::CheckEnumCache(Node* receiver, Label* use_cache, | |
| 130 Label* nothing_to_iterate, | |
| 131 Label* use_runtime) { | |
| 132 Node* map = LoadMap(receiver); | |
| 133 | |
| 134 Label check_empty_prototype(this), | |
| 135 check_dict_receiver(this, Label::kDeferred); | |
| 136 | |
| 137 // Check if the enum length field is properly initialized, indicating that | |
| 138 // there is an enum cache. | |
| 139 { | |
| 140 Node* invalid_enum_cache_sentinel = | |
| 141 SmiConstant(Smi::FromInt(kInvalidEnumCacheSentinel)); | |
| 142 Node* enum_length = EnumLength(map); | |
| 143 Branch(WordEqual(enum_length, invalid_enum_cache_sentinel), | |
| 144 &check_dict_receiver, &check_empty_prototype); | |
| 145 } | |
| 146 | |
| 147 // Check that there are no elements on the fast |receiver| and its prototype | |
| 148 // chain. | |
| 149 Bind(&check_empty_prototype); | |
| 150 CheckPrototypeEnumCache(receiver, map, use_cache, use_runtime); | |
| 151 | |
| 152 Label dict_loop(this); | |
| 153 Bind(&check_dict_receiver); | |
| 154 { | |
| 155 // Avoid runtime-call for empty dictionary receivers. | |
| 156 GotoIfNot(IsDictionaryMap(map), use_runtime); | |
| 157 Node* properties = LoadProperties(receiver); | |
| 158 Node* length = LoadFixedArrayElement( | |
| 159 properties, NameDictionary::kNumberOfElementsIndex); | |
| 160 GotoIfNot(WordEqual(length, SmiConstant(0)), use_runtime); | |
| 161 // Check that there are no elements on the |receiver| and its prototype | |
| 162 // chain. Given that we do not create an EnumCache for dict-mode objects, | |
| 163 // directly jump to |nothing_to_iterate| if there are no elements and no | |
| 164 // properties on the |receiver|. | |
| 165 CheckPrototypeEnumCache(receiver, map, nothing_to_iterate, use_runtime); | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 TF_BUILTIN(ForInFilter, ForInBuiltinsAssembler) { | |
| 170 typedef ForInFilterDescriptor Descriptor; | |
| 171 | |
| 172 Node* key = Parameter(Descriptor::kKey); | |
| 173 Node* object = Parameter(Descriptor::kObject); | |
| 174 Node* context = Parameter(Descriptor::kContext); | |
| 175 | |
| 176 Return(ForInFilter(key, object, context)); | |
| 177 } | |
| 178 | |
| 179 TF_BUILTIN(ForInNext, ForInBuiltinsAssembler) { | |
| 180 typedef ForInNextDescriptor Descriptor; | |
| 181 | |
| 182 Label filter(this); | |
| 183 Node* object = Parameter(Descriptor::kObject); | |
| 184 Node* cache_array = Parameter(Descriptor::kCacheArray); | |
| 185 Node* cache_type = Parameter(Descriptor::kCacheType); | |
| 186 Node* index = Parameter(Descriptor::kIndex); | |
| 187 Node* context = Parameter(Descriptor::kContext); | |
| 188 | |
| 189 Node* key = LoadFixedArrayElement(cache_array, SmiUntag(index)); | |
| 190 Node* map = LoadMap(object); | |
| 191 GotoIfNot(WordEqual(map, cache_type), &filter); | |
| 192 Return(key); | |
| 193 Bind(&filter); | |
| 194 Return(ForInFilter(key, object, context)); | |
| 195 } | |
| 196 | |
| 197 TF_BUILTIN(ForInPrepare, ForInBuiltinsAssembler) { | |
| 198 typedef ForInPrepareDescriptor Descriptor; | |
| 199 | |
| 200 Label call_runtime(this), nothing_to_iterate(this); | |
| 201 Node* object = Parameter(Descriptor::kObject); | |
| 202 Node* context = Parameter(Descriptor::kContext); | |
| 203 | |
| 204 Node* cache_type; | |
| 205 Node* cache_array; | |
| 206 Node* cache_length; | |
| 207 std::tie(cache_type, cache_array, cache_length) = | |
| 208 EmitForInPrepare(object, context, &call_runtime, ¬hing_to_iterate); | |
| 209 | |
| 210 Return(cache_type, cache_array, cache_length); | |
| 211 | |
| 212 Bind(&call_runtime); | |
| 213 TailCallRuntime(Runtime::kForInPrepare, context, object); | |
| 214 | |
| 215 Bind(¬hing_to_iterate); | |
| 216 { | |
| 217 Node* zero = SmiConstant(0); | |
| 218 Return(zero, zero, zero); | |
| 219 } | |
| 220 } | |
| 221 } // namespace internal | |
| 222 } // namespace v8 | |
| OLD | NEW |