lookup.cc 24.4 KB
Newer Older
1 2 3 4
// 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.

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 12 13 14 15

namespace v8 {
namespace internal {


16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
// 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);
}


48
void LookupIterator::Next() {
49 50
  DCHECK_NE(JSPROXY, state_);
  DCHECK_NE(TRANSITION, state_);
51
  DisallowHeapAllocation no_gc;
52
  has_property_ = false;
53

54
  JSReceiver* holder = *holder_;
55 56 57
  Map* map = *holder_map_;

  // Perform lookup on current holder.
58 59
  state_ = LookupInHolder(map, holder);
  if (IsFound()) return;
60 61

  // Continue lookup if lookup on current holder failed.
62
  do {
63
    JSReceiver* maybe_holder = NextHolder(map);
64 65 66 67 68 69 70
    if (maybe_holder == nullptr) {
      if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
        RestartLookupForNonMaskingInterceptors();
        return;
      }
      break;
    }
71 72
    holder = maybe_holder;
    map = holder->map();
73 74
    state_ = LookupInHolder(map, holder);
  } while (!IsFound());
75

76 77 78 79
  if (holder != *holder_) {
    holder_ = handle(holder, isolate_);
    holder_map_ = handle(map, isolate_);
  }
80 81 82
}


83
void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
84
  state_ = NOT_FOUND;
85
  interceptor_state_ = interceptor_state;
86 87 88
  property_details_ = PropertyDetails::Empty();
  holder_ = initial_holder_;
  holder_map_ = handle(holder_->map(), isolate_);
89
  number_ = DescriptorArray::kNotFound;
90 91 92 93
  Next();
}


94
// static
95 96
Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
    Isolate* isolate, Handle<Object> receiver, uint32_t index) {
97 98 99 100 101 102 103 104 105 106 107
  // 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;
  }
108
  auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate);
109 110 111 112
  if (root->IsNull()) {
    unsigned int magic = 0xbbbbbbbb;
    isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
  }
113
  return Handle<JSReceiver>::cast(root);
114 115 116 117
}


Handle<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
}


123
Handle<JSObject> LookupIterator::GetStoreTarget() const {
124 125 126
  if (receiver_->IsJSGlobalProxy()) {
    PrototypeIterator iter(isolate(), receiver_);
    if (iter.IsAtEnd()) return Handle<JSGlobalProxy>::cast(receiver_);
127
    return PrototypeIterator::GetCurrent<JSGlobalObject>(iter);
128
  }
129
  return Handle<JSObject>::cast(receiver_);
130 131 132
}


133
bool LookupIterator::HasAccess() const {
134
  DCHECK_EQ(ACCESS_CHECK, state_);
135 136
  return isolate_->MayAccess(handle(isolate_->context()),
                             GetHolder<JSObject>());
137 138 139
}


140 141
void LookupIterator::ReloadPropertyInformation() {
  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
}


148 149 150
void LookupIterator::ReloadHolderMap() {
  DCHECK_EQ(DATA, state_);
  DCHECK(IsElement());
151
  DCHECK(JSObject::cast(*holder_)->HasFixedTypedArrayElements());
152 153 154 155 156 157
  if (*holder_map_ != holder_->map()) {
    holder_map_ = handle(holder_->map(), isolate_);
  }
}


158
void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
159
  DCHECK(state_ == DATA || state_ == ACCESSOR);
160
  DCHECK(HolderIsReceiverOrHiddenPrototype());
161 162 163 164

  Handle<JSObject> holder = GetHolder<JSObject>();

  if (IsElement()) {
165 166 167
    ElementsKind kind = holder_map_->elements_kind();
    ElementsKind to = value->OptimalElementsKind();
    if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to);
168
    to = GetMoreGeneralElementsKind(kind, to);
169 170
    JSObject::TransitionElementsKind(holder, to);
    holder_map_ = handle(holder->map(), isolate_);
171 172

    // Copy the backing store if it is copy-on-write.
173
    if (IsFastSmiOrObjectElementsKind(to)) {
174 175 176 177 178 179 180 181 182 183
      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_);
184
  ReloadPropertyInformation();
185 186 187
}


