LCOV - code coverage report
Current view: top level - src/ic - handler-configuration.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 117 126 92.9 %
Date: 2017-10-20 Functions: 10 10 100.0 %

          Line data    Source code
       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/ic/handler-configuration.h"
       6             : 
       7             : #include "src/code-stubs.h"
       8             : #include "src/ic/handler-configuration-inl.h"
       9             : #include "src/transitions.h"
      10             : 
      11             : namespace v8 {
      12             : namespace internal {
      13             : 
      14             : namespace {
      15             : 
      16             : template <bool fill_array = true>
      17      732135 : int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map,
      18             :                         Handle<JSReceiver> holder, Handle<Name> name,
      19             :                         Handle<FixedArray> array, int first_index) {
      20     1368432 :   if (!holder.is_null() && holder->map() == *receiver_map) return 0;
      21             : 
      22             :   HandleScope scope(isolate);
      23             :   int checks_count = 0;
      24             : 
      25      731671 :   if (receiver_map->IsPrimitiveMap() || receiver_map->IsJSGlobalProxyMap()) {
      26             :     // The validity cell check for primitive and global proxy receivers does
      27             :     // not guarantee that certain native context ever had access to other
      28             :     // native context. However, a handler created for one native context could
      29             :     // be used in other native context through the megamorphic stub cache.
      30             :     // So we record the original native context to which this handler
      31             :     // corresponds.
      32             :     if (fill_array) {
      33       39361 :       Handle<Context> native_context = isolate->native_context();
      34       39361 :       array->set(first_index + checks_count, native_context->self_weak_cell());
      35             :     }
      36             :     checks_count++;
      37             : 
      38      652949 :   } else if (receiver_map->IsJSGlobalObjectMap()) {
      39             :     // If we are creating a handler for [Load/Store]GlobalIC then we need to
      40             :     // check that the property did not appear in the global object.
      41             :     if (fill_array) {
      42        1586 :       Handle<JSGlobalObject> global = isolate->global_object();
      43             :       Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
      44        1586 :           global, name, PropertyCellType::kInvalidated);
      45             :       DCHECK(cell->value()->IsTheHole(isolate));
      46        1586 :       Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell);
      47        1586 :       array->set(first_index + checks_count, *weak_cell);
      48             :     }
      49             :     checks_count++;
      50             :   }
      51             : 
      52             :   // Create/count entries for each global or dictionary prototype appeared in
      53             :   // the prototype chain contains from receiver till holder.
      54      731671 :   PrototypeIterator::WhereToEnd end = name->IsPrivate()
      55             :                                           ? PrototypeIterator::END_AT_NON_HIDDEN
      56      731670 :                                           : PrototypeIterator::END_AT_NULL;
      57     2700745 :   for (PrototypeIterator iter(receiver_map, end); !iter.IsAtEnd();
      58     1237404 :        iter.Advance()) {
      59             :     Handle<JSReceiver> current =
      60             :         PrototypeIterator::GetCurrent<JSReceiver>(iter);
      61     1873227 :     if (holder.is_identical_to(current)) break;
      62             :     Handle<Map> current_map(current->map(), isolate);
      63             : 
      64     1237404 :     if (current_map->IsJSGlobalObjectMap()) {
      65             :       if (fill_array) {
      66         428 :         Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(current);
      67             :         Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
      68         428 :             global, name, PropertyCellType::kInvalidated);
      69             :         DCHECK(cell->value()->IsTheHole(isolate));
      70         428 :         Handle<WeakCell> weak_cell = isolate->factory()->NewWeakCell(cell);
      71         856 :         array->set(first_index + checks_count, *weak_cell);
      72             :       }
      73         856 :       checks_count++;
      74             : 
      75     1236548 :     } else if (current_map->is_dictionary_map()) {
      76             :       DCHECK(!current_map->IsJSGlobalProxyMap());  // Proxy maps are fast.
      77             :       if (fill_array) {
      78             :         DCHECK_EQ(NameDictionary::kNotFound,
      79             :                   current->property_dictionary()->FindEntry(name));
      80             :         Handle<WeakCell> weak_cell =
      81         292 :             Map::GetOrCreatePrototypeWeakCell(current, isolate);
      82         584 :         array->set(first_index + checks_count, *weak_cell);
      83             :       }
      84         584 :       checks_count++;
      85             :     }
      86             :   }
      87             :   return checks_count;
      88             : }
      89             : 
      90             : // Returns 0 if the validity cell check is enough to ensure that the
      91             : // prototype chain from |receiver_map| till |holder| did not change.
      92             : // If the |holder| is an empty handle then the full prototype chain is
      93             : // checked.
      94             : // Returns -1 if the handler has to be compiled or the number of prototype
      95             : // checks otherwise.
      96             : int GetPrototypeCheckCount(Isolate* isolate, Handle<Map> receiver_map,
      97             :                            Handle<JSReceiver> holder, Handle<Name> name) {
      98             :   return InitPrototypeChecks<false>(isolate, receiver_map, holder, name,
      99      691079 :                                     Handle<FixedArray>(), 0);
     100             : }
     101             : 
     102             : enum class HolderCellRequest {
     103             :   kGlobalPropertyCell,
     104             :   kHolder,
     105             : };
     106             : 
     107      385661 : Handle<WeakCell> HolderCell(Isolate* isolate, Handle<JSReceiver> holder,
     108             :                             Handle<Name> name, HolderCellRequest request) {
     109      385661 :   if (request == HolderCellRequest::kGlobalPropertyCell) {
     110             :     DCHECK(holder->IsJSGlobalObject());
     111             :     Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(holder);
     112             :     GlobalDictionary* dict = global->global_dictionary();
     113             :     int number = dict->FindEntry(name);
     114             :     DCHECK_NE(NameDictionary::kNotFound, number);
     115             :     Handle<PropertyCell> cell(dict->CellAt(number), isolate);
     116       12253 :     return isolate->factory()->NewWeakCell(cell);
     117             :   }
     118      373408 :   return Map::GetOrCreatePrototypeWeakCell(holder, isolate);
     119             : }
     120             : 
     121             : }  // namespace
     122             : 
     123             : // static
     124      385661 : Handle<Object> LoadHandler::LoadFromPrototype(Isolate* isolate,
     125             :                                               Handle<Map> receiver_map,
     126             :                                               Handle<JSReceiver> holder,
     127             :                                               Handle<Name> name,
     128             :                                               Handle<Smi> smi_handler) {
     129             :   int checks_count =
     130             :       GetPrototypeCheckCount(isolate, receiver_map, holder, name);
     131             :   DCHECK_LE(0, checks_count);
     132             : 
     133      740417 :   if (receiver_map->IsPrimitiveMap() ||
     134             :       receiver_map->is_access_check_needed()) {
     135             :     DCHECK(!receiver_map->is_dictionary_map());
     136             :     DCHECK_LE(1, checks_count);  // For native context.
     137       33823 :     smi_handler = EnableAccessCheckOnReceiver(isolate, smi_handler);
     138      351838 :   } else if (receiver_map->is_dictionary_map() &&
     139             :              !receiver_map->IsJSGlobalObjectMap()) {
     140         724 :     smi_handler = EnableLookupOnReceiver(isolate, smi_handler);
     141             :   }
     142             : 
     143             :   Handle<Cell> validity_cell =
     144      385661 :       Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
     145             :   DCHECK(!validity_cell.is_null());
     146             : 
     147             :   // LoadIC dispatcher expects PropertyCell as a "holder" in case of kGlobal
     148             :   // handler kind.
     149             :   HolderCellRequest request = GetHandlerKind(*smi_handler) == kGlobal
     150             :                                   ? HolderCellRequest::kGlobalPropertyCell
     151      385661 :                                   : HolderCellRequest::kHolder;
     152             : 
     153      385661 :   Handle<WeakCell> holder_cell = HolderCell(isolate, holder, name, request);
     154             : 
     155      385661 :   if (checks_count == 0) {
     156             :     return isolate->factory()->NewTuple3(holder_cell, smi_handler,
     157      351165 :                                          validity_cell, TENURED);
     158             :   }
     159             :   Handle<FixedArray> handler_array(isolate->factory()->NewFixedArray(
     160       34496 :       kFirstPrototypeIndex + checks_count, TENURED));
     161             :   handler_array->set(kSmiHandlerIndex, *smi_handler);
     162       34496 :   handler_array->set(kValidityCellIndex, *validity_cell);
     163       34496 :   handler_array->set(kHolderCellIndex, *holder_cell);
     164             :   InitPrototypeChecks(isolate, receiver_map, holder, name, handler_array,
     165       34496 :                       kFirstPrototypeIndex);
     166       34496 :   return handler_array;
     167             : }
     168             : 
     169             : // static
     170       81553 : Handle<Object> LoadHandler::LoadFullChain(Isolate* isolate,
     171             :                                           Handle<Map> receiver_map,
     172             :                                           Handle<Object> holder,
     173             :                                           Handle<Name> name,
     174             :                                           Handle<Smi> smi_handler) {
     175             :   Handle<JSReceiver> end;  // null handle
     176             :   int checks_count = GetPrototypeCheckCount(isolate, receiver_map, end, name);
     177             :   DCHECK_LE(0, checks_count);
     178             : 
     179      158926 :   if (receiver_map->IsPrimitiveMap() ||
     180             :       receiver_map->is_access_check_needed()) {
     181             :     DCHECK(!receiver_map->is_dictionary_map());
     182             :     DCHECK_LE(1, checks_count);  // For native context.
     183        4254 :     smi_handler = EnableAccessCheckOnReceiver(isolate, smi_handler);
     184       77299 :   } else if (receiver_map->is_dictionary_map() &&
     185             :              !receiver_map->IsJSGlobalObjectMap()) {
     186        4917 :     smi_handler = EnableLookupOnReceiver(isolate, smi_handler);
     187             :   }
     188             : 
     189             :   Handle<Object> validity_cell =
     190       81553 :       Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
     191       81553 :   if (validity_cell.is_null()) {
     192             :     DCHECK_EQ(0, checks_count);
     193             :     // Lookup on receiver isn't supported in case of a simple smi handler.
     194       11770 :     if (!LookupOnReceiverBits::decode(smi_handler->value())) return smi_handler;
     195             :     validity_cell = handle(Smi::kZero, isolate);
     196             :   }
     197             : 
     198             :   Factory* factory = isolate->factory();
     199       80378 :   if (checks_count == 0) {
     200       75192 :     return factory->NewTuple3(holder, smi_handler, validity_cell, TENURED);
     201             :   }
     202             :   Handle<FixedArray> handler_array(factory->NewFixedArray(
     203        5186 :       LoadHandler::kFirstPrototypeIndex + checks_count, TENURED));
     204             :   handler_array->set(kSmiHandlerIndex, *smi_handler);
     205        5186 :   handler_array->set(kValidityCellIndex, *validity_cell);
     206        5186 :   handler_array->set(kHolderCellIndex, *holder);
     207             :   InitPrototypeChecks(isolate, receiver_map, end, name, handler_array,
     208        5186 :                       kFirstPrototypeIndex);
     209        5186 :   return handler_array;
     210             : }
     211             : 
     212             : // static
     213        1911 : Handle<Object> StoreHandler::StoreElementTransition(
     214             :     Isolate* isolate, Handle<Map> receiver_map, Handle<Map> transition,
     215             :     KeyedAccessStoreMode store_mode) {
     216        1911 :   bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
     217             :   ElementsKind elements_kind = receiver_map->elements_kind();
     218             :   Handle<Code> stub = ElementsTransitionAndStoreStub(
     219             :                           isolate, elements_kind, transition->elements_kind(),
     220             :                           is_js_array, store_mode)
     221        3822 :                           .GetCode();
     222             :   Handle<Object> validity_cell =
     223        1911 :       Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
     224        1911 :   if (validity_cell.is_null()) {
     225             :     validity_cell = handle(Smi::kZero, isolate);
     226             :   }
     227        1911 :   Handle<WeakCell> cell = Map::WeakCellForMap(transition);
     228        1911 :   return isolate->factory()->NewTuple3(cell, stub, validity_cell, TENURED);
     229             : }
     230             : 
     231             : // static
     232      224173 : Handle<Object> StoreHandler::StoreTransition(Isolate* isolate,
     233             :                                              Handle<Map> receiver_map,
     234             :                                              Handle<JSObject> holder,
     235             :                                              Handle<HeapObject> transition,
     236             :                                              Handle<Name> name) {
     237             :   Handle<Smi> smi_handler;
     238             :   Handle<WeakCell> transition_cell;
     239             : 
     240      224173 :   if (transition->IsMap()) {
     241             :     Handle<Map> transition_map = Handle<Map>::cast(transition);
     242      222383 :     if (transition_map->is_dictionary_map()) {
     243       12461 :       smi_handler = StoreNormal(isolate);
     244             :     } else {
     245             :       int descriptor = transition_map->LastAdded();
     246             :       Handle<DescriptorArray> descriptors(
     247             :           transition_map->instance_descriptors());
     248      209922 :       PropertyDetails details = descriptors->GetDetails(descriptor);
     249             :       Representation representation = details.representation();
     250             :       DCHECK(!representation.IsNone());
     251             : 
     252             :       // Declarative handlers don't support access checks.
     253             :       DCHECK(!transition_map->is_access_check_needed());
     254             : 
     255             :       DCHECK_EQ(kData, details.kind());
     256      209922 :       if (details.location() == kDescriptor) {
     257         217 :         smi_handler = TransitionToConstant(isolate, descriptor);
     258             : 
     259             :       } else {
     260             :         DCHECK_EQ(kField, details.location());
     261             :         bool extend_storage = Map::cast(transition_map->GetBackPointer())
     262      419410 :                                   ->UnusedPropertyFields() == 0;
     263             : 
     264             :         FieldIndex index =
     265      209705 :             FieldIndex::ForDescriptor(*transition_map, descriptor);
     266             :         smi_handler = TransitionToField(isolate, descriptor, index,
     267             :                                         representation, extend_storage);
     268             :       }
     269             :     }
     270             :     // |holder| is either a receiver if the property is non-existent or
     271             :     // one of the prototypes.
     272             :     DCHECK(!holder.is_null());
     273      222383 :     bool is_nonexistent = holder->map() == transition_map->GetBackPointer();
     274      222383 :     if (is_nonexistent) holder = Handle<JSObject>::null();
     275      222383 :     transition_cell = Map::WeakCellForMap(transition_map);
     276             : 
     277             :   } else {
     278             :     DCHECK(transition->IsPropertyCell());
     279        1790 :     if (receiver_map->IsJSGlobalObjectMap()) {
     280             :       // TODO(ishell): this must be handled by StoreGlobalIC once it's finished.
     281             :       return StoreGlobal(isolate, Handle<PropertyCell>::cast(transition));
     282             :     } else {
     283             :       DCHECK(receiver_map->IsJSGlobalProxyMap());
     284        1264 :       smi_handler = StoreGlobalProxy(isolate);
     285        1264 :       transition_cell = isolate->factory()->NewWeakCell(transition);
     286             :     }
     287             :   }
     288             : 
     289             :   int checks_count =
     290             :       GetPrototypeCheckCount(isolate, receiver_map, holder, name);
     291             : 
     292             :   DCHECK_LE(0, checks_count);
     293             :   DCHECK(!receiver_map->IsJSGlobalObjectMap());
     294             : 
     295      223647 :   if (receiver_map->is_access_check_needed()) {
     296             :     DCHECK(!receiver_map->is_dictionary_map());
     297             :     DCHECK_LE(1, checks_count);  // For native context.
     298        1264 :     smi_handler = EnableAccessCheckOnReceiver(isolate, smi_handler);
     299             :   }
     300             : 
     301             :   Handle<Object> validity_cell =
     302      223647 :       Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
     303      223647 :   if (validity_cell.is_null()) {
     304             :     DCHECK_EQ(0, checks_count);
     305             :     validity_cell = handle(Smi::kZero, isolate);
     306             :   }
     307             : 
     308             :   Factory* factory = isolate->factory();
     309      223647 :   if (checks_count == 0) {
     310             :     return factory->NewTuple3(transition_cell, smi_handler, validity_cell,
     311      222383 :                               TENURED);
     312             :   }
     313             :   Handle<FixedArray> handler_array(
     314        1264 :       factory->NewFixedArray(kFirstPrototypeIndex + checks_count, TENURED));
     315             :   handler_array->set(kSmiHandlerIndex, *smi_handler);
     316        1264 :   handler_array->set(kValidityCellIndex, *validity_cell);
     317        1264 :   handler_array->set(kTransitionMapOrHolderCellIndex, *transition_cell);
     318             :   InitPrototypeChecks(isolate, receiver_map, holder, name, handler_array,
     319        1264 :                       kFirstPrototypeIndex);
     320        1264 :   return handler_array;
     321             : }
     322             : 
     323             : // static
     324       53153 : Handle<Object> StoreHandler::StoreGlobal(Isolate* isolate,
     325             :                                          Handle<PropertyCell> cell) {
     326       53679 :   return isolate->factory()->NewWeakCell(cell);
     327             : }
     328             : 
     329             : // static
     330         354 : Handle<Object> StoreHandler::StoreProxy(Isolate* isolate,
     331             :                                         Handle<Map> receiver_map,
     332             :                                         Handle<JSProxy> proxy,
     333             :                                         Handle<JSReceiver> receiver,
     334             :                                         Handle<Name> name) {
     335         354 :   Handle<Smi> smi_handler = StoreProxy(isolate);
     336             : 
     337         354 :   if (receiver.is_identical_to(proxy)) return smi_handler;
     338             : 
     339             :   int checks_count = GetPrototypeCheckCount(isolate, receiver_map, proxy, name);
     340             : 
     341             :   DCHECK_LE(0, checks_count);
     342             : 
     343         218 :   if (receiver_map->is_access_check_needed()) {
     344             :     DCHECK(!receiver_map->is_dictionary_map());
     345             :     DCHECK_LE(1, checks_count);  // For native context.
     346          20 :     smi_handler = EnableAccessCheckOnReceiver(isolate, smi_handler);
     347             :   }
     348             : 
     349             :   Handle<Object> validity_cell =
     350         218 :       Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
     351         218 :   if (validity_cell.is_null()) {
     352             :     DCHECK_EQ(0, checks_count);
     353             :     validity_cell = handle(Smi::kZero, isolate);
     354             :   }
     355             : 
     356             :   Factory* factory = isolate->factory();
     357         218 :   Handle<WeakCell> holder_cell = factory->NewWeakCell(proxy);
     358             : 
     359         218 :   if (checks_count == 0) {
     360         108 :     return factory->NewTuple3(holder_cell, smi_handler, validity_cell, TENURED);
     361             :   }
     362             :   Handle<FixedArray> handler_array(
     363         110 :       factory->NewFixedArray(kFirstPrototypeIndex + checks_count, TENURED));
     364             :   handler_array->set(kSmiHandlerIndex, *smi_handler);
     365         110 :   handler_array->set(kValidityCellIndex, *validity_cell);
     366         110 :   handler_array->set(kTransitionMapOrHolderCellIndex, *holder_cell);
     367             :   InitPrototypeChecks(isolate, receiver_map, proxy, name, handler_array,
     368         110 :                       kFirstPrototypeIndex);
     369         110 :   return handler_array;
     370             : }
     371             : 
     372       22814 : Object* StoreHandler::ValidHandlerOrNull(Object* raw_handler, Name* name,
     373             :                                          Handle<Map>* out_transition) {
     374             :   STATIC_ASSERT(kValidityCellOffset == Tuple3::kValue3Offset);
     375             : 
     376             :   Smi* valid = Smi::FromInt(Map::kPrototypeChainValid);
     377             : 
     378       22814 :   if (raw_handler->IsTuple3()) {
     379             :     // Check validity cell.
     380             :     Tuple3* handler = Tuple3::cast(raw_handler);
     381             : 
     382             :     Object* raw_validity_cell = handler->value3();
     383             :     // |raw_valitity_cell| can be Smi::kZero if no validity cell is required
     384             :     // (which counts as valid).
     385       45563 :     if (raw_validity_cell->IsCell() &&
     386             :         Cell::cast(raw_validity_cell)->value() != valid) {
     387             :       return nullptr;
     388             :     }
     389             : 
     390             :   } else {
     391             :     DCHECK(raw_handler->IsFixedArray());
     392             :     FixedArray* handler = FixedArray::cast(raw_handler);
     393             : 
     394             :     // Check validity cell.
     395             :     Object* value = Cell::cast(handler->get(kValidityCellIndex))->value();
     396           0 :     if (value != valid) return nullptr;
     397             : 
     398             :     // Check prototypes.
     399           0 :     Heap* heap = handler->GetHeap();
     400             :     Isolate* isolate = heap->isolate();
     401             :     Handle<Name> name_handle(name, isolate);
     402           0 :     for (int i = kFirstPrototypeIndex; i < handler->length(); i++) {
     403             :       // This mirrors AccessorAssembler::CheckPrototype.
     404             :       WeakCell* prototype_cell = WeakCell::cast(handler->get(i));
     405           0 :       if (prototype_cell->cleared()) return nullptr;
     406             :       HeapObject* maybe_prototype = HeapObject::cast(prototype_cell->value());
     407           0 :       if (maybe_prototype->IsPropertyCell()) {
     408             :         Object* value = PropertyCell::cast(maybe_prototype)->value();
     409           0 :         if (value != heap->the_hole_value()) return nullptr;
     410             :       } else {
     411             :         DCHECK(maybe_prototype->map()->is_dictionary_map());
     412             :         // Do a negative dictionary lookup.
     413             :         NameDictionary* dict =
     414             :             JSObject::cast(maybe_prototype)->property_dictionary();
     415           0 :         int number = dict->FindEntry(isolate, name_handle);
     416           0 :         if (number != NameDictionary::kNotFound) {
     417             :           PropertyDetails details = dict->DetailsAt(number);
     418           0 :           if (details.IsReadOnly() || details.kind() == kAccessor) {
     419             :             return nullptr;
     420             :           }
     421             :           break;
     422             :         }
     423             :       }
     424             :     }
     425             :   }
     426             : 
     427             :   // Check if the transition target is deprecated.
     428       12629 :   WeakCell* target_cell = GetTransitionCell(raw_handler);
     429             :   Map* transition = Map::cast(target_cell->value());
     430       12629 :   if (transition->is_deprecated()) return nullptr;
     431       12629 :   *out_transition = handle(transition);
     432       12629 :   return raw_handler;
     433             : }
     434             : 
     435             : }  // namespace internal
     436             : }  // namespace v8

Generated by: LCOV version 1.10