LCOV - code coverage report
Current view: top level - src/builtins - builtins-forin-gen.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 90 90 100.0 %
Date: 2017-04-26 Functions: 11 11 100.0 %

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

Generated by: LCOV version 1.10