188 189
void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
                                             PropertyAttributes attributes) {
190
  DCHECK(state_ == DATA || state_ == ACCESSOR);
191 192
  DCHECK(HolderIsReceiverOrHiddenPrototype());
  Handle<JSObject> holder = GetHolder<JSObject>();
193
  if (IsElement()) {
194
    DCHECK(!holder->HasFixedTypedArrayElements());
195
    DCHECK(attributes != NONE || !holder->HasFastElements());
196 197 198
    Handle<FixedArrayBase> elements(holder->elements());
    holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
                                               attributes);
199
  } else if (holder_map_->is_dictionary_map()) {
200 201
    PropertyDetails details(attributes, v8::internal::DATA, 0,
                            PropertyCellType::kMutable);
202
    JSObject::SetNormalizedProperty(holder, name(), value, details);
203
  } else {
204 205 206 207
    holder_map_ = Map::ReconfigureExistingProperty(
        holder_map_, descriptor_number(), i::kData, attributes);
    holder_map_ =
        Map::PrepareForDataProperty(holder_map_, descriptor_number(), value);
208
    JSObject::MigrateToMap(holder, holder_map_);
209 210
  }

211
  ReloadPropertyInformation();
212 213 214 215 216 217 218
  WriteDataValue(value);

#if VERIFY_HEAP
  if (FLAG_verify_heap) {
    holder->JSObjectVerify();
  }
#endif
219 220 221
}


222
void LookupIterator::PrepareTransitionToDataProperty(
223 224
    Handle<Object> value, PropertyAttributes attributes,
    Object::StoreFromKeyed store_mode) {
225
  if (state_ == TRANSITION) return;
226 227 228
  DCHECK(state_ != LookupIterator::ACCESSOR ||
         (GetAccessors()->IsAccessorInfo() &&
          AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
229
  DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
230
  DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
231 232 233
  // 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.
234
  Handle<JSObject> receiver = GetStoreTarget();
235

236
  if (!isolate()->IsInternallyUsedPropertyName(name()) &&
237 238
      !receiver->map()->is_extensible()) {
    return;
239 240
  }

241
  auto transition = Map::TransitionToDataProperty(
242
      handle(receiver->map(), isolate_), name_, value, attributes, store_mode);
243
  state_ = TRANSITION;
244 245
  transition_ = transition;

246
  if (receiver->IsJSGlobalObject()) {
247 248
    // Install a property cell.
    InternalizeName();
249 250
    auto cell = JSGlobalObject::EnsurePropertyCell(
        Handle<JSGlobalObject>::cast(receiver), name());
251 252
    DCHECK(cell->value()->IsTheHole());
    transition_ = cell;
253
  } else if (!transition->is_dictionary_map()) {
254 255 256
    property_details_ = transition->GetLastDescriptorDetails();
    has_property_ = true;
  }
257 258 259 260 261 262 263
}


void LookupIterator::ApplyTransitionToDataProperty() {
  DCHECK_EQ(TRANSITION, state_);

  Handle<JSObject> receiver = GetStoreTarget();
264
  if (receiver->IsJSGlobalObject()) return;
265
  holder_ = receiver;
266
  holder_map_ = transition_map();
267
  JSObject::MigrateToMap(receiver, holder_map_);
268 269 270 271
  ReloadPropertyInformation();
}


272
void LookupIterator::Delete() {
273
  Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
274
  if (IsElement()) {
275 276 277
    Handle<JSObject> object = Handle<JSObject>::cast(holder);
    ElementsAccessor* accessor = object->GetElementsAccessor();
    accessor->Delete(object, number_);
278 279 280 281 282 283
  } else {
    PropertyNormalizationMode mode = holder->map()->is_prototype_map()
                                         ? KEEP_INOBJECT_PROPERTIES
                                         : CLEAR_INOBJECT_PROPERTIES;

    if (holder->HasFastProperties()) {
284 285
      JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
                                    "DeletingProperty");
286 287 288 289
      holder_map_ = handle(holder->map(), isolate_);
      ReloadPropertyInformation();
    }
    // TODO(verwaest): Get rid of the name_ argument.
290 291 292 293
    JSReceiver::DeleteNormalizedProperty(holder, name_, number_);
    if (holder->IsJSObject()) {
      JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
    }
294 295 296 297
  }
}


298 299 300 301 302 303 304
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.
305
  Handle<JSObject> receiver = GetStoreTarget();
306

307
  if (!IsElement() && !receiver->map()->is_dictionary_map()) {
308
    holder_ = receiver;
309 310 311 312
    holder_map_ = Map::TransitionToAccessorProperty(
        handle(receiver->map(), isolate_), name_, component, accessor,
        attributes);
    JSObject::MigrateToMap(receiver, holder_map_);
313

314
    ReloadPropertyInformation();
315

316 317
    if (!holder_map_->is_dictionary_map()) return;
  }
318 319

  Handle<AccessorPair> pair;
320
  if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
321 322 323 324 325 326 327 328 329
    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 {
330
    pair = factory()->NewAccessorPair();
331 332 333
    pair->set(component, *accessor);
  }

334
  TransitionToAccessorPair(pair, attributes);
335 336 337 338 339 340

#if VERIFY_HEAP
  if (FLAG_verify_heap) {
    receiver->JSObjectVerify();
  }
#endif
341 342 343 344 345 346 347 348 349 350 351
}


