text: // Copyright 2014 the V8 project authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "src/lookup.h"
  
  #include "src/bootstrapper.h"
  #include "src/deoptimizer.h"
  #include "src/elements.h"
  #include "src/isolate-inl.h"
  
  namespace v8 {
  namespace internal {
  
  
  // static
  LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate,
                                                   Handle<Object> receiver,
                                                   Handle<Object> key,
                                                   bool* success,
                                                   Configuration configuration) {
    uint32_t index = 0;
    if (key->ToArrayIndex(&index)) {
      *success = true;
      return LookupIterator(isolate, receiver, index, configuration);
    }
  
    Handle<Name> name;
    *success = Object::ToName(isolate, key).ToHandle(&name);
    if (!*success) {
      DCHECK(isolate->has_pending_exception());
      // Return an unusable dummy.
      return LookupIterator(receiver, isolate->factory()->empty_string());
    }
  
    if (name->AsArrayIndex(&index)) {
      LookupIterator it(isolate, receiver, index, configuration);
      // Here we try to avoid having to rebuild the string later
      // by storing it on the indexed LookupIterator.
      it.name_ = name;
      return it;
    }
  
    return LookupIterator(receiver, name, configuration);
  }
  
  
  void LookupIterator::Next() {
    DCHECK_NE(JSPROXY, state_);
    DCHECK_NE(TRANSITION, state_);
    DisallowHeapAllocation no_gc;
    has_property_ = false;
  
    JSReceiver* holder = *holder_;
    Map* map = *holder_map_;
  
    // Perform lookup on current holder.
    state_ = LookupInHolder(map, holder);
    if (IsFound()) return;
  
    // Continue lookup if lookup on current holder failed.
    do {
      JSReceiver* maybe_holder = NextHolder(map);
      if (maybe_holder == nullptr) {
        if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
          RestartLookupForNonMaskingInterceptors();
          return;
        }
        break;
      }
      holder = maybe_holder;
      map = holder->map();
      state_ = LookupInHolder(map, holder);
    } while (!IsFound());
  
    if (holder != *holder_) {
      holder_ = handle(holder, isolate_);
      holder_map_ = handle(map, isolate_);
    }
  }
  
  
  void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
    state_ = NOT_FOUND;
    interceptor_state_ = interceptor_state;
    property_details_ = PropertyDetails::Empty();
    holder_ = initial_holder_;
    holder_map_ = handle(holder_->map(), isolate_);
    number_ = DescriptorArray::kNotFound;
    Next();
  }
  
  
  // static
  Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
      Isolate* isolate, Handle<Object> receiver, uint32_t index) {
    // Strings are the only objects with properties (only elements) directly on
    // the wrapper. Hence we can skip generating the wrapper for all other cases.
    if (index != kMaxUInt32 && receiver->IsString() &&
        index < static_cast<uint32_t>(String::cast(*receiver)->length())) {
      // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
      // context, ensuring that we don't leak it into JS?
      Handle<JSFunction> constructor = isolate->string_function();
      Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
      Handle<JSValue>::cast(result)->set_value(*receiver);
      return result;
    }
    auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate);
    if (root->IsNull()) {
      unsigned int magic = 0xbbbbbbbb;
      isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
    }
    return Handle<JSReceiver>::cast(root);
  }
  
  
  Handle<Map> LookupIterator::GetReceiverMap() const {
    if (receiver_->IsNumber()) return factory()->heap_number_map();
    return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
  }
  
  
  Handle<JSObject> LookupIterator::GetStoreTarget() const {
    if (receiver_->IsJSGlobalProxy()) {
      PrototypeIterator iter(isolate(), receiver_);
      if (iter.IsAtEnd()) return Handle<JSGlobalProxy>::cast(receiver_);
      return PrototypeIterator::GetCurrent<JSGlobalObject>(iter);
    }
    return Handle<JSObject>::cast(receiver_);
  }
  
  
  bool LookupIterator::HasAccess() const {
    DCHECK_EQ(ACCESS_CHECK, state_);
    return isolate_->MayAccess(handle(isolate_->context()),
                               GetHolder<JSObject>());
  }
  
  
  void LookupIterator::ReloadPropertyInformation() {
    state_ = BEFORE_PROPERTY;
    interceptor_state_ = InterceptorState::kUninitialized;
    state_ = LookupInHolder(*holder_map_, *holder_);
    DCHECK(IsFound() || holder_map_->is_dictionary_map());
  }
  
  
  void LookupIterator::ReloadHolderMap() {
    DCHECK_EQ(DATA, state_);
    DCHECK(IsElement());
    DCHECK(JSObject::cast(*holder_)->HasFixedTypedArrayElements());
    if (*holder_map_ != holder_->map()) {
      holder_map_ = handle(holder_->map(), isolate_);
    }
  }
  
  
  void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
    DCHECK(state_ == DATA || state_ == ACCESSOR);
    DCHECK(HolderIsReceiverOrHiddenPrototype());
  
    Handle<JSObject> holder = GetHolder<JSObject>();
  
    if (IsElement()) {
      ElementsKind kind = holder_map_->elements_kind();
      ElementsKind to = value->OptimalElementsKind();
      if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
      to = GetMoreGeneralElementsKind(kind, to);
      JSObject::TransitionElementsKind(holder, to);
      holder_map_ = handle(holder->map(), isolate_);
  
      // Copy the backing store if it is copy-on-write.
      if (IsFastSmiOrObjectElementsKind(to)) {
        JSObject::EnsureWritableFastElements(holder);
      }
  
    } else {
      if (holder_map_->is_dictionary_map()) return;
      holder_map_ =
          Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
    }
  
    JSObject::MigrateToMap(holder, holder_map_);
    ReloadPropertyInformation();
  }
  
  
  void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
                                               PropertyAttributes attributes) {
    DCHECK(state_ == DATA || state_ == ACCESSOR);
    DCHECK(HolderIsReceiverOrHiddenPrototype());
    Handle<JSObject> holder = GetHolder<JSObject>();
    if (IsElement()) {
      DCHECK(!holder->HasFixedTypedArrayElements());
      DCHECK(attributes != NONE || !holder->HasFastElements());
      Handle<FixedArrayBase> elements(holder->elements());
      holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
                                                 attributes);
    } else if (holder_map_->is_dictionary_map()) {
      PropertyDetails details(attributes, v8::internal::DATA, 0,
                              PropertyCellType::kMutable);
      JSObject::SetNormalizedProperty(holder, name(), value, details);
    } else {
      holder_map_ = Map::ReconfigureExistingProperty(
          holder_map_, descriptor_number(), i::kData, attributes);
      holder_map_ =
          Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
      JSObject::MigrateToMap(holder, holder_map_);
    }
  
    ReloadPropertyInformation();
    WriteDataValue(value);
  
  #if VERIFY_HEAP
    if (FLAG_verify_heap) {
      holder->JSObjectVerify();
    }
  #endif
  }
  
  
  void LookupIterator::PrepareTransitionToDataProperty(
      Handle<Object> value, PropertyAttributes attributes,
      Object::StoreFromKeyed store_mode) {
    if (state_ == TRANSITION) return;
    DCHECK(state_ != LookupIterator::ACCESSOR ||
           (GetAccessors()->IsAccessorInfo() &&
            AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
    DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
    DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
    // Can only be called when the receiver is a JSObject. JSProxy has to be
    // handled via a trap. Adding properties to primitive values is not
    // observable.
    Handle<JSObject> receiver = GetStoreTarget();
  
    if (!isolate()->IsInternallyUsedPropertyName(name()) &&
        !receiver->map()->is_extensible()) {
      return;
    }
  
    auto transition = Map::TransitionToDataProperty(
        handle(receiver->map(), isolate_), name_, value, attributes, store_mode);
    state_ = TRANSITION;
    transition_ = transition;
  
    if (receiver->IsJSGlobalObject()) {
      // Install a property cell.
      InternalizeName();
      auto cell = JSGlobalObject::EnsurePropertyCell(
          Handle<JSGlobalObject>::cast(receiver), name());
      DCHECK(cell->value()->IsTheHole());
      transition_ = cell;
    } else if (!transition->is_dictionary_map()) {
      property_details_ = transition->GetLastDescriptorDetails();
      has_property_ = true;
    }
  }
  
  
  void LookupIterator::ApplyTransitionToDataProperty() {
    DCHECK_EQ(TRANSITION, state_);
  
    Handle<JSObject> receiver = GetStoreTarget();
    if (receiver->IsJSGlobalObject()) return;
    holder_ = receiver;
    holder_map_ = transition_map();
    JSObject::MigrateToMap(receiver, holder_map_);
    ReloadPropertyInformation();
  }
  
  
  void LookupIterator::Delete() {
    Handle<JSObject> holder = Handle<JSObject>::cast(holder_);
    if (IsElement()) {
      ElementsAccessor* accessor = holder->GetElementsAccessor();
      accessor->Delete(holder, number_);
    } else {
      PropertyNormalizationMode mode = holder->map()->is_prototype_map()
                                           ? KEEP_INOBJECT_PROPERTIES
                                           : CLEAR_INOBJECT_PROPERTIES;
  
      if (holder->HasFastProperties()) {
        JSObject::NormalizeProperties(holder, mode, 0, "DeletingProperty");
        holder_map_ = handle(holder->map(), isolate_);
        ReloadPropertyInformation();
      }
      // TODO(verwaest): Get rid of the name_ argument.
      JSObject::DeleteNormalizedProperty(holder, name_, number_);
      JSObject::ReoptimizeIfPrototype(holder);
    }
  }
  
  
  void LookupIterator::TransitionToAccessorProperty(
      AccessorComponent component, Handle<Object> accessor,
      PropertyAttributes attributes) {
    DCHECK(!accessor->IsNull());
    // Can only be called when the receiver is a JSObject. JSProxy has to be
    // handled via a trap. Adding properties to primitive values is not
    // observable.
    Handle<JSObject> receiver = GetStoreTarget();
  
    if (!IsElement() && !receiver->map()->is_dictionary_map()) {
      holder_ = receiver;
      holder_map_ = Map::TransitionToAccessorProperty(
          handle(receiver->map(), isolate_), name_, component, accessor,
          attributes);
      JSObject::MigrateToMap(receiver, holder_map_);
  
      ReloadPropertyInformation();
  
      if (!holder_map_->is_dictionary_map()) return;
    }
  
    Handle<AccessorPair> pair;
    if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
      pair = Handle<AccessorPair>::cast(GetAccessors());
      // If the component and attributes are identical, nothing has to be done.
      if (pair->get(component) == *accessor) {
        if (property_details().attributes() == attributes) return;
      } else {
        pair = AccessorPair::Copy(pair);
        pair->set(component, *accessor);
      }
    } else {
      pair = factory()->NewAccessorPair();
      pair->set(component, *accessor);
    }
  
    TransitionToAccessorPair(pair, attributes);
  
  #if VERIFY_HEAP
    if (FLAG_verify_heap) {
      receiver->JSObjectVerify();
    }
  #endif
  }
  
  
  void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
                                                PropertyAttributes attributes) {
    Handle<JSObject> receiver = GetStoreTarget();
    holder_ = receiver;
  
    PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
                            PropertyCellType::kMutable);
  
    if (IsElement()) {
      // TODO(verwaest): Move code into the element accessor.
      Handle<SeededNumberDictionary> dictionary =
          JSObject::NormalizeElements(receiver);
  
      // We unconditionally pass used_as_prototype=false here because the call
      // to RequireSlowElements takes care of the required IC clearing and
      // we don't want to walk the heap twice.
      dictionary =
          SeededNumberDictionary::Set(dictionary, index_, pair, details, false);
      receiver->RequireSlowElements(*dictionary);
  
      if (receiver->HasSlowArgumentsElements()) {
        FixedArray* parameter_map = FixedArray::cast(receiver->elements());
        uint32_t length = parameter_map->length() - 2;
        if (number_ < length) {
          parameter_map->set(number_ + 2, heap()->the_hole_value());
        }
        FixedArray::cast(receiver->elements())->set(1, *dictionary);
      } else {
        receiver->set_elements(*dictionary);
      }
    } else {
      PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
                                           ? KEEP_INOBJECT_PROPERTIES
                                           : CLEAR_INOBJECT_PROPERTIES;
      // Normalize object to make this operation simple.
      JSObject::NormalizeProperties(receiver, mode, 0,
                                    "TransitionToAccessorPair");
  
      JSObject::SetNormalizedProperty(receiver, name_, pair, details);
      JSObject::ReoptimizeIfPrototype(receiver);
    }
  
    holder_map_ = handle(receiver->map(), isolate_);
    ReloadPropertyInformation();
  }
  
  
  bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
    DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
    return InternalHolderIsReceiverOrHiddenPrototype();
  }
  
  bool LookupIterator::InternalHolderIsReceiverOrHiddenPrototype() const {
    // Optimization that only works if configuration_ is not mutable.
    if (!check_prototype_chain()) return true;
    DisallowHeapAllocation no_gc;
    if (!receiver_->IsJSReceiver()) return false;
    Object* current = *receiver_;
    JSReceiver* holder = *holder_;
    // JSProxy do not occur as hidden prototypes.
    if (current->IsJSProxy()) {
      return JSReceiver::cast(current) == holder;
    }
    PrototypeIterator iter(isolate(), current,
                           PrototypeIterator::START_AT_RECEIVER);
    do {
      if (iter.GetCurrent<JSReceiver>() == holder) return true;
      DCHECK(!current->IsJSProxy());
      iter.Advance();
    } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
    return false;
  }
  
  
  Handle<Object> LookupIterator::FetchValue() const {
    Object* result = NULL;
    Handle<JSObject> holder = GetHolder<JSObject>();
    if (IsElement()) {
      // TODO(verwaest): Optimize.
      if (holder->IsStringObjectWithCharacterAt(index_)) {
        Handle<JSValue> js_value = Handle<JSValue>::cast(holder);
        Handle<String> string(String::cast(js_value->value()));
        return factory()->LookupSingleCharacterStringFromCode(
            String::Flatten(string)->Get(index_));
      }
  
      ElementsAccessor* accessor = holder->GetElementsAccessor();
      return accessor->Get(handle(holder->elements()), number_);
    } else if (holder_map_->IsJSGlobalObjectMap()) {
      result = holder->global_dictionary()->ValueAt(number_);
      DCHECK(result->IsPropertyCell());
      result = PropertyCell::cast(result)->value();
    } else if (holder_map_->is_dictionary_map()) {
      result = holder->property_dictionary()->ValueAt(number_);
    } else if (property_details_.type() == v8::internal::DATA) {
      FieldIndex field_index = FieldIndex::ForDescriptor(*holder_map_, number_);
      return JSObject::FastPropertyAt(holder, property_details_.representation(),
                                      field_index);
    } else {
      result = holder_map_->instance_descriptors()->GetValue(number_);
    }
    return handle(result, isolate_);
  }
  
  
  int LookupIterator::GetAccessorIndex() const {
    DCHECK(has_property_);
    DCHECK(!holder_map_->is_dictionary_map());
    DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
    return descriptor_number();
  }
  
  
  int LookupIterator::GetConstantIndex() const {
    DCHECK(has_property_);
    DCHECK(!holder_map_->is_dictionary_map());
    DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
    DCHECK(!IsElement());
    return descriptor_number();
  }
  
  
  FieldIndex LookupIterator::GetFieldIndex() const {
    DCHECK(has_property_);
    DCHECK(!holder_map_->is_dictionary_map());
    DCHECK_EQ(v8::internal::DATA, property_details_.type());
    DCHECK(!IsElement());
    int index =
        holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number());
    bool is_double = representation().IsDouble();
    return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double);
  }
  
  
  Handle<HeapType> LookupIterator::GetFieldType() const {
    DCHECK(has_property_);
    DCHECK(!holder_map_->is_dictionary_map());
    DCHECK_EQ(v8::internal::DATA, property_details_.type());
    return handle(
        holder_map_->instance_descriptors()->GetFieldType(descriptor_number()),
        isolate_);
  }
  
  
  Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
    DCHECK(!IsElement());
    Handle<JSObject> holder = GetHolder<JSObject>();
    Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(holder);
    Object* value = global->global_dictionary()->ValueAt(dictionary_entry());
    DCHECK(value->IsPropertyCell());
    return handle(PropertyCell::cast(value));
  }
  
  
  Handle<Object> LookupIterator::GetAccessors() const {
    DCHECK_EQ(ACCESSOR, state_);
    return FetchValue();
  }
  
  
  Handle<Object> LookupIterator::GetDataValue() const {
    DCHECK_EQ(DATA, state_);
    Handle<Object> value = FetchValue();
    return value;
  }
  
  
  void LookupIterator::WriteDataValue(Handle<Object> value) {
    DCHECK_EQ(DATA, state_);
    Handle<JSObject> holder = GetHolder<JSObject>();
    if (IsElement()) {
      ElementsAccessor* accessor = holder->GetElementsAccessor();
      accessor->Set(holder->elements(), number_, *value);
    } else if (holder->IsJSGlobalObject()) {
      Handle<GlobalDictionary> property_dictionary =
          handle(holder->global_dictionary());
      PropertyCell::UpdateCell(property_dictionary, dictionary_entry(), value,
                               property_details_);
    } else if (holder_map_->is_dictionary_map()) {
      NameDictionary* property_dictionary = holder->property_dictionary();
      property_dictionary->ValueAtPut(dictionary_entry(), *value);
    } else if (property_details_.type() == v8::internal::DATA) {
      holder->WriteToField(descriptor_number(), *value);
    } else {
      DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
    }
  }
  
  
  bool LookupIterator::IsIntegerIndexedExotic(JSReceiver* holder) {
    DCHECK(exotic_index_state_ != ExoticIndexState::kNotExotic);
    if (exotic_index_state_ == ExoticIndexState::kExotic) return true;
    if (!InternalHolderIsReceiverOrHiddenPrototype()) {
      exotic_index_state_ = ExoticIndexState::kNotExotic;
      return false;
    }
    DCHECK(exotic_index_state_ == ExoticIndexState::kUninitialized);
    bool result = false;
    // Compute and cache result.
    if (IsElement()) {
      result = index_ >= JSTypedArray::cast(holder)->length_value();
    } else if (name()->IsString()) {
      Handle<String> name_string = Handle<String>::cast(name());
      if (name_string->length() != 0) {
        result = IsSpecialIndex(isolate_->unicode_cache(), *name_string);
      }
    }
    exotic_index_state_ =
        result ? ExoticIndexState::kExotic : ExoticIndexState::kNotExotic;
    return result;
  }
  
  
  void LookupIterator::InternalizeName() {
    if (name_->IsUniqueName()) return;
    name_ = factory()->InternalizeString(Handle<String>::cast(name_));
  }
  
  
  bool LookupIterator::HasInterceptor(Map* map) const {
    if (IsElement()) return map->has_indexed_interceptor();
    return map->has_named_interceptor();
  }
  
  
  bool LookupIterator::SkipInterceptor(JSObject* holder) {
    auto info = GetInterceptor(holder);
    // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
    if (info->non_masking()) {
      switch (interceptor_state_) {
        case InterceptorState::kUninitialized:
          interceptor_state_ = InterceptorState::kSkipNonMasking;
        // Fall through.
        case InterceptorState::kSkipNonMasking:
          return true;
        case InterceptorState::kProcessNonMasking:
          return false;
      }
    }
    return interceptor_state_ == InterceptorState::kProcessNonMasking;
  }
  
  
  JSReceiver* LookupIterator::NextHolder(Map* map) {
    DisallowHeapAllocation no_gc;
    if (!map->prototype()->IsJSReceiver()) return NULL;
  
    JSReceiver* next = JSReceiver::cast(map->prototype());
    DCHECK(!next->map()->IsJSGlobalObjectMap() ||
           next->map()->is_hidden_prototype());
  
    if (!check_prototype_chain() &&
        !(check_hidden() && next->map()->is_hidden_prototype()) &&
        // Always lookup behind the JSGlobalProxy into the JSGlobalObject, even
        // when not checking other hidden prototypes.
        !map->IsJSGlobalProxyMap()) {
      return NULL;
    }
  
    return next;
  }
  
  
  LookupIterator::State LookupIterator::LookupInHolder(Map* const map,
                                                       JSReceiver* const holder) {
    STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
    DisallowHeapAllocation no_gc;
    if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
      return LookupNonMaskingInterceptorInHolder(map, holder);
    }
    switch (state_) {
      case NOT_FOUND:
        if (map->IsJSProxyMap()) return JSPROXY;
        if (map->is_access_check_needed() &&
            (IsElement() || !isolate_->IsInternallyUsedPropertyName(name_))) {
          return ACCESS_CHECK;
        }
      // Fall through.
      case ACCESS_CHECK:
        if (exotic_index_state_ != ExoticIndexState::kNotExotic &&
            holder->IsJSTypedArray() && IsIntegerIndexedExotic(holder)) {
          return INTEGER_INDEXED_EXOTIC;
        }
        if (check_interceptor() && HasInterceptor(map) &&
            !SkipInterceptor(JSObject::cast(holder))) {
          return INTERCEPTOR;
        }
      // Fall through.
      case INTERCEPTOR:
        if (IsElement()) {
          // TODO(verwaest): Optimize.
          if (holder->IsStringObjectWithCharacterAt(index_)) {
            PropertyAttributes attributes =
                static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
            property_details_ = PropertyDetails(attributes, v8::internal::DATA, 0,
                                                PropertyCellType::kNoCell);
          } else {
            JSObject* js_object = JSObject::cast(holder);
            if (js_object->elements() == isolate()->heap()->empty_fixed_array()) {
              return NOT_FOUND;
            }
  
            ElementsAccessor* accessor = js_object->GetElementsAccessor();
            FixedArrayBase* backing_store = js_object->elements();
            number_ =
                accessor->GetEntryForIndex(js_object, backing_store, index_);
            if (number_ == kMaxUInt32) return NOT_FOUND;
            property_details_ = accessor->GetDetails(backing_store, number_);
          }
        } else if (!map->is_dictionary_map()) {
          DescriptorArray* descriptors = map->instance_descriptors();
          int number = descriptors->SearchWithCache(*name_, map);
          if (number == DescriptorArray::kNotFound) return NOT_FOUND;
          number_ = static_cast<uint32_t>(number);
          property_details_ = descriptors->GetDetails(number_);
        } else if (map->IsJSGlobalObjectMap()) {
          GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary();
          int number = dict->FindEntry(name_);
          if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
          number_ = static_cast<uint32_t>(number);
          DCHECK(dict->ValueAt(number_)->IsPropertyCell());
          PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_));
          if (cell->value()->IsTheHole()) return NOT_FOUND;
          property_details_ = cell->property_details();
        } else {
          NameDictionary* dict = JSObject::cast(holder)->property_dictionary();
          int number = dict->FindEntry(name_);
          if (number == NameDictionary::kNotFound) return NOT_FOUND;
          number_ = static_cast<uint32_t>(number);
          property_details_ = dict->DetailsAt(number_);
        }
        has_property_ = true;
        switch (property_details_.kind()) {
          case v8::internal::kData:
            return DATA;
          case v8::internal::kAccessor:
            return ACCESSOR;
        }
      case ACCESSOR:
      case DATA:
        return NOT_FOUND;
      case INTEGER_INDEXED_EXOTIC:
      case JSPROXY:
      case TRANSITION:
        UNREACHABLE();
    }
    UNREACHABLE();
    return state_;
  }
  
  
  LookupIterator::State LookupIterator::LookupNonMaskingInterceptorInHolder(
      Map* const map, JSReceiver* const holder) {
    switch (state_) {
      case NOT_FOUND:
        if (check_interceptor() && HasInterceptor(map) &&
            !SkipInterceptor(JSObject::cast(holder))) {
          return INTERCEPTOR;
        }
      // Fall through.
      default:
        return NOT_FOUND;
    }
    UNREACHABLE();
    return state_;
  }
  
  }  // namespace internal
  }  // namespace v8
  