lookup.cc 23.8 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 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
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);
  }
}


294 295 296 297 298 299 300
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.
301
  Handle<JSObject> receiver = GetStoreTarget();
302

303
  if (!IsElement() && !receiver->map()->is_dictionary_map()) {
304
    holder_ = receiver;
305 306 307 308
    holder_map_ = Map::TransitionToAccessorProperty(
        handle(receiver->map(), isolate_), name_, component, accessor,
        attributes);
    JSObject::MigrateToMap(receiver, holder_map_);
309

310
    ReloadPropertyInformation();
311

312 313
    if (!holder_map_->is_dictionary_map()) return;
  }
314 315

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

330
  TransitionToAccessorPair(pair, attributes);
331 332 333 334 335 336

#if VERIFY_HEAP
  if (FLAG_verify_heap) {
    receiver->JSObjectVerify();
  }
#endif
337 338 339 340 341 342 343 344 345 346 347
}


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

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

348 349 350 351 352
  if (IsElement()) {
    // TODO(verwaest): Move code into the element accessor.
    Handle<SeededNumberDictionary> dictionary =
        JSObject::NormalizeElements(receiver);

353 354 355 356 357
    // 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);
358
    receiver->RequireSlowElements(*dictionary);
359 360 361 362 363 364 365 366 367 368 369 370

    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 {
371 372 373 374 375 376 377
    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");

378 379 380 381
    JSObject::SetNormalizedProperty(receiver, name_, pair, details);
    JSObject::ReoptimizeIfPrototype(receiver);
  }

382
  holder_map_ = handle(receiver->map(), isolate_);
383
  ReloadPropertyInformation();
384 385 386
}


387
bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
388
  DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
389 390 391 392
  return InternalHolderIsReceiverOrHiddenPrototype();
}

bool LookupIterator::InternalHolderIsReceiverOrHiddenPrototype() const {
393
  // Optimization that only works if configuration_ is not mutable.
394
  if (!check_prototype_chain()) return true;
395
  DisallowHeapAllocation no_gc;
396 397 398
  if (!receiver_->IsJSReceiver()) return false;
  Object* current = *receiver_;
  JSReceiver* holder = *holder_;
399 400 401 402 403 404 405
  // JSProxy do not occur as hidden prototypes.
  if (current->IsJSProxy()) {
    return JSReceiver::cast(current) == holder;
  }
  PrototypeIterator iter(isolate(), current,
                         PrototypeIterator::START_AT_RECEIVER);
  do {
406
    if (iter.GetCurrent<JSReceiver>() == holder) return true;
407
    DCHECK(!current->IsJSProxy());
408 409 410 411 412 413
    iter.Advance();
  } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN));
  return false;
}


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

426
    ElementsAccessor* accessor = holder->GetElementsAccessor();
427
    return accessor->Get(handle(holder->elements()), number_);
428
  } else if (holder_map_->IsJSGlobalObjectMap()) {
429 430 431 432
    result = holder->global_dictionary()->ValueAt(number_);
    DCHECK(result->IsPropertyCell());
    result = PropertyCell::cast(result)->value();
  } else if (holder_map_->is_dictionary_map()) {
433
    result = holder->property_dictionary()->ValueAt(number_);
434
  } else if (property_details_.type() == v8::internal::DATA) {
435 436 437 438 439
    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_);
440 441 442 443 444
  }
  return handle(result, isolate_);
}


445 446 447 448 449 450 451 452
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();
}


453 454
int LookupIterator::GetConstantIndex() const {
  DCHECK(has_property_);
455
  DCHECK(!holder_map_->is_dictionary_map());
456
  DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type());
457
  DCHECK(!IsElement());
458 459 460 461
  return descriptor_number();
}


462
FieldIndex LookupIterator::GetFieldIndex() const {
463
  DCHECK(has_property_);
464
  DCHECK(!holder_map_->is_dictionary_map());
465
  DCHECK_EQ(v8::internal::DATA, property_details_.type());
466
  DCHECK(!IsElement());
467
  int index =
468
      holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number());
469
  bool is_double = representation().IsDouble();
470
  return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double);
471 472 473
}


474 475
Handle<HeapType> LookupIterator::GetFieldType() const {
  DCHECK(has_property_);
476
  DCHECK(!holder_map_->is_dictionary_map());
477
  DCHECK_EQ(v8::internal::DATA, property_details_.type());
478
  return handle(
479
      holder_map_->instance_descriptors()->GetFieldType(descriptor_number()),
480 481 482 483
      isolate_);
}


484
Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
485
  DCHECK(!IsElement());
486
  Handle<JSObject> holder = GetHolder<JSObject>();
487
  Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(holder);
488
  Object* value = global->global_dictionary()->ValueAt(dictionary_entry());
489
  DCHECK(value->IsPropertyCell());
490
  return handle(PropertyCell::cast(value));
491 492 493
}


494
Handle<Object> LookupIterator::GetAccessors() const {
495
  DCHECK_EQ(ACCESSOR, state_);
496 497 498 499 500
  return FetchValue();
}


Handle<Object> LookupIterator::GetDataValue() const {
501
  DCHECK_EQ(DATA, state_);
502 503 504 505 506
  Handle<Object> value = FetchValue();
  return value;
}


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


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


553 554 555 556
void LookupIterator::InternalizeName() {
  if (name_->IsUniqueName()) return;
  name_ = factory()->InternalizeString(Handle<String>::cast(name_));
}
557 558


559 560 561 562 563 564
bool LookupIterator::HasInterceptor(Map* map) const {
  if (IsElement()) return map->has_indexed_interceptor();
  return map->has_named_interceptor();
}


565
bool LookupIterator::SkipInterceptor(JSObject* holder) {
566
  auto info = GetInterceptor(holder);
567 568 569 570 571 572 573 574 575 576 577 578 579 580
  // 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;
}
581 582 583 584 585 586 587


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

  JSReceiver* next = JSReceiver::cast(map->prototype());
588
  DCHECK(!next->map()->IsJSGlobalObjectMap() ||
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
         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 &&
620
          holder->IsJSTypedArray() && IsIntegerIndexedExotic(holder)) {
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
        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_);
655
      } else if (map->IsJSGlobalObjectMap()) {
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 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
        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_;
}

707 708
}  // namespace internal
}  // namespace v8