Home | History | Annotate | Download | only in src
      1 // Copyright 2014 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/lookup.h"
      6 
      7 #include "src/bootstrapper.h"
      8 #include "src/deoptimizer.h"
      9 #include "src/elements.h"
     10 #include "src/field-type.h"
     11 #include "src/isolate-inl.h"
     12 
     13 namespace v8 {
     14 namespace internal {
     15 
     16 // static
     17 LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate,
     18                                                  Handle<Object> receiver,
     19                                                  Handle<Object> key,
     20                                                  bool* success,
     21                                                  Configuration configuration) {
     22   uint32_t index = 0;
     23   if (key->ToArrayIndex(&index)) {
     24     *success = true;
     25     return LookupIterator(isolate, receiver, index, configuration);
     26   }
     27 
     28   Handle<Name> name;
     29   *success = Object::ToName(isolate, key).ToHandle(&name);
     30   if (!*success) {
     31     DCHECK(isolate->has_pending_exception());
     32     // Return an unusable dummy.
     33     return LookupIterator(receiver, isolate->factory()->empty_string());
     34   }
     35 
     36   if (name->AsArrayIndex(&index)) {
     37     LookupIterator it(isolate, receiver, index, configuration);
     38     // Here we try to avoid having to rebuild the string later
     39     // by storing it on the indexed LookupIterator.
     40     it.name_ = name;
     41     return it;
     42   }
     43 
     44   return LookupIterator(receiver, name, configuration);
     45 }
     46 
     47 template <bool is_element>
     48 void LookupIterator::Start() {
     49   DisallowHeapAllocation no_gc;
     50 
     51   has_property_ = false;
     52   state_ = NOT_FOUND;
     53   holder_ = initial_holder_;
     54 
     55   JSReceiver* holder = *holder_;
     56   Map* map = holder->map();
     57 
     58   state_ = LookupInHolder<is_element>(map, holder);
     59   if (IsFound()) return;
     60 
     61   NextInternal<is_element>(map, holder);
     62 }
     63 
     64 template void LookupIterator::Start<true>();
     65 template void LookupIterator::Start<false>();
     66 
     67 void LookupIterator::Next() {
     68   DCHECK_NE(JSPROXY, state_);
     69   DCHECK_NE(TRANSITION, state_);
     70   DisallowHeapAllocation no_gc;
     71   has_property_ = false;
     72 
     73   JSReceiver* holder = *holder_;
     74   Map* map = holder->map();
     75 
     76   if (map->IsSpecialReceiverMap()) {
     77     state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
     78                          : LookupInSpecialHolder<false>(map, holder);
     79     if (IsFound()) return;
     80   }
     81 
     82   IsElement() ? NextInternal<true>(map, holder)
     83               : NextInternal<false>(map, holder);
     84 }
     85 
     86 template <bool is_element>
     87 void LookupIterator::NextInternal(Map* map, JSReceiver* holder) {
     88   do {
     89     JSReceiver* maybe_holder = NextHolder(map);
     90     if (maybe_holder == nullptr) {
     91       if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
     92         RestartLookupForNonMaskingInterceptors<is_element>();
     93         return;
     94       }
     95       state_ = NOT_FOUND;
     96       if (holder != *holder_) holder_ = handle(holder, isolate_);
     97       return;
     98     }
     99     holder = maybe_holder;
    100     map = holder->map();
    101     state_ = LookupInHolder<is_element>(map, holder);
    102   } while (!IsFound());
    103 
    104   holder_ = handle(holder, isolate_);
    105 }
    106 
    107 template <bool is_element>
    108 void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
    109   interceptor_state_ = interceptor_state;
    110   property_details_ = PropertyDetails::Empty();
    111   number_ = DescriptorArray::kNotFound;
    112   Start<is_element>();
    113 }
    114 
    115 template void LookupIterator::RestartInternal<true>(InterceptorState);
    116 template void LookupIterator::RestartInternal<false>(InterceptorState);
    117 
    118 // static
    119 Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
    120     Isolate* isolate, Handle<Object> receiver, uint32_t index) {
    121   // Strings are the only objects with properties (only elements) directly on
    122   // the wrapper. Hence we can skip generating the wrapper for all other cases.
    123   if (index != kMaxUInt32 && receiver->IsString() &&
    124       index < static_cast<uint32_t>(String::cast(*receiver)->length())) {
    125     // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
    126     // context, ensuring that we don't leak it into JS?
    127     Handle<JSFunction> constructor = isolate->string_function();
    128     Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
    129     Handle<JSValue>::cast(result)->set_value(*receiver);
    130     return result;
    131   }
    132   auto root =
    133       handle(receiver->GetPrototypeChainRootMap(isolate)->prototype(), isolate);
    134   if (root->IsNull(isolate)) {
    135     unsigned int magic = 0xbbbbbbbb;
    136     isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
    137   }
    138   return Handle<JSReceiver>::cast(root);
    139 }
    140 
    141 
    142 Handle<Map> LookupIterator::GetReceiverMap() const {
    143   if (receiver_->IsNumber()) return factory()->heap_number_map();
    144   return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
    145 }
    146 
    147 bool LookupIterator::HasAccess() const {
    148   DCHECK_EQ(ACCESS_CHECK, state_);
    149   return isolate_->MayAccess(handle(isolate_->context()),
    150                              GetHolder<JSObject>());
    151 }
    152 
    153 template <bool is_element>
    154 void LookupIterator::ReloadPropertyInformation() {
    155   state_ = BEFORE_PROPERTY;
    156   interceptor_state_ = InterceptorState::kUninitialized;
    157   state_ = LookupInHolder<is_element>(holder_->map(), *holder_);
    158   DCHECK(IsFound() || !holder_->HasFastProperties());
    159 }
    160 
    161 void LookupIterator::InternalUpdateProtector() {
    162   if (isolate_->bootstrapper()->IsActive()) return;
    163 
    164   if (*name_ == heap()->constructor_string()) {
    165     if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
    166     // Setting the constructor property could change an instance's @@species
    167     if (holder_->IsJSArray()) {
    168       isolate_->CountUsage(
    169           v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
    170       isolate_->InvalidateArraySpeciesProtector();
    171     } else if (holder_->map()->is_prototype_map()) {
    172       DisallowHeapAllocation no_gc;
    173       // Setting the constructor of Array.prototype of any realm also needs
    174       // to invalidate the species protector
    175       if (isolate_->IsInAnyContext(*holder_,
    176                                    Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
    177         isolate_->CountUsage(v8::Isolate::UseCounterFeature::
    178                                  kArrayPrototypeConstructorModified);
    179         isolate_->InvalidateArraySpeciesProtector();
    180       }
    181     }
    182   } else if (*name_ == heap()->species_symbol()) {
    183     if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
    184     // Setting the Symbol.species property of any Array constructor invalidates
    185     // the species protector
    186     if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) {
    187       isolate_->CountUsage(
    188           v8::Isolate::UseCounterFeature::kArraySpeciesModified);
    189       isolate_->InvalidateArraySpeciesProtector();
    190     }
    191   } else if (*name_ == heap()->is_concat_spreadable_symbol()) {
    192     if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return;
    193     isolate_->InvalidateIsConcatSpreadableProtector();
    194   } else if (*name_ == heap()->iterator_symbol()) {
    195     if (!isolate_->IsArrayIteratorLookupChainIntact()) return;
    196     if (holder_->IsJSArray()) {
    197       isolate_->InvalidateArrayIteratorProtector();
    198     }
    199   }
    200 }
    201 
    202 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
    203   DCHECK(state_ == DATA || state_ == ACCESSOR);
    204   DCHECK(HolderIsReceiverOrHiddenPrototype());
    205 
    206   Handle<JSObject> holder = GetHolder<JSObject>();
    207 
    208   if (IsElement()) {
    209     ElementsKind kind = holder->GetElementsKind();
    210     ElementsKind to = value->OptimalElementsKind();
    211     if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
    212     to = GetMoreGeneralElementsKind(kind, to);
    213 
    214     if (kind != to) {
    215       JSObject::TransitionElementsKind(holder, to);
    216     }
    217 
    218     // Copy the backing store if it is copy-on-write.
    219     if (IsFastSmiOrObjectElementsKind(to)) {
    220       JSObject::EnsureWritableFastElements(holder);
    221     }
    222     return;
    223   }
    224 
    225   if (holder->IsJSGlobalObject()) {
    226     Handle<GlobalDictionary> dictionary(holder->global_dictionary());
    227     Handle<PropertyCell> cell(
    228         PropertyCell::cast(dictionary->ValueAt(dictionary_entry())));
    229     DCHECK(!cell->IsTheHole(isolate_));
    230     property_details_ = cell->property_details();
    231     PropertyCell::PrepareForValue(dictionary, dictionary_entry(), value,
    232                                   property_details_);
    233     return;
    234   }
    235   if (!holder->HasFastProperties()) return;
    236 
    237   PropertyConstness new_constness = kConst;
    238   if (FLAG_track_constant_fields) {
    239     if (constness() == kConst) {
    240       DCHECK_EQ(kData, property_details_.kind());
    241       // Check that current value matches new value otherwise we should make
    242       // the property mutable.
    243       if (!IsConstFieldValueEqualTo(*value)) new_constness = kMutable;
    244     }
    245   } else {
    246     new_constness = kMutable;
    247   }
    248 
    249   Handle<Map> old_map(holder->map(), isolate_);
    250   Handle<Map> new_map = Map::PrepareForDataProperty(
    251       old_map, descriptor_number(), new_constness, value);
    252 
    253   if (old_map.is_identical_to(new_map)) {
    254     // Update the property details if the representation was None.
    255     if (representation().IsNone()) {
    256       property_details_ =
    257           new_map->instance_descriptors()->GetDetails(descriptor_number());
    258     }
    259     return;
    260   }
    261 
    262   JSObject::MigrateToMap(holder, new_map);
    263   ReloadPropertyInformation<false>();
    264 }
    265 
    266 
    267 void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
    268                                              PropertyAttributes attributes) {
    269   DCHECK(state_ == DATA || state_ == ACCESSOR);
    270   DCHECK(HolderIsReceiverOrHiddenPrototype());
    271   Handle<JSObject> holder = GetHolder<JSObject>();
    272   if (IsElement()) {
    273     DCHECK(!holder->HasFixedTypedArrayElements());
    274     DCHECK(attributes != NONE || !holder->HasFastElements());
    275     Handle<FixedArrayBase> elements(holder->elements());
    276     holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
    277                                                attributes);
    278     ReloadPropertyInformation<true>();
    279   } else if (holder->HasFastProperties()) {
    280     Handle<Map> old_map(holder->map(), isolate_);
    281     Handle<Map> new_map = Map::ReconfigureExistingProperty(
    282         old_map, descriptor_number(), i::kData, attributes);
    283     // Force mutable to avoid changing constant value by reconfiguring
    284     // kData -> kAccessor -> kData.
    285     new_map = Map::PrepareForDataProperty(new_map, descriptor_number(),
    286                                           kMutable, value);
    287     JSObject::MigrateToMap(holder, new_map);
    288     ReloadPropertyInformation<false>();
    289   } else {
    290     PropertyDetails details(kData, attributes, 0, PropertyCellType::kMutable);
    291     if (holder->IsJSGlobalObject()) {
    292       Handle<GlobalDictionary> dictionary(holder->global_dictionary());
    293 
    294       Handle<PropertyCell> cell = PropertyCell::PrepareForValue(
    295           dictionary, dictionary_entry(), value, details);
    296       cell->set_value(*value);
    297       property_details_ = cell->property_details();
    298     } else {
    299       Handle<NameDictionary> dictionary(holder->property_dictionary());
    300       PropertyDetails original_details =
    301           dictionary->DetailsAt(dictionary_entry());
    302       int enumeration_index = original_details.dictionary_index();
    303       DCHECK(enumeration_index > 0);
    304       details = details.set_index(enumeration_index);
    305       dictionary->SetEntry(dictionary_entry(), name(), value, details);
    306       property_details_ = details;
    307     }
    308     state_ = DATA;
    309   }
    310 
    311   WriteDataValue(value, true);
    312 
    313 #if VERIFY_HEAP
    314   if (FLAG_verify_heap) {
    315     holder->JSObjectVerify();
    316   }
    317 #endif
    318 }
    319 
    320 // Can only be called when the receiver is a JSObject. JSProxy has to be handled
    321 // via a trap. Adding properties to primitive values is not observable.
    322 void LookupIterator::PrepareTransitionToDataProperty(
    323     Handle<JSObject> receiver, Handle<Object> value,
    324     PropertyAttributes attributes, Object::StoreFromKeyed store_mode) {
    325   DCHECK(receiver.is_identical_to(GetStoreTarget()));
    326   if (state_ == TRANSITION) return;
    327 
    328   if (!IsElement() && name()->IsPrivate()) {
    329     attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
    330   }
    331 
    332   DCHECK(state_ != LookupIterator::ACCESSOR ||
    333          (GetAccessors()->IsAccessorInfo() &&
    334           AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
    335   DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
    336   DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
    337 
    338   Handle<Map> map(receiver->map(), isolate_);
    339 
    340   // Dictionary maps can always have additional data properties.
    341   if (map->is_dictionary_map()) {
    342     state_ = TRANSITION;
    343     if (map->IsJSGlobalObjectMap()) {
    344       // Install a property cell.
    345       Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver);
    346       int entry;
    347       Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
    348           global, name(), PropertyCellType::kUninitialized, &entry);
    349       Handle<GlobalDictionary> dictionary(global->global_dictionary(),
    350                                           isolate_);
    351       DCHECK(cell->value()->IsTheHole(isolate_));
    352       DCHECK(!value->IsTheHole(isolate_));
    353       transition_ = cell;
    354       // Assign an enumeration index to the property and update
    355       // SetNextEnumerationIndex.
    356       int index = dictionary->NextEnumerationIndex();
    357       dictionary->SetNextEnumerationIndex(index + 1);
    358       property_details_ = PropertyDetails(kData, attributes, index,
    359                                           PropertyCellType::kUninitialized);
    360       PropertyCellType new_type =
    361           PropertyCell::UpdatedType(cell, value, property_details_);
    362       property_details_ = property_details_.set_cell_type(new_type);
    363       cell->set_property_details(property_details_);
    364       number_ = entry;
    365       has_property_ = true;
    366     } else {
    367       // Don't set enumeration index (it will be set during value store).
    368       property_details_ =
    369           PropertyDetails(kData, attributes, 0, PropertyCellType::kNoCell);
    370       transition_ = map;
    371     }
    372     return;
    373   }
    374 
    375   Handle<Map> transition = Map::TransitionToDataProperty(
    376       map, name_, value, attributes, kDefaultFieldConstness, store_mode);
    377   state_ = TRANSITION;
    378   transition_ = transition;
    379 
    380   if (transition->is_dictionary_map()) {
    381     // Don't set enumeration index (it will be set during value store).
    382     property_details_ =
    383         PropertyDetails(kData, attributes, 0, PropertyCellType::kNoCell);
    384   } else {
    385     property_details_ = transition->GetLastDescriptorDetails();
    386     has_property_ = true;
    387   }
    388 }
    389 
    390 void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) {
    391   DCHECK_EQ(TRANSITION, state_);
    392 
    393   DCHECK(receiver.is_identical_to(GetStoreTarget()));
    394   holder_ = receiver;
    395   if (receiver->IsJSGlobalObject()) {
    396     state_ = DATA;
    397     return;
    398   }
    399   Handle<Map> transition = transition_map();
    400   bool simple_transition = transition->GetBackPointer() == receiver->map();
    401   JSObject::MigrateToMap(receiver, transition);
    402 
    403   if (simple_transition) {
    404     int number = transition->LastAdded();
    405     number_ = static_cast<uint32_t>(number);
    406     property_details_ = transition->GetLastDescriptorDetails();
    407     state_ = DATA;
    408   } else if (receiver->map()->is_dictionary_map()) {
    409     Handle<NameDictionary> dictionary(receiver->property_dictionary(),
    410                                       isolate_);
    411     int entry;
    412     dictionary = NameDictionary::Add(dictionary, name(),
    413                                      isolate_->factory()->uninitialized_value(),
    414                                      property_details_, &entry);
    415     receiver->set_properties(*dictionary);
    416     // Reload details containing proper enumeration index value.
    417     property_details_ = dictionary->DetailsAt(entry);
    418     number_ = entry;
    419     has_property_ = true;
    420     state_ = DATA;
    421 
    422   } else {
    423     ReloadPropertyInformation<false>();
    424   }
    425 }
    426 
    427 
    428 void LookupIterator::Delete() {
    429   Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
    430   if (IsElement()) {
    431     Handle<JSObject> object = Handle<JSObject>::cast(holder);
    432     ElementsAccessor* accessor = object->GetElementsAccessor();
    433     accessor->Delete(object, number_);
    434   } else {
    435     bool is_prototype_map = holder->map()->is_prototype_map();
    436     RuntimeCallTimerScope stats_scope(
    437         isolate_, is_prototype_map
    438                       ? &RuntimeCallStats::PrototypeObject_DeleteProperty
    439                       : &RuntimeCallStats::Object_DeleteProperty);
    440 
    441     PropertyNormalizationMode mode =
    442         is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
    443 
    444     if (holder->HasFastProperties()) {
    445       JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
    446                                     "DeletingProperty");
    447       ReloadPropertyInformation<false>();
    448     }
    449     // TODO(verwaest): Get rid of the name_ argument.
    450     JSReceiver::DeleteNormalizedProperty(holder, name_, number_);
    451     if (holder->IsJSObject()) {
    452       JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
    453     }
    454   }
    455   state_ = NOT_FOUND;
    456 }
    457 
    458 void LookupIterator::TransitionToAccessorProperty(
    459     Handle<Object> getter, Handle<Object> setter,
    460     PropertyAttributes attributes) {
    461   DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
    462   // Can only be called when the receiver is a JSObject. JSProxy has to be
    463   // handled via a trap. Adding properties to primitive values is not
    464   // observable.
    465   Handle<JSObject> receiver = GetStoreTarget();
    466   if (!IsElement() && name()->IsPrivate()) {
    467     attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
    468   }
    469 
    470   if (!IsElement() && !receiver->map()->is_dictionary_map()) {
    471     Handle<Map> old_map(receiver->map(), isolate_);
    472 
    473     if (!holder_.is_identical_to(receiver)) {
    474       holder_ = receiver;
    475       state_ = NOT_FOUND;
    476     } else if (state_ == INTERCEPTOR) {
    477       LookupInRegularHolder<false>(*old_map, *holder_);
    478     }
    479     int descriptor =
    480         IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound;
    481 
    482     Handle<Map> new_map = Map::TransitionToAccessorProperty(
    483         isolate_, old_map, name_, descriptor, getter, setter, attributes);
    484     bool simple_transition = new_map->GetBackPointer() == receiver->map();
    485     JSObject::MigrateToMap(receiver, new_map);
    486 
    487     if (simple_transition) {
    488       int number = new_map->LastAdded();
    489       number_ = static_cast<uint32_t>(number);
    490       property_details_ = new_map->GetLastDescriptorDetails();
    491       state_ = ACCESSOR;
    492       return;
    493     }
    494 
    495     ReloadPropertyInformation<false>();
    496     if (!new_map->is_dictionary_map()) return;
    497   }
    498 
    499   Handle<AccessorPair> pair;
    500   if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
    501     pair = Handle<AccessorPair>::cast(GetAccessors());
    502     // If the component and attributes are identical, nothing has to be done.
    503     if (pair->Equals(*getter, *setter)) {
    504       if (property_details().attributes() == attributes) {
    505         if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver);
    506         return;
    507       }
    508     } else {
    509       pair = AccessorPair::Copy(pair);
    510       pair->SetComponents(*getter, *setter);
    511     }
    512   } else {
    513     pair = factory()->NewAccessorPair();
    514     pair->SetComponents(*getter, *setter);
    515   }
    516 
    517   TransitionToAccessorPair(pair, attributes);
    518 
    519 #if VERIFY_HEAP
    520   if (FLAG_verify_heap) {
    521     receiver->JSObjectVerify();
    522   }
    523 #endif
    524 }
    525 
    526 
    527 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
    528                                               PropertyAttributes attributes) {
    529   Handle<JSObject> receiver = GetStoreTarget();
    530   holder_ = receiver;
    531 
    532   PropertyDetails details(kAccessor, attributes, 0, PropertyCellType::kMutable);
    533 
    534   if (IsElement()) {
    535     // TODO(verwaest): Move code into the element accessor.
    536     Handle<SeededNumberDictionary> dictionary =
    537         JSObject::NormalizeElements(receiver);
    538 
    539     dictionary = SeededNumberDictionary::Set(dictionary, index_, pair, details,
    540                                              receiver);
    541     receiver->RequireSlowElements(*dictionary);
    542 
    543     if (receiver->HasSlowArgumentsElements()) {
    544       FixedArray* parameter_map = FixedArray::cast(receiver->elements());
    545       uint32_t length = parameter_map->length() - 2;
    546       if (number_ < length) {
    547         parameter_map->set(number_ + 2, heap()->the_hole_value());
    548       }
    549       FixedArray::cast(receiver->elements())->set(1, *dictionary);
    550     } else {
    551       receiver->set_elements(*dictionary);
    552     }
    553 
    554     ReloadPropertyInformation<true>();
    555   } else {
    556     PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
    557                                          ? KEEP_INOBJECT_PROPERTIES
    558                                          : CLEAR_INOBJECT_PROPERTIES;
    559     // Normalize object to make this operation simple.
    560     JSObject::NormalizeProperties(receiver, mode, 0,
    561                                   "TransitionToAccessorPair");
    562 
    563     JSObject::SetNormalizedProperty(receiver, name_, pair, details);
    564     JSObject::ReoptimizeIfPrototype(receiver);
    565 
    566     ReloadPropertyInformation<false>();
    567   }
    568 }
    569 
    570 
    571 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
    572   DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
    573   // Optimization that only works if configuration_ is not mutable.
    574   if (!check_prototype_chain()) return true;
    575   DisallowHeapAllocation no_gc;
    576   if (*receiver_ == *holder_) return true;
    577   if (!receiver_->IsJSReceiver()) return false;
    578   JSReceiver* current = JSReceiver::cast(*receiver_);
    579   JSReceiver* object = *holder_;
    580   if (!current->map()->has_hidden_prototype()) return false;
    581   // JSProxy do not occur as hidden prototypes.
    582   if (object->IsJSProxy()) return false;
    583   PrototypeIterator iter(isolate(), current, kStartAtPrototype,
    584                          PrototypeIterator::END_AT_NON_HIDDEN);
    585   while (!iter.IsAtEnd()) {
    586     if (iter.GetCurrent<JSReceiver>() == object) return true;
    587     iter.Advance();
    588   }
    589   return false;
    590 }
    591 
    592 
    593 Handle<Object> LookupIterator::FetchValue() const {
    594   Object* result = NULL;
    595   if (IsElement()) {
    596     Handle<JSObject> holder = GetHolder<JSObject>();
    597     ElementsAccessor* accessor = holder->GetElementsAccessor();
    598     return accessor->Get(holder, number_);
    599   } else if (holder_->IsJSGlobalObject()) {
    600     Handle<JSObject> holder = GetHolder<JSObject>();
    601     result = holder->global_dictionary()->ValueAt(number_);
    602     DCHECK(result->IsPropertyCell());
    603     result = PropertyCell::cast(result)->value();
    604   } else if (!holder_->HasFastProperties()) {
    605     result = holder_->property_dictionary()->ValueAt(number_);
    606   } else if (property_details_.location() == kField) {
    607     DCHECK_EQ(kData, property_details_.kind());
    608     Handle<JSObject> holder = GetHolder<JSObject>();
    609     FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
    610     return JSObject::FastPropertyAt(holder, property_details_.representation(),
    611                                     field_index);
    612   } else {
    613     result = holder_->map()->instance_descriptors()->GetValue(number_);
    614   }
    615   return handle(result, isolate_);
    616 }
    617 
    618 bool LookupIterator::IsConstFieldValueEqualTo(Object* value) const {
    619   DCHECK(!IsElement());
    620   DCHECK(holder_->HasFastProperties());
    621   DCHECK_EQ(kField, property_details_.location());
    622   DCHECK_EQ(kConst, property_details_.constness());
    623   Handle<JSObject> holder = GetHolder<JSObject>();
    624   FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
    625   if (property_details_.representation().IsDouble()) {
    626     if (!value->IsNumber()) return false;
    627     uint64_t bits;
    628     if (holder->IsUnboxedDoubleField(field_index)) {
    629       bits = holder->RawFastDoublePropertyAsBitsAt(field_index);
    630     } else {
    631       Object* current_value = holder->RawFastPropertyAt(field_index);
    632       DCHECK(current_value->IsMutableHeapNumber());
    633       bits = HeapNumber::cast(current_value)->value_as_bits();
    634     }
    635     // Use bit representation of double to to check for hole double, since
    636     // manipulating the signaling NaN used for the hole in C++, e.g. with
    637     // bit_cast or value(), will change its value on ia32 (the x87 stack is
    638     // used to return values and stores to the stack silently clear the
    639     // signalling bit).
    640     if (bits == kHoleNanInt64) {
    641       // Uninitialized double field.
    642       return true;
    643     }
    644     return bit_cast<double>(bits) == value->Number();
    645   } else {
    646     Object* current_value = holder->RawFastPropertyAt(field_index);
    647     return current_value->IsUninitialized(isolate()) || current_value == value;
    648   }
    649 }
    650 
    651 int LookupIterator::GetFieldDescriptorIndex() const {
    652   DCHECK(has_property_);
    653   DCHECK(holder_->HasFastProperties());
    654   DCHECK_EQ(kField, property_details_.location());
    655   DCHECK_EQ(kData, property_details_.kind());
    656   return descriptor_number();
    657 }
    658 
    659 int LookupIterator::GetAccessorIndex() const {
    660   DCHECK(has_property_);
    661   DCHECK(holder_->HasFastProperties());
    662   DCHECK_EQ(kDescriptor, property_details_.location());
    663   DCHECK_EQ(kAccessor, property_details_.kind());
    664   return descriptor_number();
    665 }
    666 
    667 
    668 int LookupIterator::GetConstantIndex() const {
    669   DCHECK(has_property_);
    670   DCHECK(holder_->HasFastProperties());
    671   DCHECK_EQ(kDescriptor, property_details_.location());
    672   DCHECK_EQ(kData, property_details_.kind());
    673   DCHECK(!FLAG_track_constant_fields);
    674   DCHECK(!IsElement());
    675   return descriptor_number();
    676 }
    677 
    678 Handle<Map> LookupIterator::GetFieldOwnerMap() const {
    679   DCHECK(has_property_);
    680   DCHECK(holder_->HasFastProperties());
    681   DCHECK_EQ(kField, property_details_.location());
    682   DCHECK(!IsElement());
    683   Map* holder_map = holder_->map();
    684   return handle(holder_map->FindFieldOwner(descriptor_number()), isolate_);
    685 }
    686 
    687 FieldIndex LookupIterator::GetFieldIndex() const {
    688   DCHECK(has_property_);
    689   DCHECK(holder_->HasFastProperties());
    690   DCHECK_EQ(kField, property_details_.location());
    691   DCHECK(!IsElement());
    692   Map* holder_map = holder_->map();
    693   int index =
    694       holder_map->instance_descriptors()->GetFieldIndex(descriptor_number());
    695   bool is_double = representation().IsDouble();
    696   return FieldIndex::ForPropertyIndex(holder_map, index, is_double);
    697 }
    698 
    699 Handle<FieldType> LookupIterator::GetFieldType() const {
    700   DCHECK(has_property_);
    701   DCHECK(holder_->HasFastProperties());
    702   DCHECK_EQ(kField, property_details_.location());
    703   return handle(
    704       holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()),
    705       isolate_);
    706 }
    707 
    708 
    709 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
    710   DCHECK(!IsElement());
    711   Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
    712   Object* value = holder->global_dictionary()->ValueAt(dictionary_entry());
    713   DCHECK(value->IsPropertyCell());
    714   return handle(PropertyCell::cast(value), isolate_);
    715 }
    716 
    717 
    718 Handle<Object> LookupIterator::GetAccessors() const {
    719   DCHECK_EQ(ACCESSOR, state_);
    720   return FetchValue();
    721 }
    722 
    723 
    724 Handle<Object> LookupIterator::GetDataValue() const {
    725   DCHECK_EQ(DATA, state_);
    726   Handle<Object> value = FetchValue();
    727   return value;
    728 }
    729 
    730 void LookupIterator::WriteDataValue(Handle<Object> value,
    731                                     bool initializing_store) {
    732   DCHECK_EQ(DATA, state_);
    733   Handle<JSReceiver> holder = GetHolder<JSReceiver>();
    734   if (IsElement()) {
    735     Handle<JSObject> object = Handle<JSObject>::cast(holder);
    736     ElementsAccessor* accessor = object->GetElementsAccessor();
    737     accessor->Set(object, number_, *value);
    738   } else if (holder->HasFastProperties()) {
    739     if (property_details_.location() == kField) {
    740       // Check that in case of kConst field the existing value is equal to
    741       // |value|.
    742       DCHECK_IMPLIES(
    743           !initializing_store && property_details_.constness() == kConst,
    744           IsConstFieldValueEqualTo(*value));
    745       JSObject::cast(*holder)->WriteToField(descriptor_number(),
    746                                             property_details_, *value);
    747     } else {
    748       DCHECK_EQ(kDescriptor, property_details_.location());
    749       DCHECK_EQ(kConst, property_details_.constness());
    750     }
    751   } else if (holder->IsJSGlobalObject()) {
    752     GlobalDictionary* dictionary = JSObject::cast(*holder)->global_dictionary();
    753     Object* cell = dictionary->ValueAt(dictionary_entry());
    754     DCHECK(cell->IsPropertyCell());
    755     PropertyCell::cast(cell)->set_value(*value);
    756   } else {
    757     NameDictionary* dictionary = holder->property_dictionary();
    758     dictionary->ValueAtPut(dictionary_entry(), *value);
    759   }
    760 }
    761 
    762 template <bool is_element>
    763 bool LookupIterator::SkipInterceptor(JSObject* holder) {
    764   auto info = GetInterceptor<is_element>(holder);
    765   // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
    766   if (info->non_masking()) {
    767     switch (interceptor_state_) {
    768       case InterceptorState::kUninitialized:
    769         interceptor_state_ = InterceptorState::kSkipNonMasking;
    770       // Fall through.
    771       case InterceptorState::kSkipNonMasking:
    772         return true;
    773       case InterceptorState::kProcessNonMasking:
    774         return false;
    775     }
    776   }
    777   return interceptor_state_ == InterceptorState::kProcessNonMasking;
    778 }
    779 
    780 JSReceiver* LookupIterator::NextHolder(Map* map) {
    781   DisallowHeapAllocation no_gc;
    782   if (map->prototype() == heap()->null_value()) return NULL;
    783   if (!check_prototype_chain() && !map->has_hidden_prototype()) return NULL;
    784   return JSReceiver::cast(map->prototype());
    785 }
    786 
    787 LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const {
    788   DCHECK(!IsElement());
    789   if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND;
    790 
    791   Handle<String> name_string = Handle<String>::cast(name_);
    792   if (name_string->length() == 0) return NOT_FOUND;
    793 
    794   return IsSpecialIndex(isolate_->unicode_cache(), *name_string)
    795              ? INTEGER_INDEXED_EXOTIC
    796              : NOT_FOUND;
    797 }
    798 
    799 namespace {
    800 
    801 template <bool is_element>
    802 bool HasInterceptor(Map* map) {
    803   return is_element ? map->has_indexed_interceptor()
    804                     : map->has_named_interceptor();
    805 }
    806 
    807 }  // namespace
    808 
    809 template <bool is_element>
    810 LookupIterator::State LookupIterator::LookupInSpecialHolder(
    811     Map* const map, JSReceiver* const holder) {
    812   STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
    813   switch (state_) {
    814     case NOT_FOUND:
    815       if (map->IsJSProxyMap()) {
    816         if (is_element || !name_->IsPrivate()) return JSPROXY;
    817       }
    818       if (map->is_access_check_needed()) {
    819         if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
    820       }
    821     // Fall through.
    822     case ACCESS_CHECK:
    823       if (check_interceptor() && HasInterceptor<is_element>(map) &&
    824           !SkipInterceptor<is_element>(JSObject::cast(holder))) {
    825         if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
    826       }
    827     // Fall through.
    828     case INTERCEPTOR:
    829       if (!is_element && map->IsJSGlobalObjectMap()) {
    830         GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary();
    831         int number = dict->FindEntry(name_);
    832         if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
    833         number_ = static_cast<uint32_t>(number);
    834         DCHECK(dict->ValueAt(number_)->IsPropertyCell());
    835         PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_));
    836         if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND;
    837         property_details_ = cell->property_details();
    838         has_property_ = true;
    839         switch (property_details_.kind()) {
    840           case v8::internal::kData:
    841             return DATA;
    842           case v8::internal::kAccessor:
    843             return ACCESSOR;
    844         }
    845       }
    846       return LookupInRegularHolder<is_element>(map, holder);
    847     case ACCESSOR:
    848     case DATA:
    849       return NOT_FOUND;
    850     case INTEGER_INDEXED_EXOTIC:
    851     case JSPROXY:
    852     case TRANSITION:
    853       UNREACHABLE();
    854   }
    855   UNREACHABLE();
    856   return NOT_FOUND;
    857 }
    858 
    859 template <bool is_element>
    860 LookupIterator::State LookupIterator::LookupInRegularHolder(
    861     Map* const map, JSReceiver* const holder) {
    862   DisallowHeapAllocation no_gc;
    863   if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
    864     return NOT_FOUND;
    865   }
    866 
    867   if (is_element) {
    868     JSObject* js_object = JSObject::cast(holder);
    869     ElementsAccessor* accessor = js_object->GetElementsAccessor();
    870     FixedArrayBase* backing_store = js_object->elements();
    871     number_ =
    872         accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_);
    873     if (number_ == kMaxUInt32) {
    874       return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
    875     }
    876     property_details_ = accessor->GetDetails(js_object, number_);
    877   } else if (!map->is_dictionary_map()) {
    878     DescriptorArray* descriptors = map->instance_descriptors();
    879     int number = descriptors->SearchWithCache(isolate_, *name_, map);
    880     if (number == DescriptorArray::kNotFound) return NotFound(holder);
    881     number_ = static_cast<uint32_t>(number);
    882     property_details_ = descriptors->GetDetails(number_);
    883   } else {
    884     NameDictionary* dict = holder->property_dictionary();
    885     int number = dict->FindEntry(name_);
    886     if (number == NameDictionary::kNotFound) return NotFound(holder);
    887     number_ = static_cast<uint32_t>(number);
    888     property_details_ = dict->DetailsAt(number_);
    889   }
    890   has_property_ = true;
    891   switch (property_details_.kind()) {
    892     case v8::internal::kData:
    893       return DATA;
    894     case v8::internal::kAccessor:
    895       return ACCESSOR;
    896   }
    897 
    898   UNREACHABLE();
    899   return state_;
    900 }
    901 
    902 Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
    903     const {
    904   DCHECK_EQ(ACCESS_CHECK, state_);
    905   DisallowHeapAllocation no_gc;
    906   AccessCheckInfo* access_check_info =
    907       AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
    908   if (access_check_info) {
    909     Object* interceptor = IsElement() ? access_check_info->indexed_interceptor()
    910                                       : access_check_info->named_interceptor();
    911     if (interceptor) {
    912       return handle(InterceptorInfo::cast(interceptor), isolate_);
    913     }
    914   }
    915   return Handle<InterceptorInfo>();
    916 }
    917 
    918 bool LookupIterator::TryLookupCachedProperty() {
    919   return state() == LookupIterator::ACCESSOR &&
    920          GetAccessors()->IsAccessorPair() && LookupCachedProperty();
    921 }
    922 
    923 bool LookupIterator::LookupCachedProperty() {
    924   DCHECK_EQ(state(), LookupIterator::ACCESSOR);
    925   DCHECK(GetAccessors()->IsAccessorPair());
    926 
    927   AccessorPair* accessor_pair = AccessorPair::cast(*GetAccessors());
    928   Handle<Object> getter(accessor_pair->getter(), isolate());
    929   MaybeHandle<Name> maybe_name =
    930       FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), getter);
    931   if (maybe_name.is_null()) return false;
    932 
    933   // We have found a cached property! Modify the iterator accordingly.
    934   name_ = maybe_name.ToHandleChecked();
    935   Restart();
    936   CHECK_EQ(state(), LookupIterator::DATA);
    937   return true;
    938 }
    939 
    940 }  // namespace internal
    941 }  // namespace v8
    942