void LookupIterator::TransitionToAccessorPair(Handle<Object> pair,
                                              PropertyAttributes attributes) {
  Handle<JSObject> receiver = GetStoreTarget();
  holder_ = receiver;

  PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0,
                          PropertyCellType::kMutable);

352 353 354 355 356
  if (IsElement()) {
    // TODO(verwaest): Move code into the element accessor.
    Handle<SeededNumberDictionary> dictionary =
        JSObject::NormalizeElements(receiver);

357 358 359 360 361
    // 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);
362
    receiver->RequireSlowElements(*dictionary);
363 364 365 366 367 368 369 370 371 372 373 374

    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 {
375 376 377 378 379 380 381
    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");

382 383 384 385
    JSObject::SetNormalizedProperty(receiver, name_, pair, details);
    JSObject::ReoptimizeIfPrototype(receiver);
  }

386
  holder_map_ = handle(receiver->map(), isolate_);
387
  ReloadPropertyInformation();
388 389 390
}


391
bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
392
  DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
393 394 395 396
  return InternalHolderIsReceiverOrHiddenPrototype();
}

bool 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 401 402
  if (!receiver_->IsJSReceiver()) return false;
  Object* current = *receiver_;
  JSReceiver* holder = *holder_;
403 404 405 406 407 408 409
  // JSProxy do not occur as hidden prototypes.
  if (current->IsJSProxy()) {
    return JSReceiver::cast(current) == holder;
  }
  PrototypeIterator iter(isolate(), current,
                         PrototypeIterator::START_AT_RECEIVER);
  do {
410
    if (iter.GetCurrent<JSReceiver>() == holder) return true;
411
    DCHECK(!current->IsJSProxy());
412 413 414 415 416 417
    iter.Advance();
  } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
  return false;
}


418 419
Handle<Object> LookupIterator::FetchValue() const {
  Object* result = NULL;
420
  if (IsElement()) {
421
    Handle<JSObject> holder = GetHolder<JSObject>();
422
    // TODO(verwaest): Optimize.
423 424 425 426 427 428 429
    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_));
    }

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 435 436 437
    result = holder->global_dictionary()->ValueAt(number_);
    DCHECK(result->IsPropertyCell());
    result = PropertyCell::cast(result)->value();
  } 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 442 443 444 445
    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_);
446 447 448 449 450
  }
  return handle(result, isolate_);
}


451 452 453 454 455 456 457 458
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();
}


459 460
int LookupIterator::GetConstantIndex() const {
  DCHECK(has_property_);
461
  DCHECK(!holder_map_->is_dictionary_map());
462
  DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
463
  DCHECK(!IsElement());
464 465 466 467
  return descriptor_number();
}


468
FieldIndex 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
Handle<FieldType> LookupIterator::GetFieldType() const {
480
  DCHECK(has_property_);
481
  DCHECK(!holder_map_->is_dictionary_map());
482
  DCHECK_EQ(v8::internal::DATA, property_details_.type());
483
  return handle(
484
      holder_map_->instance_descriptors()->GetFieldType(descriptor_number()),
485 486 487 488
      isolate_);
}


489
Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
490
  DCHECK(!IsElement());
491
  Handle<JSObject> holder = GetHolder<JSObject>();
492
  Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(holder);
493
  Object* value = global->global_dictionary()->ValueAt(dictionary_entry());
494
  DCHECK(value->IsPropertyCell());
495
  return handle(PropertyCell::cast(value));
496 497 498
}


499
Handle<Object> LookupIterator::GetAccessors() const {
500
  DCHECK_EQ(ACCESSOR, state_);
501 502 503 504 505
  return FetchValue();
}


Handle<Object> LookupIterator::GetDataValue() const {
506
  DCHECK_EQ(DATA, state_);
507 508 509 510 511
  Handle<Object> value = FetchValue();
  return value;
}


