| 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 | 
|---|