lookup.cc revision 3b9bc31999c9787eb726ecdbfd5796bfdec32a18
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
13namespace v8 {
14namespace internal {
15
16
17// static
18LookupIterator 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
48template <bool is_element>
49void 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
65template void LookupIterator::Start<true>();
66template void LookupIterator::Start<false>();
67
68void 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
87template <bool is_element>
88void 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
108template <bool is_element>
109void 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
116template void LookupIterator::RestartInternal<true>(InterceptorState);
117template void LookupIterator::RestartInternal<false>(InterceptorState);
118
119// static
120Handle<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()) {
135    unsigned int magic = 0xbbbbbbbb;
136    isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
137  }
138  return Handle<JSReceiver>::cast(root);
139}
140
141
142Handle<Map> LookupIterator::GetReceiverMap() const {
143  if (receiver_->IsNumber()) return factory()->heap_number_map();
144  return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
145}
146
147bool LookupIterator::HasAccess() const {
148  DCHECK_EQ(ACCESS_CHECK, state_);
149  return isolate_->MayAccess(handle(isolate_->context()),
150                             GetHolder<JSObject>());
151}
152
153template <bool is_element>
154void 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
161bool LookupIterator::HolderIsInContextIndex(uint32_t index) const {
162  DisallowHeapAllocation no_gc;
163
164  Object* context = heap()->native_contexts_list();
165  while (!context->IsUndefined()) {
166    Context* current_context = Context::cast(context);
167    if (current_context->get(index) == *holder_) {
168      return true;
169    }
170    context = current_context->get(Context::NEXT_CONTEXT_LINK);
171  }
172  return false;
173}
174
175void LookupIterator::InternalUpdateProtector() {
176  if (isolate_->bootstrapper()->IsActive()) return;
177  if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
178
179  if (*name_ == heap()->constructor_string()) {
180    // Setting the constructor property could change an instance's @@species
181    if (holder_->IsJSArray()) {
182      isolate_->CountUsage(
183          v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
184      isolate_->InvalidateArraySpeciesProtector();
185    } else if (holder_->map()->is_prototype_map()) {
186      // Setting the constructor of Array.prototype of any realm also needs
187      // to invalidate the species protector
188      if (HolderIsInContextIndex(Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
189        isolate_->CountUsage(v8::Isolate::UseCounterFeature::
190                                 kArrayPrototypeConstructorModified);
191        isolate_->InvalidateArraySpeciesProtector();
192      }
193    }
194  } else if (*name_ == heap()->species_symbol()) {
195    // Setting the Symbol.species property of any Array constructor invalidates
196    // the species protector
197    if (HolderIsInContextIndex(Context::ARRAY_FUNCTION_INDEX)) {
198      isolate_->CountUsage(
199          v8::Isolate::UseCounterFeature::kArraySpeciesModified);
200      isolate_->InvalidateArraySpeciesProtector();
201    }
202  }
203}
204
205void 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->HasFastProperties()) return;
229
230  Handle<Map> old_map(holder->map(), isolate_);
231  Handle<Map> new_map =
232      Map::PrepareForDataProperty(old_map, descriptor_number(), value);
233
234  if (old_map.is_identical_to(new_map)) {
235    // Update the property details if the representation was None.
236    if (representation().IsNone()) {
237      property_details_ =
238          new_map->instance_descriptors()->GetDetails(descriptor_number());
239    }
240    return;
241  }
242
243  JSObject::MigrateToMap(holder, new_map);
244  ReloadPropertyInformation<false>();
245}
246
247
248void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
249                                             PropertyAttributes attributes) {
250  DCHECK(state_ == DATA || state_ == ACCESSOR);
251  DCHECK(HolderIsReceiverOrHiddenPrototype());
252  Handle<JSObject> holder = GetHolder<JSObject>();
253  if (IsElement()) {
254    DCHECK(!holder->HasFixedTypedArrayElements());
255    DCHECK(attributes != NONE || !holder->HasFastElements());
256    Handle<FixedArrayBase> elements(holder->elements());
257    holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
258                                               attributes);
259    ReloadPropertyInformation<true>();
260  } else {
261    if (!holder->HasFastProperties()) {
262      PropertyDetails details(attributes, v8::internal::DATA, 0,
263                              PropertyCellType::kMutable);
264      JSObject::SetNormalizedProperty(holder, name(), value, details);
265    } else {
266      Handle<Map> old_map(holder->map(), isolate_);
267      Handle<Map> new_map = Map::ReconfigureExistingProperty(
268          old_map, descriptor_number(), i::kData, attributes);
269      new_map =
270          Map::PrepareForDataProperty(new_map, descriptor_number(), value);
271      JSObject::MigrateToMap(holder, new_map);
272    }
273    ReloadPropertyInformation<false>();
274  }
275
276  WriteDataValue(value);
277
278#if VERIFY_HEAP
279  if (FLAG_verify_heap) {
280    holder->JSObjectVerify();
281  }
282#endif
283}
284
285// Can only be called when the receiver is a JSObject. JSProxy has to be handled
286// via a trap. Adding properties to primitive values is not observable.
287void LookupIterator::PrepareTransitionToDataProperty(
288    Handle<JSObject> receiver, Handle<Object> value,
289    PropertyAttributes attributes, Object::StoreFromKeyed store_mode) {
290  DCHECK(receiver.is_identical_to(GetStoreTarget()));
291  if (state_ == TRANSITION) return;
292  DCHECK(state_ != LookupIterator::ACCESSOR ||
293         (GetAccessors()->IsAccessorInfo() &&
294          AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
295  DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
296  DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
297
298  Handle<Map> map(receiver->map(), isolate_);
299
300  // Dictionary maps can always have additional data properties.
301  if (map->is_dictionary_map()) {
302    state_ = TRANSITION;
303    if (map->IsJSGlobalObjectMap()) {
304      // Install a property cell.
305      auto cell = JSGlobalObject::EnsurePropertyCell(
306          Handle<JSGlobalObject>::cast(receiver), name());
307      DCHECK(cell->value()->IsTheHole());
308      transition_ = cell;
309    } else {
310      transition_ = map;
311    }
312    return;
313  }
314
315  Handle<Map> transition =
316      Map::TransitionToDataProperty(map, name_, value, attributes, store_mode);
317  state_ = TRANSITION;
318  transition_ = transition;
319
320  if (!transition->is_dictionary_map()) {
321    property_details_ = transition->GetLastDescriptorDetails();
322    has_property_ = true;
323  }
324}
325
326void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) {
327  DCHECK_EQ(TRANSITION, state_);
328
329  DCHECK(receiver.is_identical_to(GetStoreTarget()));
330
331  if (receiver->IsJSGlobalObject()) return;
332  holder_ = receiver;
333  Handle<Map> transition = transition_map();
334  bool simple_transition = transition->GetBackPointer() == receiver->map();
335  JSObject::MigrateToMap(receiver, transition);
336
337  if (simple_transition) {
338    int number = transition->LastAdded();
339    number_ = static_cast<uint32_t>(number);
340    property_details_ = transition->GetLastDescriptorDetails();
341    state_ = DATA;
342  } else {
343    ReloadPropertyInformation<false>();
344  }
345}
346
347
348void LookupIterator::Delete() {
349  Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
350  if (IsElement()) {
351    Handle<JSObject> object = Handle<JSObject>::cast(holder);
352    ElementsAccessor* accessor = object->GetElementsAccessor();
353    accessor->Delete(object, number_);
354  } else {
355    PropertyNormalizationMode mode = holder->map()->is_prototype_map()
356                                         ? KEEP_INOBJECT_PROPERTIES
357                                         : 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
374void LookupIterator::TransitionToAccessorProperty(
375    AccessorComponent component, Handle<Object> accessor,
376    PropertyAttributes attributes) {
377  DCHECK(!accessor->IsNull());
378  // Can only be called when the receiver is a JSObject. JSProxy has to be
379  // handled via a trap. Adding properties to primitive values is not
380  // observable.
381  Handle<JSObject> receiver = GetStoreTarget();
382
383  if (!IsElement() && !receiver->map()->is_dictionary_map()) {
384    Handle<Map> old_map(receiver->map(), isolate_);
385
386    if (!holder_.is_identical_to(receiver)) {
387      holder_ = receiver;
388      state_ = NOT_FOUND;
389    } else if (state_ == INTERCEPTOR) {
390      LookupInRegularHolder<false>(*old_map, *holder_);
391    }
392    int descriptor =
393        IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound;
394
395    Handle<Map> new_map = Map::TransitionToAccessorProperty(
396        old_map, name_, descriptor, component, accessor, attributes);
397    bool simple_transition = new_map->GetBackPointer() == receiver->map();
398    JSObject::MigrateToMap(receiver, new_map);
399
400    if (simple_transition) {
401      int number = new_map->LastAdded();
402      number_ = static_cast<uint32_t>(number);
403      property_details_ = new_map->GetLastDescriptorDetails();
404      state_ = ACCESSOR;
405      return;
406    }
407
408    ReloadPropertyInformation<false>();
409    if (!new_map->is_dictionary_map()) return;
410  }
411
412  Handle<AccessorPair> pair;
413  if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
414    pair = Handle<AccessorPair>::cast(GetAccessors());
415    // If the component and attributes are identical, nothing has to be done.
416    if (pair->get(component) == *accessor) {
417      if (property_details().attributes() == attributes) return;
418    } else {
419      pair = AccessorPair::Copy(pair);
420      pair->set(component, *accessor);
421    }
422  } else {
423    pair = factory()->NewAccessorPair();
424    pair->set(component, *accessor);
425  }
426
427  TransitionToAccessorPair(pair, attributes);
428
429#if VERIFY_HEAP
430  if (FLAG_verify_heap) {
431    receiver->JSObjectVerify();
432  }
433#endif
434}
435
436
437void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
438                                              PropertyAttributes attributes) {
439  Handle<JSObject> receiver = GetStoreTarget();
440  holder_ = receiver;
441
442  PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
443                          PropertyCellType::kMutable);
444
445  if (IsElement()) {
446    // TODO(verwaest): Move code into the element accessor.
447    Handle<SeededNumberDictionary> dictionary =
448        JSObject::NormalizeElements(receiver);
449
450    // We unconditionally pass used_as_prototype=false here because the call
451    // to RequireSlowElements takes care of the required IC clearing and
452    // we don't want to walk the heap twice.
453    dictionary =
454        SeededNumberDictionary::Set(dictionary, index_, pair, details, false);
455    receiver->RequireSlowElements(*dictionary);
456
457    if (receiver->HasSlowArgumentsElements()) {
458      FixedArray* parameter_map = FixedArray::cast(receiver->elements());
459      uint32_t length = parameter_map->length() - 2;
460      if (number_ < length) {
461        parameter_map->set(number_ + 2, heap()->the_hole_value());
462      }
463      FixedArray::cast(receiver->elements())->set(1, *dictionary);
464    } else {
465      receiver->set_elements(*dictionary);
466    }
467
468    ReloadPropertyInformation<true>();
469  } else {
470    PropertyNormalizationMode mode = receiver->map()->is_prototype_map()
471                                         ? KEEP_INOBJECT_PROPERTIES
472                                         : CLEAR_INOBJECT_PROPERTIES;
473    // Normalize object to make this operation simple.
474    JSObject::NormalizeProperties(receiver, mode, 0,
475                                  "TransitionToAccessorPair");
476
477    JSObject::SetNormalizedProperty(receiver, name_, pair, details);
478    JSObject::ReoptimizeIfPrototype(receiver);
479
480    ReloadPropertyInformation<false>();
481  }
482}
483
484
485bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
486  DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
487  // Optimization that only works if configuration_ is not mutable.
488  if (!check_prototype_chain()) return true;
489  DisallowHeapAllocation no_gc;
490  if (*receiver_ == *holder_) return true;
491  if (!receiver_->IsJSReceiver()) return false;
492  JSReceiver* current = JSReceiver::cast(*receiver_);
493  JSReceiver* object = *holder_;
494  if (!current->map()->has_hidden_prototype()) return false;
495  // JSProxy do not occur as hidden prototypes.
496  if (object->IsJSProxy()) return false;
497  PrototypeIterator iter(isolate(), current,
498                         PrototypeIterator::START_AT_PROTOTYPE,
499                         PrototypeIterator::END_AT_NON_HIDDEN);
500  while (!iter.IsAtEnd()) {
501    if (iter.GetCurrent<JSReceiver>() == object) return true;
502    iter.Advance();
503  }
504  return false;
505}
506
507
508Handle<Object> LookupIterator::FetchValue() const {
509  Object* result = NULL;
510  if (IsElement()) {
511    Handle<JSObject> holder = GetHolder<JSObject>();
512    ElementsAccessor* accessor = holder->GetElementsAccessor();
513    return accessor->Get(holder, number_);
514  } else if (holder_->IsJSGlobalObject()) {
515    Handle<JSObject> holder = GetHolder<JSObject>();
516    result = holder->global_dictionary()->ValueAt(number_);
517    DCHECK(result->IsPropertyCell());
518    result = PropertyCell::cast(result)->value();
519  } else if (!holder_->HasFastProperties()) {
520    result = holder_->property_dictionary()->ValueAt(number_);
521  } else if (property_details_.type() == v8::internal::DATA) {
522    Handle<JSObject> holder = GetHolder<JSObject>();
523    FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
524    return JSObject::FastPropertyAt(holder, property_details_.representation(),
525                                    field_index);
526  } else {
527    result = holder_->map()->instance_descriptors()->GetValue(number_);
528  }
529  return handle(result, isolate_);
530}
531
532
533int LookupIterator::GetAccessorIndex() const {
534  DCHECK(has_property_);
535  DCHECK(holder_->HasFastProperties());
536  DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type());
537  return descriptor_number();
538}
539
540
541int LookupIterator::GetConstantIndex() const {
542  DCHECK(has_property_);
543  DCHECK(holder_->HasFastProperties());
544  DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
545  DCHECK(!IsElement());
546  return descriptor_number();
547}
548
549
550FieldIndex LookupIterator::GetFieldIndex() const {
551  DCHECK(has_property_);
552  DCHECK(holder_->HasFastProperties());
553  DCHECK_EQ(v8::internal::DATA, property_details_.type());
554  DCHECK(!IsElement());
555  Map* holder_map = holder_->map();
556  int index =
557      holder_map->instance_descriptors()->GetFieldIndex(descriptor_number());
558  bool is_double = representation().IsDouble();
559  return FieldIndex::ForPropertyIndex(holder_map, index, is_double);
560}
561
562Handle<FieldType> LookupIterator::GetFieldType() const {
563  DCHECK(has_property_);
564  DCHECK(holder_->HasFastProperties());
565  DCHECK_EQ(v8::internal::DATA, property_details_.type());
566  return handle(
567      holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()),
568      isolate_);
569}
570
571
572Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
573  DCHECK(!IsElement());
574  Handle<JSObject> holder = GetHolder<JSObject>();
575  Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(holder);
576  Object* value = global->global_dictionary()->ValueAt(dictionary_entry());
577  DCHECK(value->IsPropertyCell());
578  return handle(PropertyCell::cast(value));
579}
580
581
582Handle<Object> LookupIterator::GetAccessors() const {
583  DCHECK_EQ(ACCESSOR, state_);
584  return FetchValue();
585}
586
587
588Handle<Object> LookupIterator::GetDataValue() const {
589  DCHECK_EQ(DATA, state_);
590  Handle<Object> value = FetchValue();
591  return value;
592}
593
594
595void LookupIterator::WriteDataValue(Handle<Object> value) {
596  DCHECK_EQ(DATA, state_);
597  Handle<JSReceiver> holder = GetHolder<JSReceiver>();
598  if (IsElement()) {
599    Handle<JSObject> object = Handle<JSObject>::cast(holder);
600    ElementsAccessor* accessor = object->GetElementsAccessor();
601    accessor->Set(object, number_, *value);
602  } else if (holder->HasFastProperties()) {
603    if (property_details_.type() == v8::internal::DATA) {
604      JSObject::cast(*holder)->WriteToField(descriptor_number(),
605                                            property_details_, *value);
606    } else {
607      DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
608    }
609  } else if (holder->IsJSGlobalObject()) {
610    Handle<GlobalDictionary> property_dictionary =
611        handle(JSObject::cast(*holder)->global_dictionary());
612    PropertyCell::UpdateCell(property_dictionary, dictionary_entry(), value,
613                             property_details_);
614  } else {
615    NameDictionary* property_dictionary = holder->property_dictionary();
616    property_dictionary->ValueAtPut(dictionary_entry(), *value);
617  }
618}
619
620template <bool is_element>
621bool LookupIterator::SkipInterceptor(JSObject* holder) {
622  auto info = GetInterceptor<is_element>(holder);
623  // TODO(dcarney): check for symbol/can_intercept_symbols here as well.
624  if (info->non_masking()) {
625    switch (interceptor_state_) {
626      case InterceptorState::kUninitialized:
627        interceptor_state_ = InterceptorState::kSkipNonMasking;
628      // Fall through.
629      case InterceptorState::kSkipNonMasking:
630        return true;
631      case InterceptorState::kProcessNonMasking:
632        return false;
633    }
634  }
635  return interceptor_state_ == InterceptorState::kProcessNonMasking;
636}
637
638JSReceiver* LookupIterator::NextHolder(Map* map) {
639  DisallowHeapAllocation no_gc;
640  if (map->prototype() == heap()->null_value()) return NULL;
641
642  DCHECK(!map->IsJSGlobalProxyMap() || map->has_hidden_prototype());
643
644  if (!check_prototype_chain() &&
645      !(check_hidden() && map->has_hidden_prototype()) &&
646      // Always lookup behind the JSGlobalProxy into the JSGlobalObject, even
647      // when not checking other hidden prototypes.
648      !map->IsJSGlobalProxyMap()) {
649    return NULL;
650  }
651
652  return JSReceiver::cast(map->prototype());
653}
654
655LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const {
656  DCHECK(!IsElement());
657  if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND;
658
659  Handle<String> name_string = Handle<String>::cast(name_);
660  if (name_string->length() == 0) return NOT_FOUND;
661
662  return IsSpecialIndex(isolate_->unicode_cache(), *name_string)
663             ? INTEGER_INDEXED_EXOTIC
664             : NOT_FOUND;
665}
666
667namespace {
668
669template <bool is_element>
670bool HasInterceptor(Map* map) {
671  return is_element ? map->has_indexed_interceptor()
672                    : map->has_named_interceptor();
673}
674
675}  // namespace
676
677template <bool is_element>
678LookupIterator::State LookupIterator::LookupInSpecialHolder(
679    Map* const map, JSReceiver* const holder) {
680  STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
681  switch (state_) {
682    case NOT_FOUND:
683      if (map->IsJSProxyMap()) {
684        if (is_element || !name_->IsPrivate()) return JSPROXY;
685      }
686      if (map->is_access_check_needed()) {
687        if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
688      }
689    // Fall through.
690    case ACCESS_CHECK:
691      if (check_interceptor() && HasInterceptor<is_element>(map) &&
692          !SkipInterceptor<is_element>(JSObject::cast(holder))) {
693        if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
694      }
695    // Fall through.
696    case INTERCEPTOR:
697      if (!is_element && map->IsJSGlobalObjectMap()) {
698        GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary();
699        int number = dict->FindEntry(name_);
700        if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
701        number_ = static_cast<uint32_t>(number);
702        DCHECK(dict->ValueAt(number_)->IsPropertyCell());
703        PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_));
704        if (cell->value()->IsTheHole()) return NOT_FOUND;
705        property_details_ = cell->property_details();
706        has_property_ = true;
707        switch (property_details_.kind()) {
708          case v8::internal::kData:
709            return DATA;
710          case v8::internal::kAccessor:
711            return ACCESSOR;
712        }
713      }
714      return LookupInRegularHolder<is_element>(map, holder);
715    case ACCESSOR:
716    case DATA:
717      return NOT_FOUND;
718    case INTEGER_INDEXED_EXOTIC:
719    case JSPROXY:
720    case TRANSITION:
721      UNREACHABLE();
722  }
723  UNREACHABLE();
724  return NOT_FOUND;
725}
726
727template <bool is_element>
728LookupIterator::State LookupIterator::LookupInRegularHolder(
729    Map* const map, JSReceiver* const holder) {
730  DisallowHeapAllocation no_gc;
731  if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
732    return NOT_FOUND;
733  }
734
735  if (is_element) {
736    JSObject* js_object = JSObject::cast(holder);
737    ElementsAccessor* accessor = js_object->GetElementsAccessor();
738    FixedArrayBase* backing_store = js_object->elements();
739    number_ = accessor->GetEntryForIndex(js_object, backing_store, index_);
740    if (number_ == kMaxUInt32) {
741      return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
742    }
743    property_details_ = accessor->GetDetails(js_object, number_);
744  } else if (!map->is_dictionary_map()) {
745    DescriptorArray* descriptors = map->instance_descriptors();
746    int number = descriptors->SearchWithCache(isolate_, *name_, map);
747    if (number == DescriptorArray::kNotFound) return NotFound(holder);
748    number_ = static_cast<uint32_t>(number);
749    property_details_ = descriptors->GetDetails(number_);
750  } else {
751    NameDictionary* dict = holder->property_dictionary();
752    int number = dict->FindEntry(name_);
753    if (number == NameDictionary::kNotFound) return NotFound(holder);
754    number_ = static_cast<uint32_t>(number);
755    property_details_ = dict->DetailsAt(number_);
756  }
757  has_property_ = true;
758  switch (property_details_.kind()) {
759    case v8::internal::kData:
760      return DATA;
761    case v8::internal::kAccessor:
762      return ACCESSOR;
763  }
764
765  UNREACHABLE();
766  return state_;
767}
768
769}  // namespace internal
770}  // namespace v8
771