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->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) {
     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()->has_instance_symbol()) {
    195     if (!isolate_->IsHasInstanceLookupChainIntact()) return;
    196     isolate_->InvalidateHasInstanceProtector();
    197   } else if (*name_ == heap()->iterator_symbol()) {
    198     if (!isolate_->IsArrayIteratorLookupChainIntact()) return;
    199     if (holder_->IsJSArray()) {
    200       isolate_->InvalidateArrayIteratorProtector();
    201     }
    202   }
    203 }
    204 
    205 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
    206   DCHECK(state_ == DATA || state_ == ACCESSOR);
    207   DCHECK(HolderIsReceiverOrHiddenPrototype());
    208 
    209   Handle<JSObject> holder = GetHolder<JSObject>();
    210 
    211   if (IsElement()) {
    212     ElementsKind kind = holder->GetElementsKind();
    213     ElementsKind to = value->OptimalElementsKind();
    214     if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
    215     to = GetMoreGeneralElementsKind(kind, to);
    216 
    217     if (kind != to) {
    218       JSObject::TransitionElementsKind(holder, to);
    219     }
    220 
    221     // Copy the backing store if it is copy-on-write.
    222     if (IsFastSmiOrObjectElementsKind(to)) {
    223       JSObject::EnsureWritableFastElements(holder);
    224     }
    225     return;
    226   }
    227 
    228   if (holder->IsJSGlobalObject()) {
    229     Handle<GlobalDictionary> dictionary(holder->global_dictionary());
    230     Handle<PropertyCell> cell(
    231         PropertyCell::cast(dictionary->ValueAt(dictionary_entry())));
    232     DCHECK(!cell->IsTheHole(isolate_));
    233     property_details_ = cell->property_details();
    234     PropertyCell::PrepareForValue(dictionary, dictionary_entry(), value,
    235                                   property_details_);
    236     return;
    237   }
    238   if (!holder->HasFastProperties()) return;
    239 
    240   Handle<Map> old_map(holder->map(), isolate_);
    241   Handle<Map> new_map =
    242       Map::PrepareForDataProperty(old_map, descriptor_number(), value);
    243 
    244   if (old_map.is_identical_to(new_map)) {
    245     // Update the property details if the representation was None.
    246     if (representation().IsNone()) {
    247       property_details_ =
    248           new_map->instance_descriptors()->GetDetails(descriptor_number());
    249     }
    250     return;
    251   }
    252 
    253   JSObject::MigrateToMap(holder, new_map);
    254   ReloadPropertyInformation<false>();
    255 }
    256 
    257 
    258 void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
    259                                              PropertyAttributes attributes) {
    260   DCHECK(state_ == DATA || state_ == ACCESSOR);
    261   DCHECK(HolderIsReceiverOrHiddenPrototype());
    262   Handle<JSObject> holder = GetHolder<JSObject>();
    263   if (IsElement()) {
    264     DCHECK(!holder->HasFixedTypedArrayElements());
    265     DCHECK(attributes != NONE || !holder->HasFastElements());
    266     Handle<FixedArrayBase> elements(holder->elements());
    267     holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
    268                                                attributes);
    269     ReloadPropertyInformation<true>();
    270   } else if (holder->HasFastProperties()) {
    271     Handle<Map> old_map(holder->map(), isolate_);
    272     Handle<Map> new_map = Map::ReconfigureExistingProperty(
    273         old_map, descriptor_number(), i::kData, attributes);
    274     new_map = Map::PrepareForDataProperty(new_map, descriptor_number(), value);
    275     JSObject::MigrateToMap(holder, new_map);
    276     ReloadPropertyInformation<false>();
    277   } else {
    278     PropertyDetails details(attributes, v8::internal::DATA, 0,
    279                             PropertyCellType::kMutable);
    280     if (holder->IsJSGlobalObject()) {
    281       Handle<GlobalDictionary> dictionary(holder->global_dictionary());
    282 
    283       Handle<PropertyCell> cell = PropertyCell::PrepareForValue(
    284           dictionary, dictionary_entry(), value, details);
    285       cell->set_value(*value);
    286       property_details_ = cell->property_details();
    287     } else {
    288       Handle<NameDictionary> dictionary(holder->property_dictionary());
    289       PropertyDetails original_details =
    290           dictionary->DetailsAt(dictionary_entry());
    291       int enumeration_index = original_details.dictionary_index();
    292       DCHECK(enumeration_index > 0);
    293       details = details.set_index(enumeration_index);
    294       dictionary->SetEntry(dictionary_entry(), name(), value, details);
    295       property_details_ = details;
    296     }
    297     state_ = DATA;
    298   }
    299 
    300   WriteDataValue(value);
    301 
    302 #if VERIFY_HEAP
    303   if (FLAG_verify_heap) {
    304     holder->JSObjectVerify();
    305   }
    306 #endif
    307 }
    308 
    309 // Can only be called when the receiver is a JSObject. JSProxy has to be handled
    310 // via a trap. Adding properties to primitive values is not observable.
    311 void LookupIterator::PrepareTransitionToDataProperty(
    312     Handle<JSObject> receiver, Handle<Object> value,
    313     PropertyAttributes attributes, Object::StoreFromKeyed store_mode) {
    314   DCHECK(receiver.is_identical_to(GetStoreTarget()));
    315   if (state_ == TRANSITION) return;
    316 
    317   if (!IsElement() && name()->IsPrivate()) {
    318     attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
    319   }
    320 
    321   DCHECK(state_ != LookupIterator::ACCESSOR ||
    322          (GetAccessors()->IsAccessorInfo() &&
    323           AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
    324   DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
    325   DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
    326 
    327   Handle<Map> map(receiver->map(), isolate_);
    328 
    329   // Dictionary maps can always have additional data properties.
    330   if (map->is_dictionary_map()) {
    331     state_ = TRANSITION;
    332     if (map->IsJSGlobalObjectMap()) {
    333       // Install a property cell.
    334       Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver);
    335       int entry;
    336       Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
    337           global, name(), PropertyCellType::kUninitialized, &entry);
    338       Handle<GlobalDictionary> dictionary(global->global_dictionary(),
    339                                           isolate_);
    340       DCHECK(cell->value()->IsTheHole(isolate_));
    341       DCHECK(!value->IsTheHole(isolate_));
    342       transition_ = cell;
    343       // Assign an enumeration index to the property and update
    344       // SetNextEnumerationIndex.
    345       int index = dictionary->NextEnumerationIndex();
    346       dictionary->SetNextEnumerationIndex(index + 1);
    347       property_details_ = PropertyDetails(attributes, i::DATA, index,
    348                                           PropertyCellType::kUninitialized);
    349       PropertyCellType new_type =
    350           PropertyCell::UpdatedType(cell, value, property_details_);
    351       property_details_ = property_details_.set_cell_type(new_type);
    352       cell->set_property_details(property_details_);
    353       number_ = entry;
    354       has_property_ = true;
    355     } else {
    356       // Don't set enumeration index (it will be set during value store).
    357       property_details_ =
    358           PropertyDetails(attributes, i::DATA, 0, PropertyCellType::kNoCell);
    359       transition_ = map;
    360     }
    361     return;
    362   }
    363 
    364   Handle<Map> transition =
    365       Map::TransitionToDataProperty(map, name_, value, attributes, store_mode);
    366   state_ = TRANSITION;
    367   transition_ = transition;
    368 
    369   if (transition->is_dictionary_map()) {
    370     // Don't set enumeration index (it will be set during value store).
    371     property_details_ =
    372         PropertyDetails(attributes, i::DATA, 0, PropertyCellType::kNoCell);
    373   } else {
    374     property_details_ = transition->GetLastDescriptorDetails();
    375     has_property_ = true;
    376   }
    377 }
    378 
    379 void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) {
    380   DCHECK_EQ(TRANSITION, state_);
    381 
    382   DCHECK(receiver.is_identical_to(GetStoreTarget()));
    383   holder_ = receiver;
    384   if (receiver->IsJSGlobalObject()) {
    385     state_ = DATA;
    386     return;
    387   }
    388   Handle<Map> transition = transition_map();
    389   bool simple_transition = transition->GetBackPointer() == receiver->map();
    390   JSObject::MigrateToMap(receiver, transition);
    391 
    392   if (simple_transition) {
    393     int number = transition->LastAdded();
    394     number_ = static_cast<uint32_t>(number);
    395     property_details_ = transition->GetLastDescriptorDetails();
    396     state_ = DATA;
    397   } else if (receiver->map()->is_dictionary_map()) {
    398     Handle<NameDictionary> dictionary(receiver->property_dictionary(),
    399                                       isolate_);
    400     int entry;
    401     dictionary = NameDictionary::Add(dictionary, name(),
    402                                      isolate_->factory()->uninitialized_value(),
    403                                      property_details_, &entry);
    404     receiver->set_properties(*dictionary);
    405     // Reload details containing proper enumeration index value.
    406     property_details_ = dictionary->DetailsAt(entry);
    407     number_ = entry;
    408     has_property_ = true;
    409     state_ = DATA;
    410 
    411   } else {
    412     ReloadPropertyInformation<false>();
    413   }
    414 }
    415 
    416 
    417 void LookupIterator::Delete() {
    418   Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
    419   if (IsElement()) {
    420     Handle<JSObject> object = Handle<JSObject>::cast(holder);
    421     ElementsAccessor* accessor = object->GetElementsAccessor();
    422     accessor->Delete(object, number_);
    423   } else {
    424     bool is_prototype_map = holder->map()->is_prototype_map();
    425     RuntimeCallTimerScope stats_scope(
    426         isolate_, is_prototype_map
    427                       ? &RuntimeCallStats::PrototypeObject_DeleteProperty
    428                       : &RuntimeCallStats::Object_DeleteProperty);
    429 
    430     PropertyNormalizationMode mode =
    431         is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
    432 
    433     if (holder->HasFastProperties()) {
    434       JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
    435                                     "DeletingProperty");
    436       ReloadPropertyInformation<false>();
    437     }
    438     // TODO(verwaest): Get rid of the name_ argument.
    439     JSReceiver::DeleteNormalizedProperty(holder, name_, number_);
    440     if (holder->IsJSObject()) {
    441       JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
    442     }
    443   }
    444   state_ = NOT_FOUND;
    445 }
    446 
    447 void LookupIterator::TransitionToAccessorProperty(
    448     Handle<Object> getter, Handle<Object> setter,
    449     PropertyAttributes attributes) {
    450   DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
    451   // Can only be called when the receiver is a JSObject. JSProxy has to be
    452   // handled via a trap. Adding properties to primitive values is not
    453   // observable.
    454   Handle<JSObject> receiver = GetStoreTarget();
    455   if (!IsElement() && name()->IsPrivate()) {
    456     attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
    457   }
    458 
    459   if (!IsElement() && !receiver->map()->is_dictionary_map()) {
    460     Handle<Map> old_map(receiver->map(), isolate_);
    461 
    462     if (!holder_.is_identical_to(receiver)) {
    463       holder_ = receiver;
    464       state_ = NOT_FOUND;
    465     } else if (state_ == INTERCEPTOR) {
    466       LookupInRegularHolder<false>(*old_map, *holder_);
    467     }
    468     int descriptor =
    469         IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound;
    470 
    471     Handle<Map> new_map = Map::TransitionToAccessorProperty(
    472         isolate_, old_map, name_, descriptor, getter, setter, attributes);
    473     bool simple_transition = new_map->GetBackPointer() == receiver->map();
    474     JSObject::MigrateToMap(receiver, new_map);
    475 
    476     if (simple_transition) {
    477       int number = new_map->LastAdded();
    478       number_ = static_cast<uint32_t>(number);
    479       property_details_ = new_map->GetLastDescriptorDetails();
    480       state_ = ACCESSOR;
    481       return;
    482     }
    483 
    484     ReloadPropertyInformation<false>();
    485     if (!new_map->is_dictionary_map()) return;
    486   }
    487 
    488   Handle<AccessorPair> pair;
    489   if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
    490     pair = Handle<AccessorPair>::cast(GetAccessors());
    491     // If the component and attributes are identical, nothing has to be done.
    492     if (pair->Equals(*getter, *setter)) {
    493       if (property_details().attributes() == attributes) {
    494         if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver);
    495         return;
    496       }
    497     } else {
    498       pair = AccessorPair::Copy(pair);
    499       pair->SetComponents(*getter, *setter);
    500     }
    501   } else {
    502     pair = factory()->NewAccessorPair();
    503     pair->SetComponents(*getter, *setter);
    504   }
    505 
    506   TransitionToAccessorPair(pair, attributes);
    507 
    508 #if VERIFY_HEAP
    509   if (FLAG_verify_heap) {
    510     receiver->JSObjectVerify();
    511   }
    512 #endif
    513 }
    514 
    515 
    516 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
    517                                               PropertyAttributes attributes) {
    518   Handle<JSObject> receiver = GetStoreTarget();
    519   holder_ = receiver;
    520 
    521   PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
    522                           PropertyCellType::kMutable);
    523 
    524   if (IsElement()) {
    525     // TODO(verwaest): Move code into the element accessor.
    526     Handle<SeededNumberDictionary> dictionary =
    527         JSObject::NormalizeElements(receiver);
    528 
    529     // We unconditionally pass used_as_prototype=false here because the call
    530     // to RequireSlowElements takes care of the required IC clearing and
    531     // we don't want to walk the heap twice.
    532     dictionary =
    533         SeededNumberDictionary::Set(dictionary, index_, pair, details, false);
    534     receiver->RequireSlowElements(*dictionary);
    535 
    536     if (receiver->HasSlowArgumentsElements()) {
    537       FixedArray* parameter_map = FixedArray::cast(receiver->elements());
    538       uint32_t length = parameter_map->length() - 2;
    539       if (number_ < length) {
    540         parameter_map->set(number_ + 2, heap()->the_hole_value());
    541       }
    542       FixedArray::cast(receiver->elements())->set(1, *dictionary);
    543     } else {
    544       receiver->set_elements(*dictionary);
    545     }
    546 
    547     ReloadPropertyInformation<true>();
    548   } else {
    549     PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
    550                                          ? KEEP_INOBJECT_PROPERTIES
    551                                          : CLEAR_INOBJECT_PROPERTIES;
    552     // Normalize object to make this operation simple.
    553     JSObject::NormalizeProperties(receiver, mode, 0,
    554                                   "TransitionToAccessorPair");
    555 
    556     JSObject::SetNormalizedProperty(receiver, name_, pair, details);
    557     JSObject::ReoptimizeIfPrototype(receiver);
    558 
    559     ReloadPropertyInformation<false>();
    560   }
    561 }
    562 
    563 
    564 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
    565   DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
    566   // Optimization that only works if configuration_ is not mutable.
    567   if (!check_prototype_chain()) return true;
    568   DisallowHeapAllocation no_gc;
    569   if (*receiver_ == *holder_) return true;
    570   if (!receiver_->IsJSReceiver()) return false;
    571   JSReceiver* current = JSReceiver::cast(*receiver_);
    572   JSReceiver* object = *holder_;
    573   if (!current->map()->has_hidden_prototype()) return false;
    574   // JSProxy do not occur as hidden prototypes.
    575   if (object->IsJSProxy()) return false;
    576   PrototypeIterator iter(isolate(), current, kStartAtPrototype,
    577                          PrototypeIterator::END_AT_NON_HIDDEN);
    578   while (!iter.IsAtEnd()) {
    579     if (iter.GetCurrent<JSReceiver>() == object) return true;
    580     iter.Advance();
    581   }
    582   return false;
    583 }
    584 
    585 
    586 Handle<Object> LookupIterator::FetchValue() const {
    587   Object* result = NULL;
    588   if (IsElement()) {
    589     Handle<JSObject> holder = GetHolder<JSObject>();
    590     ElementsAccessor* accessor = holder->GetElementsAccessor();
    591     return accessor->Get(holder, number_);
    592   } else if (holder_->IsJSGlobalObject()) {
    593     Handle<JSObject> holder = GetHolder<JSObject>();
    594     result = holder->global_dictionary()->ValueAt(number_);
    595     DCHECK(result->IsPropertyCell());
    596     result = PropertyCell::cast(result)->value();
    597   } else if (!holder_->HasFastProperties()) {
    598     result = holder_->property_dictionary()->ValueAt(number_);
    599   } else if (property_details_.type() == v8::internal::DATA) {
    600     Handle<JSObject> holder = GetHolder<JSObject>();
    601     FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
    602     return JSObject::FastPropertyAt(holder, property_details_.representation(),
    603                                     field_index);
    604   } else {
    605     result = holder_->map()->instance_descriptors()->GetValue(number_);
    606   }
    607   return handle(result, isolate_);
    608 }
    609 
    610 int LookupIterator::GetFieldDescriptorIndex() const {
    611   DCHECK(has_property_);
    612   DCHECK(holder_->HasFastProperties());
    613   DCHECK_EQ(v8::internal::DATA, property_details_.type());
    614   return descriptor_number();
    615 }
    616 
    617 int LookupIterator::GetAccessorIndex() const {
    618   DCHECK(has_property_);
    619   DCHECK(holder_->HasFastProperties());
    620   DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
    621   return descriptor_number();
    622 }
    623 
    624 
    625 int LookupIterator::GetConstantIndex() const {
    626   DCHECK(has_property_);
    627   DCHECK(holder_->HasFastProperties());
    628   DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
    629   DCHECK(!IsElement());
    630   return descriptor_number();
    631 }
    632 
    633 
    634 FieldIndex LookupIterator::GetFieldIndex() const {
    635   DCHECK(has_property_);
    636   DCHECK(holder_->HasFastProperties());
    637   DCHECK_EQ(v8::internal::DATA, property_details_.type());
    638   DCHECK(!IsElement());
    639   Map* holder_map = holder_->map();
    640   int index =
    641       holder_map->instance_descriptors()->GetFieldIndex(descriptor_number());
    642   bool is_double = representation().IsDouble();
    643   return FieldIndex::ForPropertyIndex(holder_map, index, is_double);
    644 }
    645 
    646 Handle<FieldType> LookupIterator::GetFieldType() const {
    647   DCHECK(has_property_);
    648   DCHECK(holder_->HasFastProperties());
    649   DCHECK_EQ(v8::internal::DATA, property_details_.type());
    650   return handle(
    651       holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()),
    652       isolate_);
    653 }
    654 
    655 
    656 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
    657   DCHECK(!IsElement());
    658   Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
    659   Object* value = holder->global_dictionary()->ValueAt(dictionary_entry());
    660   DCHECK(value->IsPropertyCell());
    661   return handle(PropertyCell::cast(value), isolate_);
    662 }
    663 
    664 
    665 Handle<Object> LookupIterator::GetAccessors() const {
    666   DCHECK_EQ(ACCESSOR, state_);
    667   return FetchValue();
    668 }
    669 
    670 
    671 Handle<Object> LookupIterator::GetDataValue() const {
    672   DCHECK_EQ(DATA, state_);
    673   Handle<Object> value = FetchValue();
    674   return value;
    675 }
    676 
    677 
    678 void LookupIterator::WriteDataValue(Handle<Object> value) {
    679   DCHECK_EQ(DATA, state_);
    680   Handle<JSReceiver> holder = GetHolder<JSReceiver>();
    681   if (IsElement()) {
    682     Handle<JSObject> object = Handle<JSObject>::cast(holder);
    683     ElementsAccessor* accessor = object->GetElementsAccessor();
    684     accessor->Set(object, number_, *value);
    685   } else if (holder->HasFastProperties()) {
    686     if (property_details_.type() == v8::internal::DATA) {
    687       JSObject::cast(*holder)->WriteToField(descriptor_number(),
    688                                             property_details_, *value);
    689     } else {
    690       DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
    691     }
    692   } else if (holder->IsJSGlobalObject()) {
    693     GlobalDictionary* dictionary = JSObject::cast(*holder)->global_dictionary();
    694     Object* cell = dictionary->ValueAt(dictionary_entry());
    695     DCHECK(cell->IsPropertyCell());
    696     PropertyCell::cast(cell)->set_value(*value);
    697   } else {
    698     NameDictionary* dictionary = holder->property_dictionary();
    699     dictionary->ValueAtPut(dictionary_entry(), *value);
    700   }
    701 }
    702 
    703 template <bool is_element>
    704 bool LookupIterator::SkipInterceptor(JSObject* holder) {
    705   auto info = GetInterceptor<is_element>(holder);
    706   // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
    707   if (info->non_masking()) {
    708     switch (interceptor_state_) {
    709       case InterceptorState::kUninitialized:
    710         interceptor_state_ = InterceptorState::kSkipNonMasking;
    711       // Fall through.
    712       case InterceptorState::kSkipNonMasking:
    713         return true;
    714       case InterceptorState::kProcessNonMasking:
    715         return false;
    716     }
    717   }
    718   return interceptor_state_ == InterceptorState::kProcessNonMasking;
    719 }
    720 
    721 JSReceiver* LookupIterator::NextHolder(Map* map) {
    722   DisallowHeapAllocation no_gc;
    723   if (map->prototype() == heap()->null_value()) return NULL;
    724   if (!check_prototype_chain() && !map->has_hidden_prototype()) return NULL;
    725   return JSReceiver::cast(map->prototype());
    726 }
    727 
    728 LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const {
    729   DCHECK(!IsElement());
    730   if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND;
    731 
    732   Handle<String> name_string = Handle<String>::cast(name_);
    733   if (name_string->length() == 0) return NOT_FOUND;
    734 
    735   return IsSpecialIndex(isolate_->unicode_cache(), *name_string)
    736              ? INTEGER_INDEXED_EXOTIC
    737              : NOT_FOUND;
    738 }
    739 
    740 namespace {
    741 
    742 template <bool is_element>
    743 bool HasInterceptor(Map* map) {
    744   return is_element ? map->has_indexed_interceptor()
    745                     : map->has_named_interceptor();
    746 }
    747 
    748 }  // namespace
    749 
    750 template <bool is_element>
    751 LookupIterator::State LookupIterator::LookupInSpecialHolder(
    752     Map* const map, JSReceiver* const holder) {
    753   STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
    754   switch (state_) {
    755     case NOT_FOUND:
    756       if (map->IsJSProxyMap()) {
    757         if (is_element || !name_->IsPrivate()) return JSPROXY;
    758       }
    759       if (map->is_access_check_needed()) {
    760         if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
    761       }
    762     // Fall through.
    763     case ACCESS_CHECK:
    764       if (check_interceptor() && HasInterceptor<is_element>(map) &&
    765           !SkipInterceptor<is_element>(JSObject::cast(holder))) {
    766         if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
    767       }
    768     // Fall through.
    769     case INTERCEPTOR:
    770       if (!is_element && map->IsJSGlobalObjectMap()) {
    771         GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary();
    772         int number = dict->FindEntry(name_);
    773         if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
    774         number_ = static_cast<uint32_t>(number);
    775         DCHECK(dict->ValueAt(number_)->IsPropertyCell());
    776         PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_));
    777         if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND;
    778         property_details_ = cell->property_details();
    779         has_property_ = true;
    780         switch (property_details_.kind()) {
    781           case v8::internal::kData:
    782             return DATA;
    783           case v8::internal::kAccessor:
    784             return ACCESSOR;
    785         }
    786       }
    787       return LookupInRegularHolder<is_element>(map, holder);
    788     case ACCESSOR:
    789     case DATA:
    790       return NOT_FOUND;
    791     case INTEGER_INDEXED_EXOTIC:
    792     case JSPROXY:
    793     case TRANSITION:
    794       UNREACHABLE();
    795   }
    796   UNREACHABLE();
    797   return NOT_FOUND;
    798 }
    799 
    800 template <bool is_element>
    801 LookupIterator::State LookupIterator::LookupInRegularHolder(
    802     Map* const map, JSReceiver* const holder) {
    803   DisallowHeapAllocation no_gc;
    804   if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
    805     return NOT_FOUND;
    806   }
    807 
    808   if (is_element) {
    809     JSObject* js_object = JSObject::cast(holder);
    810     ElementsAccessor* accessor = js_object->GetElementsAccessor();
    811     FixedArrayBase* backing_store = js_object->elements();
    812     number_ =
    813         accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_);
    814     if (number_ == kMaxUInt32) {
    815       return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
    816     }
    817     property_details_ = accessor->GetDetails(js_object, number_);
    818   } else if (!map->is_dictionary_map()) {
    819     DescriptorArray* descriptors = map->instance_descriptors();
    820     int number = descriptors->SearchWithCache(isolate_, *name_, map);
    821     if (number == DescriptorArray::kNotFound) return NotFound(holder);
    822     number_ = static_cast<uint32_t>(number);
    823     property_details_ = descriptors->GetDetails(number_);
    824   } else {
    825     NameDictionary* dict = holder->property_dictionary();
    826     int number = dict->FindEntry(name_);
    827     if (number == NameDictionary::kNotFound) return NotFound(holder);
    828     number_ = static_cast<uint32_t>(number);
    829     property_details_ = dict->DetailsAt(number_);
    830   }
    831   has_property_ = true;
    832   switch (property_details_.kind()) {
    833     case v8::internal::kData:
    834       return DATA;
    835     case v8::internal::kAccessor:
    836       return ACCESSOR;
    837   }
    838 
    839   UNREACHABLE();
    840   return state_;
    841 }
    842 
    843 Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
    844     const {
    845   DCHECK_EQ(ACCESS_CHECK, state_);
    846   DisallowHeapAllocation no_gc;
    847   AccessCheckInfo* access_check_info =
    848       AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
    849   if (access_check_info) {
    850     Object* interceptor = IsElement() ? access_check_info->indexed_interceptor()
    851                                       : access_check_info->named_interceptor();
    852     if (interceptor) {
    853       return handle(InterceptorInfo::cast(interceptor), isolate_);
    854     }
    855   }
    856   return Handle<InterceptorInfo>();
    857 }
    858 
    859 bool LookupIterator::TryLookupCachedProperty() {
    860   return state() == LookupIterator::ACCESSOR &&
    861          GetAccessors()->IsAccessorPair() && LookupCachedProperty();
    862 }
    863 
    864 bool LookupIterator::LookupCachedProperty() {
    865   DCHECK_EQ(state(), LookupIterator::ACCESSOR);
    866   DCHECK(GetAccessors()->IsAccessorPair());
    867 
    868   AccessorPair* accessor_pair = AccessorPair::cast(*GetAccessors());
    869   Handle<Object> getter(accessor_pair->getter(), isolate());
    870   MaybeHandle<Name> maybe_name =
    871       FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), getter);
    872   if (maybe_name.is_null()) return false;
    873 
    874   // We have found a cached property! Modify the iterator accordingly.
    875   name_ = maybe_name.ToHandleChecked();
    876   Restart();
    877   CHECK_EQ(state(), LookupIterator::DATA);
    878   return true;
    879 }
    880 
    881 }  // namespace internal
    882 }  // namespace v8
    883