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 
     17 // static
     18 LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate,
     19                                                  Handle<Object> receiver,
     20                                                  Handle<Object> key,
     21                                                  bool* success,
     22                                                  Configuration configuration) {
     23   uint32_t index = 0;
     24   if (key->ToArrayIndex(&index)) {
     25     *success = true;
     26     return LookupIterator(isolate, receiver, index, configuration);
     27   }
     28 
     29   Handle<Name> name;
     30   *success = Object::ToName(isolate, key).ToHandle(&name);
     31   if (!*success) {
     32     DCHECK(isolate->has_pending_exception());
     33     // Return an unusable dummy.
     34     return LookupIterator(receiver, isolate->factory()->empty_string());
     35   }
     36 
     37   if (name->AsArrayIndex(&index)) {
     38     LookupIterator it(isolate, receiver, index, configuration);
     39     // Here we try to avoid having to rebuild the string later
     40     // by storing it on the indexed LookupIterator.
     41     it.name_ = name;
     42     return it;
     43   }
     44 
     45   return LookupIterator(receiver, name, configuration);
     46 }
     47 
     48 template <bool is_element>
     49 void LookupIterator::Start() {
     50   DisallowHeapAllocation no_gc;
     51 
     52   has_property_ = false;
     53   state_ = NOT_FOUND;
     54   holder_ = initial_holder_;
     55 
     56   JSReceiver* holder = *holder_;
     57   Map* map = holder->map();
     58 
     59   state_ = LookupInHolder<is_element>(map, holder);
     60   if (IsFound()) return;
     61 
     62   NextInternal<is_element>(map, holder);
     63 }
     64 
     65 template void LookupIterator::Start<true>();
     66 template void LookupIterator::Start<false>();
     67 
     68 void LookupIterator::Next() {
     69   DCHECK_NE(JSPROXY, state_);
     70   DCHECK_NE(TRANSITION, state_);
     71   DisallowHeapAllocation no_gc;
     72   has_property_ = false;
     73 
     74   JSReceiver* holder = *holder_;
     75   Map* map = holder->map();
     76 
     77   if (map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) {
     78     state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
     79                          : LookupInSpecialHolder<false>(map, holder);
     80     if (IsFound()) return;
     81   }
     82 
     83   IsElement() ? NextInternal<true>(map, holder)
     84               : NextInternal<false>(map, holder);
     85 }
     86 
     87 template <bool is_element>
     88 void LookupIterator::NextInternal(Map* map, JSReceiver* holder) {
     89   do {
     90     JSReceiver* maybe_holder = NextHolder(map);
     91     if (maybe_holder == nullptr) {
     92       if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
     93         RestartLookupForNonMaskingInterceptors<is_element>();
     94         return;
     95       }
     96       state_ = NOT_FOUND;
     97       if (holder != *holder_) holder_ = handle(holder, isolate_);
     98       return;
     99     }
    100     holder = maybe_holder;
    101     map = holder->map();
    102     state_ = LookupInHolder<is_element>(map, holder);
    103   } while (!IsFound());
    104 
    105   holder_ = handle(holder, isolate_);
    106 }
    107 
    108 template <bool is_element>
    109 void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
    110   interceptor_state_ = interceptor_state;
    111   property_details_ = PropertyDetails::Empty();
    112   number_ = DescriptorArray::kNotFound;
    113   Start<is_element>();
    114 }
    115 
    116 template void LookupIterator::RestartInternal<true>(InterceptorState);
    117 template void LookupIterator::RestartInternal<false>(InterceptorState);
    118 
    119 // static
    120 Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
    121     Isolate* isolate, Handle<Object> receiver, uint32_t index) {
    122   // Strings are the only objects with properties (only elements) directly on
    123   // the wrapper. Hence we can skip generating the wrapper for all other cases.
    124   if (index != kMaxUInt32 && receiver->IsString() &&
    125       index < static_cast<uint32_t>(String::cast(*receiver)->length())) {
    126     // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
    127     // context, ensuring that we don't leak it into JS?
    128     Handle<JSFunction> constructor = isolate->string_function();
    129     Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
    130     Handle<JSValue>::cast(result)->set_value(*receiver);
    131     return result;
    132   }
    133   auto root = handle(receiver->GetRootMap(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   }
    198 }
    199 
    200 void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
    201   DCHECK(state_ == DATA || state_ == ACCESSOR);
    202   DCHECK(HolderIsReceiverOrHiddenPrototype());
    203 
    204   Handle<JSObject> holder = GetHolder<JSObject>();
    205 
    206   if (IsElement()) {
    207     ElementsKind kind = holder->GetElementsKind();
    208     ElementsKind to = value->OptimalElementsKind();
    209     if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
    210     to = GetMoreGeneralElementsKind(kind, to);
    211 
    212     if (kind != to) {
    213       JSObject::TransitionElementsKind(holder, to);
    214     }
    215 
    216     // Copy the backing store if it is copy-on-write.
    217     if (IsFastSmiOrObjectElementsKind(to)) {
    218       JSObject::EnsureWritableFastElements(holder);
    219     }
    220     return;
    221   }
    222 
    223   if (!holder->HasFastProperties()) return;
    224 
    225   Handle<Map> old_map(holder->map(), isolate_);
    226   Handle<Map> new_map =
    227       Map::PrepareForDataProperty(old_map, descriptor_number(), value);
    228 
    229   if (old_map.is_identical_to(new_map)) {
    230     // Update the property details if the representation was None.
    231     if (representation().IsNone()) {
    232       property_details_ =
    233           new_map->instance_descriptors()->GetDetails(descriptor_number());
    234     }
    235     return;
    236   }
    237 
    238   JSObject::MigrateToMap(holder, new_map);
    239   ReloadPropertyInformation<false>();
    240 }
    241 
    242 
    243 void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
    244                                              PropertyAttributes attributes) {
    245   DCHECK(state_ == DATA || state_ == ACCESSOR);
    246   DCHECK(HolderIsReceiverOrHiddenPrototype());
    247   Handle<JSObject> holder = GetHolder<JSObject>();
    248   if (IsElement()) {
    249     DCHECK(!holder->HasFixedTypedArrayElements());
    250     DCHECK(attributes != NONE || !holder->HasFastElements());
    251     Handle<FixedArrayBase> elements(holder->elements());
    252     holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
    253                                                attributes);
    254     ReloadPropertyInformation<true>();
    255   } else {
    256     if (!holder->HasFastProperties()) {
    257       PropertyDetails details(attributes, v8::internal::DATA, 0,
    258                               PropertyCellType::kMutable);
    259       JSObject::SetNormalizedProperty(holder, name(), value, details);
    260     } else {
    261       Handle<Map> old_map(holder->map(), isolate_);
    262       Handle<Map> new_map = Map::ReconfigureExistingProperty(
    263           old_map, descriptor_number(), i::kData, attributes);
    264       new_map =
    265           Map::PrepareForDataProperty(new_map, descriptor_number(), value);
    266       JSObject::MigrateToMap(holder, new_map);
    267     }
    268     ReloadPropertyInformation<false>();
    269   }
    270 
    271   WriteDataValue(value);
    272 
    273 #if VERIFY_HEAP
    274   if (FLAG_verify_heap) {
    275     holder->JSObjectVerify();
    276   }
    277 #endif
    278 }
    279 
    280 // Can only be called when the receiver is a JSObject. JSProxy has to be handled
    281 // via a trap. Adding properties to primitive values is not observable.
    282 void LookupIterator::PrepareTransitionToDataProperty(
    283     Handle<JSObject> receiver, Handle<Object> value,
    284     PropertyAttributes attributes, Object::StoreFromKeyed store_mode) {
    285   DCHECK(receiver.is_identical_to(GetStoreTarget()));
    286   if (state_ == TRANSITION) return;
    287   DCHECK(state_ != LookupIterator::ACCESSOR ||
    288          (GetAccessors()->IsAccessorInfo() &&
    289           AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
    290   DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
    291   DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
    292 
    293   Handle<Map> map(receiver->map(), isolate_);
    294 
    295   // Dictionary maps can always have additional data properties.
    296   if (map->is_dictionary_map()) {
    297     state_ = TRANSITION;
    298     if (map->IsJSGlobalObjectMap()) {
    299       // Install a property cell.
    300       auto cell = JSGlobalObject::EnsurePropertyCell(
    301           Handle<JSGlobalObject>::cast(receiver), name());
    302       DCHECK(cell->value()->IsTheHole(isolate_));
    303       transition_ = cell;
    304     } else {
    305       transition_ = map;
    306     }
    307     return;
    308   }
    309 
    310   Handle<Map> transition =
    311       Map::TransitionToDataProperty(map, name_, value, attributes, store_mode);
    312   state_ = TRANSITION;
    313   transition_ = transition;
    314 
    315   if (!transition->is_dictionary_map()) {
    316     property_details_ = transition->GetLastDescriptorDetails();
    317     has_property_ = true;
    318   }
    319 }
    320 
    321 void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) {
    322   DCHECK_EQ(TRANSITION, state_);
    323 
    324   DCHECK(receiver.is_identical_to(GetStoreTarget()));
    325 
    326   if (receiver->IsJSGlobalObject()) return;
    327   holder_ = receiver;
    328   Handle<Map> transition = transition_map();
    329   bool simple_transition = transition->GetBackPointer() == receiver->map();
    330   JSObject::MigrateToMap(receiver, transition);
    331 
    332   if (simple_transition) {
    333     int number = transition->LastAdded();
    334     number_ = static_cast<uint32_t>(number);
    335     property_details_ = transition->GetLastDescriptorDetails();
    336     state_ = DATA;
    337   } else {
    338     ReloadPropertyInformation<false>();
    339   }
    340 }
    341 
    342 
    343 void LookupIterator::Delete() {
    344   Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
    345   if (IsElement()) {
    346     Handle<JSObject> object = Handle<JSObject>::cast(holder);
    347     ElementsAccessor* accessor = object->GetElementsAccessor();
    348     accessor->Delete(object, number_);
    349   } else {
    350     bool is_prototype_map = holder->map()->is_prototype_map();
    351     RuntimeCallTimerScope stats_scope(
    352         isolate_, is_prototype_map
    353                       ? &RuntimeCallStats::PrototypeObject_DeleteProperty
    354                       : &RuntimeCallStats::Object_DeleteProperty);
    355 
    356     PropertyNormalizationMode mode =
    357         is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
    358 
    359     if (holder->HasFastProperties()) {
    360       JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
    361                                     "DeletingProperty");
    362       ReloadPropertyInformation<false>();
    363     }
    364     // TODO(verwaest): Get rid of the name_ argument.
    365     JSReceiver::DeleteNormalizedProperty(holder, name_, number_);
    366     if (holder->IsJSObject()) {
    367       JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
    368     }
    369   }
    370   state_ = NOT_FOUND;
    371 }
    372 
    373 void LookupIterator::TransitionToAccessorProperty(
    374     Handle<Object> getter, Handle<Object> setter,
    375     PropertyAttributes attributes) {
    376   DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
    377   // Can only be called when the receiver is a JSObject. JSProxy has to be
    378   // handled via a trap. Adding properties to primitive values is not
    379   // observable.
    380   Handle<JSObject> receiver = GetStoreTarget();
    381 
    382   if (!IsElement() && !receiver->map()->is_dictionary_map()) {
    383     Handle<Map> old_map(receiver->map(), isolate_);
    384 
    385     if (!holder_.is_identical_to(receiver)) {
    386       holder_ = receiver;
    387       state_ = NOT_FOUND;
    388     } else if (state_ == INTERCEPTOR) {
    389       LookupInRegularHolder<false>(*old_map, *holder_);
    390     }
    391     int descriptor =
    392         IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound;
    393 
    394     Handle<Map> new_map = Map::TransitionToAccessorProperty(
    395         isolate_, old_map, name_, descriptor, getter, setter, attributes);
    396     bool simple_transition = new_map->GetBackPointer() == receiver->map();
    397     JSObject::MigrateToMap(receiver, new_map);
    398 
    399     if (simple_transition) {
    400       int number = new_map->LastAdded();
    401       number_ = static_cast<uint32_t>(number);
    402       property_details_ = new_map->GetLastDescriptorDetails();
    403       state_ = ACCESSOR;
    404       return;
    405     }
    406 
    407     ReloadPropertyInformation<false>();
    408     if (!new_map->is_dictionary_map()) return;
    409   }
    410 
    411   Handle<AccessorPair> pair;
    412   if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
    413     pair = Handle<AccessorPair>::cast(GetAccessors());
    414     // If the component and attributes are identical, nothing has to be done.
    415     if (pair->Equals(*getter, *setter)) {
    416       if (property_details().attributes() == attributes) {
    417         if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver);
    418         return;
    419       }
    420     } else {
    421       pair = AccessorPair::Copy(pair);
    422       pair->SetComponents(*getter, *setter);
    423     }
    424   } else {
    425     pair = factory()->NewAccessorPair();
    426     pair->SetComponents(*getter, *setter);
    427   }
    428 
    429   TransitionToAccessorPair(pair, attributes);
    430 
    431 #if VERIFY_HEAP
    432   if (FLAG_verify_heap) {
    433     receiver->JSObjectVerify();
    434   }
    435 #endif
    436 }
    437 
    438 
    439 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
    440                                               PropertyAttributes attributes) {
    441   Handle<JSObject> receiver = GetStoreTarget();
    442   holder_ = receiver;
    443 
    444   PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
    445                           PropertyCellType::kMutable);
    446 
    447   if (IsElement()) {
    448     // TODO(verwaest): Move code into the element accessor.
    449     Handle<SeededNumberDictionary> dictionary =
    450         JSObject::NormalizeElements(receiver);
    451 
    452     // We unconditionally pass used_as_prototype=false here because the call
    453     // to RequireSlowElements takes care of the required IC clearing and
    454     // we don't want to walk the heap twice.
    455     dictionary =
    456         SeededNumberDictionary::Set(dictionary, index_, pair, details, false);
    457     receiver->RequireSlowElements(*dictionary);
    458 
    459     if (receiver->HasSlowArgumentsElements()) {
    460       FixedArray* parameter_map = FixedArray::cast(receiver->elements());
    461       uint32_t length = parameter_map->length() - 2;
    462       if (number_ < length) {
    463         parameter_map->set(number_ + 2, heap()->the_hole_value());
    464       }
    465       FixedArray::cast(receiver->elements())->set(1, *dictionary);
    466     } else {
    467       receiver->set_elements(*dictionary);
    468     }
    469 
    470     ReloadPropertyInformation<true>();
    471   } else {
    472     PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
    473                                          ? KEEP_INOBJECT_PROPERTIES
    474                                          : CLEAR_INOBJECT_PROPERTIES;
    475     // Normalize object to make this operation simple.
    476     JSObject::NormalizeProperties(receiver, mode, 0,
    477                                   "TransitionToAccessorPair");
    478 
    479     JSObject::SetNormalizedProperty(receiver, name_, pair, details);
    480     JSObject::ReoptimizeIfPrototype(receiver);
    481 
    482     ReloadPropertyInformation<false>();
    483   }
    484 }
    485 
    486 
    487 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
    488   DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
    489   // Optimization that only works if configuration_ is not mutable.
    490   if (!check_prototype_chain()) return true;
    491   DisallowHeapAllocation no_gc;
    492   if (*receiver_ == *holder_) return true;
    493   if (!receiver_->IsJSReceiver()) return false;
    494   JSReceiver* current = JSReceiver::cast(*receiver_);
    495   JSReceiver* object = *holder_;
    496   if (!current->map()->has_hidden_prototype()) return false;
    497   // JSProxy do not occur as hidden prototypes.
    498   if (object->IsJSProxy()) return false;
    499   PrototypeIterator iter(isolate(), current, kStartAtPrototype,
    500                          PrototypeIterator::END_AT_NON_HIDDEN);
    501   while (!iter.IsAtEnd()) {
    502     if (iter.GetCurrent<JSReceiver>() == object) return true;
    503     iter.Advance();
    504   }
    505   return false;
    506 }
    507 
    508 
    509 Handle<Object> LookupIterator::FetchValue() const {
    510   Object* result = NULL;
    511   if (IsElement()) {
    512     Handle<JSObject> holder = GetHolder<JSObject>();
    513     ElementsAccessor* accessor = holder->GetElementsAccessor();
    514     return accessor->Get(holder, number_);
    515   } else if (holder_->IsJSGlobalObject()) {
    516     Handle<JSObject> holder = GetHolder<JSObject>();
    517     result = holder->global_dictionary()->ValueAt(number_);
    518     DCHECK(result->IsPropertyCell());
    519     result = PropertyCell::cast(result)->value();
    520   } else if (!holder_->HasFastProperties()) {
    521     result = holder_->property_dictionary()->ValueAt(number_);
    522   } else if (property_details_.type() == v8::internal::DATA) {
    523     Handle<JSObject> holder = GetHolder<JSObject>();
    524     FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
    525     return JSObject::FastPropertyAt(holder, property_details_.representation(),
    526                                     field_index);
    527   } else {
    528     result = holder_->map()->instance_descriptors()->GetValue(number_);
    529   }
    530   return handle(result, isolate_);
    531 }
    532 
    533 
    534 int LookupIterator::GetAccessorIndex() const {
    535   DCHECK(has_property_);
    536   DCHECK(holder_->HasFastProperties());
    537   DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
    538   return descriptor_number();
    539 }
    540 
    541 
    542 int LookupIterator::GetConstantIndex() const {
    543   DCHECK(has_property_);
    544   DCHECK(holder_->HasFastProperties());
    545   DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
    546   DCHECK(!IsElement());
    547   return descriptor_number();
    548 }
    549 
    550 
    551 FieldIndex LookupIterator::GetFieldIndex() const {
    552   DCHECK(has_property_);
    553   DCHECK(holder_->HasFastProperties());
    554   DCHECK_EQ(v8::internal::DATA, property_details_.type());
    555   DCHECK(!IsElement());
    556   Map* holder_map = holder_->map();
    557   int index =
    558       holder_map->instance_descriptors()->GetFieldIndex(descriptor_number());
    559   bool is_double = representation().IsDouble();
    560   return FieldIndex::ForPropertyIndex(holder_map, index, is_double);
    561 }
    562 
    563 Handle<FieldType> LookupIterator::GetFieldType() const {
    564   DCHECK(has_property_);
    565   DCHECK(holder_->HasFastProperties());
    566   DCHECK_EQ(v8::internal::DATA, property_details_.type());
    567   return handle(
    568       holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()),
    569       isolate_);
    570 }
    571 
    572 
    573 Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
    574   DCHECK(!IsElement());
    575   Handle<JSObject> holder = GetHolder<JSObject>();
    576   Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(holder);
    577   Object* value = global->global_dictionary()->ValueAt(dictionary_entry());
    578   DCHECK(value->IsPropertyCell());
    579   return handle(PropertyCell::cast(value));
    580 }
    581 
    582 
    583 Handle<Object> LookupIterator::GetAccessors() const {
    584   DCHECK_EQ(ACCESSOR, state_);
    585   return FetchValue();
    586 }
    587 
    588 
    589 Handle<Object> LookupIterator::GetDataValue() const {
    590   DCHECK_EQ(DATA, state_);
    591   Handle<Object> value = FetchValue();
    592   return value;
    593 }
    594 
    595 
    596 void LookupIterator::WriteDataValue(Handle<Object> value) {
    597   DCHECK_EQ(DATA, state_);
    598   Handle<JSReceiver> holder = GetHolder<JSReceiver>();
    599   if (IsElement()) {
    600     Handle<JSObject> object = Handle<JSObject>::cast(holder);
    601     ElementsAccessor* accessor = object->GetElementsAccessor();
    602     accessor->Set(object, number_, *value);
    603   } else if (holder->HasFastProperties()) {
    604     if (property_details_.type() == v8::internal::DATA) {
    605       JSObject::cast(*holder)->WriteToField(descriptor_number(),
    606                                             property_details_, *value);
    607     } else {
    608       DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
    609     }
    610   } else if (holder->IsJSGlobalObject()) {
    611     Handle<GlobalDictionary> property_dictionary =
    612         handle(JSObject::cast(*holder)->global_dictionary());
    613     PropertyCell::UpdateCell(property_dictionary, dictionary_entry(), value,
    614                              property_details_);
    615   } else {
    616     NameDictionary* property_dictionary = holder->property_dictionary();
    617     property_dictionary->ValueAtPut(dictionary_entry(), *value);
    618   }
    619 }
    620 
    621 template <bool is_element>
    622 bool LookupIterator::SkipInterceptor(JSObject* holder) {
    623   auto info = GetInterceptor<is_element>(holder);
    624   // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
    625   if (info->non_masking()) {
    626     switch (interceptor_state_) {
    627       case InterceptorState::kUninitialized:
    628         interceptor_state_ = InterceptorState::kSkipNonMasking;
    629       // Fall through.
    630       case InterceptorState::kSkipNonMasking:
    631         return true;
    632       case InterceptorState::kProcessNonMasking:
    633         return false;
    634     }
    635   }
    636   return interceptor_state_ == InterceptorState::kProcessNonMasking;
    637 }
    638 
    639 JSReceiver* LookupIterator::NextHolder(Map* map) {
    640   DisallowHeapAllocation no_gc;
    641   if (map->prototype() == heap()->null_value()) return NULL;
    642   if (!check_prototype_chain() && !map->has_hidden_prototype()) return NULL;
    643   return JSReceiver::cast(map->prototype());
    644 }
    645 
    646 LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const {
    647   DCHECK(!IsElement());
    648   if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND;
    649 
    650   Handle<String> name_string = Handle<String>::cast(name_);
    651   if (name_string->length() == 0) return NOT_FOUND;
    652 
    653   return IsSpecialIndex(isolate_->unicode_cache(), *name_string)
    654              ? INTEGER_INDEXED_EXOTIC
    655              : NOT_FOUND;
    656 }
    657 
    658 namespace {
    659 
    660 template <bool is_element>
    661 bool HasInterceptor(Map* map) {
    662   return is_element ? map->has_indexed_interceptor()
    663                     : map->has_named_interceptor();
    664 }
    665 
    666 }  // namespace
    667 
    668 template <bool is_element>
    669 LookupIterator::State LookupIterator::LookupInSpecialHolder(
    670     Map* const map, JSReceiver* const holder) {
    671   STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
    672   switch (state_) {
    673     case NOT_FOUND:
    674       if (map->IsJSProxyMap()) {
    675         if (is_element || !name_->IsPrivate()) return JSPROXY;
    676       }
    677       if (map->is_access_check_needed()) {
    678         if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
    679       }
    680     // Fall through.
    681     case ACCESS_CHECK:
    682       if (check_interceptor() && HasInterceptor<is_element>(map) &&
    683           !SkipInterceptor<is_element>(JSObject::cast(holder))) {
    684         if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
    685       }
    686     // Fall through.
    687     case INTERCEPTOR:
    688       if (!is_element && map->IsJSGlobalObjectMap()) {
    689         GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary();
    690         int number = dict->FindEntry(name_);
    691         if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
    692         number_ = static_cast<uint32_t>(number);
    693         DCHECK(dict->ValueAt(number_)->IsPropertyCell());
    694         PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_));
    695         if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND;
    696         property_details_ = cell->property_details();
    697         has_property_ = true;
    698         switch (property_details_.kind()) {
    699           case v8::internal::kData:
    700             return DATA;
    701           case v8::internal::kAccessor:
    702             return ACCESSOR;
    703         }
    704       }
    705       return LookupInRegularHolder<is_element>(map, holder);
    706     case ACCESSOR:
    707     case DATA:
    708       return NOT_FOUND;
    709     case INTEGER_INDEXED_EXOTIC:
    710     case JSPROXY:
    711     case TRANSITION:
    712       UNREACHABLE();
    713   }
    714   UNREACHABLE();
    715   return NOT_FOUND;
    716 }
    717 
    718 template <bool is_element>
    719 LookupIterator::State LookupIterator::LookupInRegularHolder(
    720     Map* const map, JSReceiver* const holder) {
    721   DisallowHeapAllocation no_gc;
    722   if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
    723     return NOT_FOUND;
    724   }
    725 
    726   if (is_element) {
    727     JSObject* js_object = JSObject::cast(holder);
    728     ElementsAccessor* accessor = js_object->GetElementsAccessor();
    729     FixedArrayBase* backing_store = js_object->elements();
    730     number_ = accessor->GetEntryForIndex(js_object, backing_store, index_);
    731     if (number_ == kMaxUInt32) {
    732       return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
    733     }
    734     property_details_ = accessor->GetDetails(js_object, number_);
    735   } else if (!map->is_dictionary_map()) {
    736     DescriptorArray* descriptors = map->instance_descriptors();
    737     int number = descriptors->SearchWithCache(isolate_, *name_, map);
    738     if (number == DescriptorArray::kNotFound) return NotFound(holder);
    739     number_ = static_cast<uint32_t>(number);
    740     property_details_ = descriptors->GetDetails(number_);
    741   } else {
    742     NameDictionary* dict = holder->property_dictionary();
    743     int number = dict->FindEntry(name_);
    744     if (number == NameDictionary::kNotFound) return NotFound(holder);
    745     number_ = static_cast<uint32_t>(number);
    746     property_details_ = dict->DetailsAt(number_);
    747   }
    748   has_property_ = true;
    749   switch (property_details_.kind()) {
    750     case v8::internal::kData:
    751       return DATA;
    752     case v8::internal::kAccessor:
    753       return ACCESSOR;
    754   }
    755 
    756   UNREACHABLE();
    757   return state_;
    758 }
    759 
    760 Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
    761     const {
    762   DCHECK_EQ(ACCESS_CHECK, state_);
    763   DisallowHeapAllocation no_gc;
    764   AccessCheckInfo* access_check_info =
    765       AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
    766   if (access_check_info) {
    767     Object* interceptor = IsElement() ? access_check_info->indexed_interceptor()
    768                                       : access_check_info->named_interceptor();
    769     if (interceptor) {
    770       return handle(InterceptorInfo::cast(interceptor), isolate_);
    771     }
    772   }
    773   return Handle<InterceptorInfo>();
    774 }
    775 
    776 }  // namespace internal
    777 }  // namespace v8
    778