lookup.cc revision f2e3994fa5148cc3d9946666f0b0596290192b0e
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/isolate-inl.h"
11
12namespace v8 {
13namespace internal {
14
15
16// static
17LookupIterator 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
48void LookupIterator::Next() {
49  DCHECK_NE(JSPROXY, state_);
50  DCHECK_NE(TRANSITION, state_);
51  DisallowHeapAllocation no_gc;
52  has_property_ = false;
53
54  JSReceiver* holder = *holder_;
55  Map* map = *holder_map_;
56
57  // Perform lookup on current holder.
58  state_ = LookupInHolder(map, holder);
59  if (IsFound()) return;
60
61  // Continue lookup if lookup on current holder failed.
62  do {
63    JSReceiver* maybe_holder = NextHolder(map);
64    if (maybe_holder == nullptr) {
65      if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
66        RestartLookupForNonMaskingInterceptors();
67        return;
68      }
69      break;
70    }
71    holder = maybe_holder;
72    map = holder->map();
73    state_ = LookupInHolder(map, holder);
74  } while (!IsFound());
75
76  if (holder != *holder_) {
77    holder_ = handle(holder, isolate_);
78    holder_map_ = handle(map, isolate_);
79  }
80}
81
82
83void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
84  state_ = NOT_FOUND;
85  interceptor_state_ = interceptor_state;
86  property_details_ = PropertyDetails::Empty();
87  holder_ = initial_holder_;
88  holder_map_ = handle(holder_->map(), isolate_);
89  number_ = DescriptorArray::kNotFound;
90  Next();
91}
92
93
94// static
95Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
96    Isolate* isolate, Handle<Object> receiver, uint32_t index) {
97  // Strings are the only objects with properties (only elements) directly on
98  // the wrapper. Hence we can skip generating the wrapper for all other cases.
99  if (index != kMaxUInt32 && receiver->IsString() &&
100      index < static_cast<uint32_t>(String::cast(*receiver)->length())) {
101    // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native
102    // context, ensuring that we don't leak it into JS?
103    Handle<JSFunction> constructor = isolate->string_function();
104    Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
105    Handle<JSValue>::cast(result)->set_value(*receiver);
106    return result;
107  }
108  auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate);
109  if (root->IsNull()) {
110    unsigned int magic = 0xbbbbbbbb;
111    isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
112  }
113  return Handle<JSReceiver>::cast(root);
114}
115
116
117Handle<Map> LookupIterator::GetReceiverMap() const {
118  if (receiver_->IsNumber()) return factory()->heap_number_map();
119  return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
120}
121
122
123Handle<JSObject> LookupIterator::GetStoreTarget() const {
124  if (receiver_->IsJSGlobalProxy()) {
125    PrototypeIterator iter(isolate(), receiver_);
126    if (iter.IsAtEnd()) return Handle<JSGlobalProxy>::cast(receiver_);
127    return PrototypeIterator::GetCurrent<JSGlobalObject>(iter);
128  }
129  return Handle<JSObject>::cast(receiver_);
130}
131
132
133bool LookupIterator::HasAccess() const {
134  DCHECK_EQ(ACCESS_CHECK, state_);
135  return isolate_->MayAccess(handle(isolate_->context()),
136                             GetHolder<JSObject>());
137}
138
139
140void LookupIterator::ReloadPropertyInformation() {
141  state_ = BEFORE_PROPERTY;
142  interceptor_state_ = InterceptorState::kUninitialized;
143  state_ = LookupInHolder(*holder_map_, *holder_);
144  DCHECK(IsFound() || holder_map_->is_dictionary_map());
145}
146
147
148void LookupIterator::ReloadHolderMap() {
149  DCHECK_EQ(DATA, state_);
150  DCHECK(IsElement());
151  DCHECK(JSObject::cast(*holder_)->HasFixedTypedArrayElements());
152  if (*holder_map_ != holder_->map()) {
153    holder_map_ = handle(holder_->map(), isolate_);
154  }
155}
156
157
158void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
159  DCHECK(state_ == DATA || state_ == ACCESSOR);
160  DCHECK(HolderIsReceiverOrHiddenPrototype());
161
162  Handle<JSObject> holder = GetHolder<JSObject>();
163
164  if (IsElement()) {
165    ElementsKind kind = holder_map_->elements_kind();
166    ElementsKind to = value->OptimalElementsKind();
167    if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
168    to = GetMoreGeneralElementsKind(kind, to);
169    JSObject::TransitionElementsKind(holder, to);
170    holder_map_ = handle(holder->map(), isolate_);
171
172    // Copy the backing store if it is copy-on-write.
173    if (IsFastSmiOrObjectElementsKind(to)) {
174      JSObject::EnsureWritableFastElements(holder);
175    }
176
177  } else {
178    if (holder_map_->is_dictionary_map()) return;
179    holder_map_ =
180        Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
181  }
182
183  JSObject::MigrateToMap(holder, holder_map_);
184  ReloadPropertyInformation();
185}
186
187
188void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
189                                             PropertyAttributes attributes) {
190  DCHECK(state_ == DATA || state_ == ACCESSOR);
191  DCHECK(HolderIsReceiverOrHiddenPrototype());
192  Handle<JSObject> holder = GetHolder<JSObject>();
193  if (IsElement()) {
194    DCHECK(!holder->HasFixedTypedArrayElements());
195    DCHECK(attributes != NONE || !holder->HasFastElements());
196    Handle<FixedArrayBase> elements(holder->elements());
197    holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
198                                               attributes);
199  } else if (holder_map_->is_dictionary_map()) {
200    PropertyDetails details(attributes, v8::internal::DATA, 0,
201                            PropertyCellType::kMutable);
202    JSObject::SetNormalizedProperty(holder, name(), value, details);
203  } else {
204    holder_map_ = Map::ReconfigureExistingProperty(
205        holder_map_, descriptor_number(), i::kData, attributes);
206    holder_map_ =
207        Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
208    JSObject::MigrateToMap(holder, holder_map_);
209  }
210
211  ReloadPropertyInformation();
212  WriteDataValue(value);
213
214#if VERIFY_HEAP
215  if (FLAG_verify_heap) {
216    holder->JSObjectVerify();
217  }
218#endif
219}
220
221
222void LookupIterator::PrepareTransitionToDataProperty(
223    Handle<Object> value, PropertyAttributes attributes,
224    Object::StoreFromKeyed store_mode) {
225  if (state_ == TRANSITION) return;
226  DCHECK(state_ != LookupIterator::ACCESSOR ||
227         (GetAccessors()->IsAccessorInfo() &&
228          AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
229  DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
230  DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
231  // Can only be called when the receiver is a JSObject. JSProxy has to be
232  // handled via a trap. Adding properties to primitive values is not
233  // observable.
234  Handle<JSObject> receiver = GetStoreTarget();
235
236  if (!isolate()->IsInternallyUsedPropertyName(name()) &&
237      !receiver->map()->is_extensible()) {
238    return;
239  }
240
241  auto transition = Map::TransitionToDataProperty(
242      handle(receiver->map(), isolate_), name_, value, attributes, store_mode);
243  state_ = TRANSITION;
244  transition_ = transition;
245
246  if (receiver->IsJSGlobalObject()) {
247    // Install a property cell.
248    InternalizeName();
249    auto cell = JSGlobalObject::EnsurePropertyCell(
250        Handle<JSGlobalObject>::cast(receiver), name());
251    DCHECK(cell->value()->IsTheHole());
252    transition_ = cell;
253  } else if (!transition->is_dictionary_map()) {
254    property_details_ = transition->GetLastDescriptorDetails();
255    has_property_ = true;
256  }
257}
258
259
260void LookupIterator::ApplyTransitionToDataProperty() {
261  DCHECK_EQ(TRANSITION, state_);
262
263  Handle<JSObject> receiver = GetStoreTarget();
264  if (receiver->IsJSGlobalObject()) return;
265  holder_ = receiver;
266  holder_map_ = transition_map();
267  JSObject::MigrateToMap(receiver, holder_map_);
268  ReloadPropertyInformation();
269}
270
271
272void LookupIterator::Delete() {
273  Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
274  if (IsElement()) {
275    Handle<JSObject> object = Handle<JSObject>::cast(holder);
276    ElementsAccessor* accessor = object->GetElementsAccessor();
277    accessor->Delete(object, number_);
278  } else {
279    PropertyNormalizationMode mode = holder->map()->is_prototype_map()
280                                         ? KEEP_INOBJECT_PROPERTIES
281                                         : CLEAR_INOBJECT_PROPERTIES;
282
283    if (holder->HasFastProperties()) {
284      JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
285                                    "DeletingProperty");
286      holder_map_ = handle(holder->map(), isolate_);
287      ReloadPropertyInformation();
288    }
289    // TODO(verwaest): Get rid of the name_ argument.
290    JSReceiver::DeleteNormalizedProperty(holder, name_, number_);
291    if (holder->IsJSObject()) {
292      JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
293    }
294  }
295}
296
297
298void LookupIterator::TransitionToAccessorProperty(
299    AccessorComponent component, Handle<Object> accessor,
300    PropertyAttributes attributes) {
301  DCHECK(!accessor->IsNull());
302  // Can only be called when the receiver is a JSObject. JSProxy has to be
303  // handled via a trap. Adding properties to primitive values is not
304  // observable.
305  Handle<JSObject> receiver = GetStoreTarget();
306
307  if (!IsElement() && !receiver->map()->is_dictionary_map()) {
308    holder_ = receiver;
309    holder_map_ = Map::TransitionToAccessorProperty(
310        handle(receiver->map(), isolate_), name_, component, accessor,
311        attributes);
312    JSObject::MigrateToMap(receiver, holder_map_);
313
314    ReloadPropertyInformation();
315
316    if (!holder_map_->is_dictionary_map()) return;
317  }
318
319  Handle<AccessorPair> pair;
320  if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
321    pair = Handle<AccessorPair>::cast(GetAccessors());
322    // If the component and attributes are identical, nothing has to be done.
323    if (pair->get(component) == *accessor) {
324      if (property_details().attributes() == attributes) return;
325    } else {
326      pair = AccessorPair::Copy(pair);
327      pair->set(component, *accessor);
328    }
329  } else {
330    pair = factory()->NewAccessorPair();
331    pair->set(component, *accessor);
332  }
333
334  TransitionToAccessorPair(pair, attributes);
335
336#if VERIFY_HEAP
337  if (FLAG_verify_heap) {
338    receiver->JSObjectVerify();
339  }
340#endif
341}
342
343
344void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
345                                              PropertyAttributes attributes) {
346  Handle<JSObject> receiver = GetStoreTarget();
347  holder_ = receiver;
348
349  PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
350                          PropertyCellType::kMutable);
351
352  if (IsElement()) {
353    // TODO(verwaest): Move code into the element accessor.
354    Handle<SeededNumberDictionary> dictionary =
355        JSObject::NormalizeElements(receiver);
356
357    // We unconditionally pass used_as_prototype=false here because the call
358    // to RequireSlowElements takes care of the required IC clearing and
359    // we don't want to walk the heap twice.
360    dictionary =
361        SeededNumberDictionary::Set(dictionary, index_, pair, details, false);
362    receiver->RequireSlowElements(*dictionary);
363
364    if (receiver->HasSlowArgumentsElements()) {
365      FixedArray* parameter_map = FixedArray::cast(receiver->elements());
366      uint32_t length = parameter_map->length() - 2;
367      if (number_ < length) {
368        parameter_map->set(number_ + 2, heap()->the_hole_value());
369      }
370      FixedArray::cast(receiver->elements())->set(1, *dictionary);
371    } else {
372      receiver->set_elements(*dictionary);
373    }
374  } else {
375    PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
376                                         ? KEEP_INOBJECT_PROPERTIES
377                                         : CLEAR_INOBJECT_PROPERTIES;
378    // Normalize object to make this operation simple.
379    JSObject::NormalizeProperties(receiver, mode, 0,
380                                  "TransitionToAccessorPair");
381
382    JSObject::SetNormalizedProperty(receiver, name_, pair, details);
383    JSObject::ReoptimizeIfPrototype(receiver);
384  }
385
386  holder_map_ = handle(receiver->map(), isolate_);
387  ReloadPropertyInformation();
388}
389
390
391bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
392  DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
393  return InternalHolderIsReceiverOrHiddenPrototype();
394}
395
396bool LookupIterator::InternalHolderIsReceiverOrHiddenPrototype() const {
397  // Optimization that only works if configuration_ is not mutable.
398  if (!check_prototype_chain()) return true;
399  DisallowHeapAllocation no_gc;
400  if (!receiver_->IsJSReceiver()) return false;
401  Object* current = *receiver_;
402  JSReceiver* holder = *holder_;
403  // JSProxy do not occur as hidden prototypes.
404  if (current->IsJSProxy()) {
405    return JSReceiver::cast(current) == holder;
406  }
407  PrototypeIterator iter(isolate(), current,
408                         PrototypeIterator::START_AT_RECEIVER);
409  do {
410    if (iter.GetCurrent<JSReceiver>() == holder) return true;
411    DCHECK(!current->IsJSProxy());
412    iter.Advance();
413  } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
414  return false;
415}
416
417
418Handle<Object> LookupIterator::FetchValue() const {
419  Object* result = NULL;
420  if (IsElement()) {
421    Handle<JSObject> holder = GetHolder<JSObject>();
422    // TODO(verwaest): Optimize.
423    if (holder->IsStringObjectWithCharacterAt(index_)) {
424      Handle<JSValue> js_value = Handle<JSValue>::cast(holder);
425      Handle<String> string(String::cast(js_value->value()));
426      return factory()->LookupSingleCharacterStringFromCode(
427          String::Flatten(string)->Get(index_));
428    }
429
430    ElementsAccessor* accessor = holder->GetElementsAccessor();
431    return accessor->Get(handle(holder->elements()), number_);
432  } else if (holder_map_->IsJSGlobalObjectMap()) {
433    Handle<JSObject> holder = GetHolder<JSObject>();
434    result = holder->global_dictionary()->ValueAt(number_);
435    DCHECK(result->IsPropertyCell());
436    result = PropertyCell::cast(result)->value();
437  } else if (holder_map_->is_dictionary_map()) {
438    result = holder_->property_dictionary()->ValueAt(number_);
439  } else if (property_details_.type() == v8::internal::DATA) {
440    Handle<JSObject> holder = GetHolder<JSObject>();
441    FieldIndex field_index = FieldIndex::ForDescriptor(*holder_map_, number_);
442    return JSObject::FastPropertyAt(holder, property_details_.representation(),
443                                    field_index);
444  } else {
445    result = holder_map_->instance_descriptors()->GetValue(number_);
446  }
447  return handle(result, isolate_);
448}
449
450
451int LookupIterator::GetAccessorIndex() const {
452  DCHECK(has_property_);
453  DCHECK(!holder_map_->is_dictionary_map());
454  DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
455  return descriptor_number();
456}
457
458
459int LookupIterator::GetConstantIndex() const {
460  DCHECK(has_property_);
461  DCHECK(!holder_map_->is_dictionary_map());
462  DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
463  DCHECK(!IsElement());
464  return descriptor_number();
465}
466
467
468FieldIndex LookupIterator::GetFieldIndex() const {
469  DCHECK(has_property_);
470  DCHECK(!holder_map_->is_dictionary_map());
471  DCHECK_EQ(v8::internal::DATA, property_details_.type());
472  DCHECK(!IsElement());
473  int index =
474      holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number());
475  bool is_double = representation().IsDouble();
476  return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double);
477}
478
479
480Handle<HeapType> LookupIterator::GetFieldType() const {
481  DCHECK(has_property_);
482  DCHECK(!holder_map_->is_dictionary_map());
483  DCHECK_EQ(v8::internal::DATA, property_details_.type());
484  return handle(
485      holder_map_->instance_descriptors()->GetFieldType(descriptor_number()),
486      isolate_);
487}
488
489
490Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
491  DCHECK(!IsElement());
492  Handle<JSObject> holder = GetHolder<JSObject>();
493  Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(holder);
494  Object* value = global->global_dictionary()->ValueAt(dictionary_entry());
495  DCHECK(value->IsPropertyCell());
496  return handle(PropertyCell::cast(value));
497}
498
499
500Handle<Object> LookupIterator::GetAccessors() const {
501  DCHECK_EQ(ACCESSOR, state_);
502  return FetchValue();
503}
504
505
506Handle<Object> LookupIterator::GetDataValue() const {
507  DCHECK_EQ(DATA, state_);
508  Handle<Object> value = FetchValue();
509  return value;
510}
511
512
513void LookupIterator::WriteDataValue(Handle<Object> value) {
514  DCHECK_EQ(DATA, state_);
515  Handle<JSReceiver> holder = GetHolder<JSReceiver>();
516  if (IsElement()) {
517    Handle<JSObject> object = Handle<JSObject>::cast(holder);
518    ElementsAccessor* accessor = object->GetElementsAccessor();
519    accessor->Set(object->elements(), number_, *value);
520  } else if (holder->IsJSGlobalObject()) {
521    Handle<GlobalDictionary> property_dictionary =
522        handle(JSObject::cast(*holder)->global_dictionary());
523    PropertyCell::UpdateCell(property_dictionary, dictionary_entry(), value,
524                             property_details_);
525  } else if (holder_map_->is_dictionary_map()) {
526    NameDictionary* property_dictionary = holder->property_dictionary();
527    property_dictionary->ValueAtPut(dictionary_entry(), *value);
528  } else if (property_details_.type() == v8::internal::DATA) {
529    JSObject::cast(*holder)->WriteToField(descriptor_number(), *value);
530  } else {
531    DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
532  }
533}
534
535
536bool LookupIterator::IsIntegerIndexedExotic(JSReceiver* holder) {
537  DCHECK(exotic_index_state_ != ExoticIndexState::kNotExotic);
538  if (exotic_index_state_ == ExoticIndexState::kExotic) return true;
539  if (!InternalHolderIsReceiverOrHiddenPrototype()) {
540    exotic_index_state_ = ExoticIndexState::kNotExotic;
541    return false;
542  }
543  DCHECK(exotic_index_state_ == ExoticIndexState::kUninitialized);
544  bool result = false;
545  // Compute and cache result.
546  if (IsElement()) {
547    result = index_ >= JSTypedArray::cast(holder)->length_value();
548  } else if (name()->IsString()) {
549    Handle<String> name_string = Handle<String>::cast(name());
550    if (name_string->length() != 0) {
551      result = IsSpecialIndex(isolate_->unicode_cache(), *name_string);
552    }
553  }
554  exotic_index_state_ =
555      result ? ExoticIndexState::kExotic : ExoticIndexState::kNotExotic;
556  return result;
557}
558
559
560void LookupIterator::InternalizeName() {
561  if (name_->IsUniqueName()) return;
562  name_ = factory()->InternalizeString(Handle<String>::cast(name_));
563}
564
565
566bool LookupIterator::HasInterceptor(Map* map) const {
567  if (IsElement()) return map->has_indexed_interceptor();
568  return map->has_named_interceptor();
569}
570
571
572bool LookupIterator::SkipInterceptor(JSObject* holder) {
573  auto info = GetInterceptor(holder);
574  // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
575  if (info->non_masking()) {
576    switch (interceptor_state_) {
577      case InterceptorState::kUninitialized:
578        interceptor_state_ = InterceptorState::kSkipNonMasking;
579      // Fall through.
580      case InterceptorState::kSkipNonMasking:
581        return true;
582      case InterceptorState::kProcessNonMasking:
583        return false;
584    }
585  }
586  return interceptor_state_ == InterceptorState::kProcessNonMasking;
587}
588
589
590JSReceiver* LookupIterator::NextHolder(Map* map) {
591  DisallowHeapAllocation no_gc;
592  if (!map->prototype()->IsJSReceiver()) return NULL;
593
594  JSReceiver* next = JSReceiver::cast(map->prototype());
595  DCHECK(!next->map()->IsJSGlobalObjectMap() ||
596         next->map()->is_hidden_prototype());
597
598  if (!check_prototype_chain() &&
599      !(check_hidden() && next->map()->is_hidden_prototype()) &&
600      // Always lookup behind the JSGlobalProxy into the JSGlobalObject, even
601      // when not checking other hidden prototypes.
602      !map->IsJSGlobalProxyMap()) {
603    return NULL;
604  }
605
606  return next;
607}
608
609
610LookupIterator::State LookupIterator::LookupInHolder(Map* const map,
611                                                     JSReceiver* const holder) {
612  STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
613  DisallowHeapAllocation no_gc;
614  if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
615    return LookupNonMaskingInterceptorInHolder(map, holder);
616  }
617  switch (state_) {
618    case NOT_FOUND:
619      if (map->IsJSProxyMap()) {
620        // Do not leak private property names.
621        if (IsElement() || !name_->IsPrivate()) return JSPROXY;
622      }
623      if (map->is_access_check_needed() &&
624          (IsElement() || !isolate_->IsInternallyUsedPropertyName(name_))) {
625        return ACCESS_CHECK;
626      }
627    // Fall through.
628    case ACCESS_CHECK:
629      if (exotic_index_state_ != ExoticIndexState::kNotExotic &&
630          holder->IsJSTypedArray() && IsIntegerIndexedExotic(holder)) {
631        return INTEGER_INDEXED_EXOTIC;
632      }
633      if (check_interceptor() && HasInterceptor(map) &&
634          !SkipInterceptor(JSObject::cast(holder))) {
635        // Do not leak private property names.
636        if (!name_.is_null() && name_->IsPrivate()) return NOT_FOUND;
637        return INTERCEPTOR;
638      }
639    // Fall through.
640    case INTERCEPTOR:
641      if (IsElement()) {
642        // TODO(verwaest): Optimize.
643        if (holder->IsStringObjectWithCharacterAt(index_)) {
644          PropertyAttributes attributes =
645              static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
646          property_details_ = PropertyDetails(attributes, v8::internal::DATA, 0,
647                                              PropertyCellType::kNoCell);
648        } else {
649          JSObject* js_object = JSObject::cast(holder);
650          if (js_object->elements() == isolate()->heap()->empty_fixed_array()) {
651            return NOT_FOUND;
652          }
653
654          ElementsAccessor* accessor = js_object->GetElementsAccessor();
655          FixedArrayBase* backing_store = js_object->elements();
656          number_ =
657              accessor->GetEntryForIndex(js_object, backing_store, index_);
658          if (number_ == kMaxUInt32) return NOT_FOUND;
659          property_details_ = accessor->GetDetails(backing_store, number_);
660        }
661      } else if (!map->is_dictionary_map()) {
662        DescriptorArray* descriptors = map->instance_descriptors();
663        int number = descriptors->SearchWithCache(*name_, map);
664        if (number == DescriptorArray::kNotFound) return NOT_FOUND;
665        number_ = static_cast<uint32_t>(number);
666        property_details_ = descriptors->GetDetails(number_);
667      } else if (map->IsJSGlobalObjectMap()) {
668        GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary();
669        int number = dict->FindEntry(name_);
670        if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
671        number_ = static_cast<uint32_t>(number);
672        DCHECK(dict->ValueAt(number_)->IsPropertyCell());
673        PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_));
674        if (cell->value()->IsTheHole()) return NOT_FOUND;
675        property_details_ = cell->property_details();
676      } else {
677        NameDictionary* dict = holder->property_dictionary();
678        int number = dict->FindEntry(name_);
679        if (number == NameDictionary::kNotFound) return NOT_FOUND;
680        number_ = static_cast<uint32_t>(number);
681        property_details_ = dict->DetailsAt(number_);
682      }
683      has_property_ = true;
684      switch (property_details_.kind()) {
685        case v8::internal::kData:
686          return DATA;
687        case v8::internal::kAccessor:
688          return ACCESSOR;
689      }
690    case ACCESSOR:
691    case DATA:
692      return NOT_FOUND;
693    case INTEGER_INDEXED_EXOTIC:
694    case JSPROXY:
695    case TRANSITION:
696      UNREACHABLE();
697  }
698  UNREACHABLE();
699  return state_;
700}
701
702
703LookupIterator::State LookupIterator::LookupNonMaskingInterceptorInHolder(
704    Map* const map, JSReceiver* const holder) {
705  switch (state_) {
706    case NOT_FOUND:
707      if (check_interceptor() && HasInterceptor(map) &&
708          !SkipInterceptor(JSObject::cast(holder))) {
709        return INTERCEPTOR;
710      }
711    // Fall through.
712    default:
713      return NOT_FOUND;
714  }
715  UNREACHABLE();
716  return state_;
717}
718
719}  // namespace internal
720}  // namespace v8
721