512
void LookupIterator::WriteDataValue(Handle<Object> value) {
513
  DCHECK_EQ(DATA, state_);
514
  Handle<JSReceiver> holder = GetHolder<JSReceiver>();
515
  if (IsElement()) {
516 517 518
    Handle<JSObject> object = Handle<JSObject>::cast(holder);
    ElementsAccessor* accessor = object->GetElementsAccessor();
    accessor->Set(object->elements(), number_, *value);
519
  } else if (holder->IsJSGlobalObject()) {
520
    Handle<GlobalDictionary> property_dictionary =
521
        handle(JSObject::cast(*holder)->global_dictionary());
522 523 524
    PropertyCell::UpdateCell(property_dictionary, dictionary_entry(), value,
                             property_details_);
  } else if (holder_map_->is_dictionary_map()) {
525
    NameDictionary* property_dictionary = holder->property_dictionary();
526
    property_dictionary->ValueAtPut(dictionary_entry(), *value);
527
  } else if (property_details_.type() == v8::internal::DATA) {
528
    JSObject::cast(*holder)->WriteToField(descriptor_number(), *value);
529
  } else {
530
    DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
531 532 533 534
  }
}


535
bool LookupIterator::IsIntegerIndexedExotic(JSReceiver* holder) {
536
  DCHECK(exotic_index_state_ != ExoticIndexState::kNotExotic);
537
  if (exotic_index_state_ == ExoticIndexState::kExotic) return true;
538 539 540 541
  if (!InternalHolderIsReceiverOrHiddenPrototype()) {
    exotic_index_state_ = ExoticIndexState::kNotExotic;
    return false;
  }
542 543 544
  DCHECK(exotic_index_state_ == ExoticIndexState::kUninitialized);
  bool result = false;
  // Compute and cache result.
545 546 547
  if (IsElement()) {
    result = index_ >= JSTypedArray::cast(holder)->length_value();
  } else if (name()->IsString()) {
548
    Handle<String> name_string = Handle<String>::cast(name());
549
    if (name_string->length() != 0) {
dcarney's avatar
dcarney committed
550
      result = IsSpecialIndex(isolate_->unicode_cache(), *name_string);
551 552
    }
  }
553
  exotic_index_state_ =
554
      result ? ExoticIndexState::kExotic : ExoticIndexState::kNotExotic;
555
  return result;
556 557 558
}


559 560 561 562
void LookupIterator::InternalizeName() {
  if (name_->IsUniqueName()) return;
  name_ = factory()->InternalizeString(Handle<String>::cast(name_));
}
563 564


565 566 567 568 569 570
bool LookupIterator::HasInterceptor(Map* map) const {
  if (IsElement()) return map->has_indexed_interceptor();
  return map->has_named_interceptor();
}


571
bool LookupIterator::SkipInterceptor(JSObject* holder) {
572
  auto info = GetInterceptor(holder);
573 574 575 576 577 578 579 580 581 582 583 584 585 586
  // 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;
}
587 588 589 590 591 592 593


JSReceiver* LookupIterator::NextHolder(Map* map) {
  DisallowHeapAllocation no_gc;
  if (!map->prototype()->IsJSReceiver()) return NULL;

  JSReceiver* next = JSReceiver::cast(map->prototype());
594
  DCHECK(!next->map()->IsJSGlobalObjectMap() ||
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
         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:
618
      if (map->IsJSProxyMap()) {
619
        // Do not leak private property names.
620
        if (IsElement() || !name_->IsPrivate()) return JSPROXY;
621
      }
622 623 624 625 626 627 628
      if (map->is_access_check_needed() &&
          (IsElement() || !isolate_->IsInternallyUsedPropertyName(name_))) {
        return ACCESS_CHECK;
      }
    // Fall through.
    case ACCESS_CHECK:
      if (exotic_index_state_ != ExoticIndexState::kNotExotic &&
629
          holder->IsJSTypedArray() && IsIntegerIndexedExotic(holder)) {
630 631 632 633
        return INTEGER_INDEXED_EXOTIC;
      }
      if (check_interceptor() && HasInterceptor(map) &&
          !SkipInterceptor(JSObject::cast(holder))) {
634 635
        // Do not leak private property names.
        if (!name_.is_null() && name_->IsPrivate()) return NOT_FOUND;
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
        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_);
666
      } else if (map->IsJSGlobalObjectMap()) {
667 668 669 670 671 672 673 674 675
        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 {
676
        NameDictionary* dict = holder->property_dictionary();
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
        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_;
}

718 719
}  // namespace internal
}  // namespace v8