lookup.cc 43.1 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/field-type.h"
11
#include "src/isolate-inl.h"
12
#include "src/objects/hash-table-inl.h"
13 14 15 16

namespace v8 {
namespace internal {

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
// static
LookupIterator LookupIterator::PropertyOrElement(
    Isolate* isolate, Handle<Object> receiver, Handle<Object> key,
    bool* success, Handle<JSReceiver> holder, Configuration configuration) {
  uint32_t index = 0;
  if (key->ToArrayIndex(&index)) {
    *success = true;
    return LookupIterator(isolate, receiver, index, holder, 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, holder, 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, holder, configuration);
}

46 47 48 49 50 51
// static
LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate,
                                                 Handle<Object> receiver,
                                                 Handle<Object> key,
                                                 bool* success,
                                                 Configuration configuration) {
52
  // TODO(mslekova): come up with better way to avoid duplication
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
  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.
71
    it.name_ = name;
72 73 74 75 76 77
    return it;
  }

  return LookupIterator(receiver, name, configuration);
}

78
// TODO(ishell): Consider removing this way of LookupIterator creation.
79 80 81
// static
LookupIterator LookupIterator::ForTransitionHandler(
    Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
82 83 84 85 86 87 88
    Handle<Object> value, MaybeHandle<Map> maybe_transition_map) {
  Handle<Map> transition_map;
  if (!maybe_transition_map.ToHandle(&transition_map) ||
      !transition_map->IsPrototypeValidityCellValid()) {
    // This map is not a valid transition handler, so full lookup is required.
    return LookupIterator(receiver, name);
  }
89 90 91 92 93 94 95 96 97 98

  PropertyDetails details = PropertyDetails::Empty();
  bool has_property;
  if (transition_map->is_dictionary_map()) {
    details = PropertyDetails(kData, NONE, PropertyCellType::kNoCell);
    has_property = false;
  } else {
    details = transition_map->GetLastDescriptorDetails();
    has_property = true;
  }
99 100 101 102 103 104 105
#ifdef DEBUG
  if (name->IsPrivate()) {
    DCHECK_EQ(DONT_ENUM, details.attributes());
  } else {
    DCHECK_EQ(NONE, details.attributes());
  }
#endif
106 107 108 109 110
  LookupIterator it(isolate, receiver, name, transition_map, details,
                    has_property);

  if (!transition_map->is_dictionary_map()) {
    int descriptor_number = transition_map->LastAdded();
111 112 113
    Handle<Map> new_map =
        Map::PrepareForDataProperty(isolate, transition_map, descriptor_number,
                                    PropertyConstness::kConst, value);
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
    // Reload information; this is no-op if nothing changed.
    it.property_details_ =
        new_map->instance_descriptors()->GetDetails(descriptor_number);
    it.transition_ = new_map;
  }
  return it;
}

LookupIterator::LookupIterator(Isolate* isolate, Handle<Object> receiver,
                               Handle<Name> name, Handle<Map> transition_map,
                               PropertyDetails details, bool has_property)
    : configuration_(DEFAULT),
      state_(TRANSITION),
      has_property_(has_property),
      interceptor_state_(InterceptorState::kUninitialized),
      property_details_(details),
      isolate_(isolate),
      name_(name),
      transition_(transition_map),
      receiver_(receiver),
      initial_holder_(GetRoot(isolate, receiver)),
      index_(kMaxUInt32),
      number_(static_cast<uint32_t>(DescriptorArray::kNotFound)) {
  holder_ = initial_holder_;
}

140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
template <bool is_element>
void LookupIterator::Start() {
  DisallowHeapAllocation no_gc;

  has_property_ = false;
  state_ = NOT_FOUND;
  holder_ = initial_holder_;

  JSReceiver* holder = *holder_;
  Map* map = holder->map();

  state_ = LookupInHolder<is_element>(map, holder);
  if (IsFound()) return;

  NextInternal<is_element>(map, holder);
}

template void LookupIterator::Start<true>();
template void LookupIterator::Start<false>();
159

160
void LookupIterator::Next() {
161 162
  DCHECK_NE(JSPROXY, state_);
  DCHECK_NE(TRANSITION, state_);
163
  DisallowHeapAllocation no_gc;
164
  has_property_ = false;
165

166
  JSReceiver* holder = *holder_;
167
  Map* map = holder->map();
168

169
  if (map->IsSpecialReceiverMap()) {
170 171 172 173 174 175 176 177
    state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
                         : LookupInSpecialHolder<false>(map, holder);
    if (IsFound()) return;
  }

  IsElement() ? NextInternal<true>(map, holder)
              : NextInternal<false>(map, holder);
}
178

179 180
template <bool is_element>
void LookupIterator::NextInternal(Map* map, JSReceiver* holder) {
181
  do {
182
    JSReceiver* maybe_holder = NextHolder(map);
183 184
    if (maybe_holder == nullptr) {
      if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
185
        RestartLookupForNonMaskingInterceptors<is_element>();
186 187
        return;
      }
188 189 190
      state_ = NOT_FOUND;
      if (holder != *holder_) holder_ = handle(holder, isolate_);
      return;
191
    }
192 193
    holder = maybe_holder;
    map = holder->map();
194
    state_ = LookupInHolder<is_element>(map, holder);
195
  } while (!IsFound());
196

197
  holder_ = handle(holder, isolate_);
198 199
}

200
template <bool is_element>
201 202
void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
  interceptor_state_ = interceptor_state;
203
  property_details_ = PropertyDetails::Empty();
204
  number_ = static_cast<uint32_t>(DescriptorArray::kNotFound);
205
  Start<is_element>();
206 207
}

208 209
template void LookupIterator::RestartInternal<true>(InterceptorState);
template void LookupIterator::RestartInternal<false>(InterceptorState);
210

211
// static
212 213
Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
    Isolate* isolate, Handle<Object> receiver, uint32_t index) {
214 215 216 217 218 219 220 221 222 223 224
  // 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;
  }
225 226
  auto root =
      handle(receiver->GetPrototypeChainRootMap(isolate)->prototype(), isolate);
227
  if (root->IsNull(isolate)) {
228
    isolate->PushStackTraceAndDie(*receiver);
229
  }
230
  return Handle<JSReceiver>::cast(root);
231 232 233 234
}


Handle<Map> LookupIterator::GetReceiverMap() const {
235
  if (receiver_->IsNumber()) return factory()->heap_number_map();
236
  return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
237 238
}

239
bool LookupIterator::HasAccess() const {
240
  DCHECK_EQ(ACCESS_CHECK, state_);
241
  return isolate_->MayAccess(handle(isolate_->context(), isolate_),
242
                             GetHolder<JSObject>());
243 244
}

245
template <bool is_element>
246 247
void LookupIterator::ReloadPropertyInformation() {
  state_ = BEFORE_PROPERTY;
248
  interceptor_state_ = InterceptorState::kUninitialized;
249
  state_ = LookupInHolder<is_element>(holder_->map(), *holder_);
250
  DCHECK(IsFound() || !holder_->HasFastProperties());
251 252
}

253
namespace {
254

255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
bool IsTypedArrayFunctionInAnyContext(Isolate* isolate, JSReceiver* holder) {
  static uint32_t context_slots[] = {
#define TYPED_ARRAY_CONTEXT_SLOTS(Type, type, TYPE, ctype, size) \
  Context::TYPE##_ARRAY_FUN_INDEX,

      TYPED_ARRAYS(TYPED_ARRAY_CONTEXT_SLOTS)
#undef TYPED_ARRAY_CONTEXT_SLOTS
  };

  if (!holder->IsJSFunction()) return false;

  return std::any_of(
      std::begin(context_slots), std::end(context_slots),
      [=](uint32_t slot) { return isolate->IsInAnyContext(holder, slot); });
}
270

271 272
}  // namespace

273
void LookupIterator::InternalUpdateProtector() {
274 275
  if (isolate_->bootstrapper()->IsActive()) return;

276 277
  ReadOnlyRoots roots(heap());
  if (*name_ == roots.constructor_string()) {
278 279 280 281
    if (!isolate_->IsArraySpeciesLookupChainIntact() &&
        !isolate_->IsTypedArraySpeciesLookupChainIntact() &&
        !isolate_->IsPromiseSpeciesLookupChainIntact())
      return;
282
    // Setting the constructor property could change an instance's @@species
283 284
    if (holder_->IsJSArray()) {
      if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
285 286
      isolate_->CountUsage(
          v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
287 288 289 290 291 292 293 294 295 296 297 298
      isolate_->InvalidateArraySpeciesProtector();
      return;
    } else if (holder_->IsJSPromise()) {
      if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return;
      isolate_->InvalidatePromiseSpeciesProtector();
      return;
    } else if (holder_->IsJSTypedArray()) {
      if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return;
      isolate_->InvalidateTypedArraySpeciesProtector();
      return;
    }
    if (holder_->map()->is_prototype_map()) {
299
      DisallowHeapAllocation no_gc;
300 301 302
      // Setting the constructor of Array.prototype, Promise.prototype or
      // %TypedArray%.prototype of any realm also needs to invalidate the
      // @@species protector.
303 304 305
      // For typed arrays, we check a prototype of this holder since TypedArrays
      // have different prototypes for each type, and their parent prototype is
      // pointing the same TYPED_ARRAY_PROTOTYPE.
306
      if (isolate_->IsInAnyContext(*holder_,
307 308 309 310 311 312 313 314 315 316 317 318 319 320
                                   Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
        if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
        isolate_->CountUsage(
            v8::Isolate::UseCounterFeature::kArrayPrototypeConstructorModified);
        isolate_->InvalidateArraySpeciesProtector();
      } else if (isolate_->IsInAnyContext(*holder_,
                                          Context::PROMISE_PROTOTYPE_INDEX)) {
        if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return;
        isolate_->InvalidatePromiseSpeciesProtector();
      } else if (isolate_->IsInAnyContext(
                     holder_->map()->prototype(),
                     Context::TYPED_ARRAY_PROTOTYPE_INDEX)) {
        if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return;
        isolate_->InvalidateTypedArraySpeciesProtector();
321 322
      }
    }
323
  } else if (*name_ == roots.next_string()) {
324 325 326 327 328 329 330
    if (!isolate_->IsArrayIteratorLookupChainIntact()) return;
    // Setting the next property of %ArrayIteratorPrototype% also needs to
    // invalidate the array iterator protector.
    if (isolate_->IsInAnyContext(
            *holder_, Context::INITIAL_ARRAY_ITERATOR_PROTOTYPE_INDEX)) {
      isolate_->InvalidateArrayIteratorProtector();
    }
331
  } else if (*name_ == roots.species_symbol()) {
332 333 334 335
    if (!isolate_->IsArraySpeciesLookupChainIntact() &&
        !isolate_->IsTypedArraySpeciesLookupChainIntact() &&
        !isolate_->IsPromiseSpeciesLookupChainIntact())
      return;
336 337
    // Setting the Symbol.species property of any Array, Promise or TypedArray
    // constructor invalidates the @@species protector
338 339
    if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) {
      if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
340 341
      isolate_->CountUsage(
          v8::Isolate::UseCounterFeature::kArraySpeciesModified);
342 343 344 345 346 347 348 349
      isolate_->InvalidateArraySpeciesProtector();
    } else if (isolate_->IsInAnyContext(*holder_,
                                        Context::PROMISE_FUNCTION_INDEX)) {
      if (!isolate_->IsPromiseSpeciesLookupChainIntact()) return;
      isolate_->InvalidatePromiseSpeciesProtector();
    } else if (IsTypedArrayFunctionInAnyContext(isolate_, *holder_)) {
      if (!isolate_->IsTypedArraySpeciesLookupChainIntact()) return;
      isolate_->InvalidateTypedArraySpeciesProtector();
350
    }
351
  } else if (*name_ == roots.is_concat_spreadable_symbol()) {
352 353
    if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return;
    isolate_->InvalidateIsConcatSpreadableProtector();
354
  } else if (*name_ == roots.iterator_symbol()) {
355 356 357 358
    if (!isolate_->IsArrayIteratorLookupChainIntact()) return;
    if (holder_->IsJSArray()) {
      isolate_->InvalidateArrayIteratorProtector();
    }
359
  } else if (*name_ == roots.resolve_string()) {
360 361 362 363 364 365
    if (!isolate_->IsPromiseResolveLookupChainIntact()) return;
    // Setting the "resolve" property on any %Promise% intrinsic object
    // invalidates the Promise.resolve protector.
    if (isolate_->IsInAnyContext(*holder_, Context::PROMISE_FUNCTION_INDEX)) {
      isolate_->InvalidatePromiseResolveProtector();
    }
366
  } else if (*name_ == roots.then_string()) {
367 368 369
    if (!isolate_->IsPromiseThenLookupChainIntact()) return;
    // Setting the "then" property on any JSPromise instance or on the
    // initial %PromisePrototype% invalidates the Promise#then protector.
370 371 372 373 374
    // Also setting the "then" property on the initial %ObjectPrototype%
    // invalidates the Promise#then protector, since we use this protector
    // to guard the fast-path in AsyncGeneratorResolve, where we can skip
    // the ResolvePromise step and go directly to FulfillPromise if we
    // know that the Object.prototype doesn't contain a "then" method.
375
    if (holder_->IsJSPromise() ||
376 377
        isolate_->IsInAnyContext(*holder_,
                                 Context::INITIAL_OBJECT_PROTOTYPE_INDEX) ||
378 379 380
        isolate_->IsInAnyContext(*holder_, Context::PROMISE_PROTOTYPE_INDEX)) {
      isolate_->InvalidatePromiseThenProtector();
    }
381 382
  }
}
383

384
void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
385
  DCHECK(state_ == DATA || state_ == ACCESSOR);
386
  DCHECK(HolderIsReceiverOrHiddenPrototype());
387

388 389 390 391 392 393 394
  Handle<JSReceiver> holder = GetHolder<JSReceiver>();
  // JSProxy does not have fast properties so we do an early return.
  DCHECK_IMPLIES(holder->IsJSProxy(), !holder->HasFastProperties());
  DCHECK_IMPLIES(holder->IsJSProxy(), name()->IsPrivate());
  if (holder->IsJSProxy()) return;

  Handle<JSObject> holder_obj = Handle<JSObject>::cast(holder);
395 396

  if (IsElement()) {
397
    ElementsKind kind = holder_obj->GetElementsKind();
398
    ElementsKind to = value->OptimalElementsKind();
399
    if (IsHoleyOrDictionaryElementsKind(kind)) to = GetHoleyElementsKind(to);
400
    to = GetMoreGeneralElementsKind(kind, to);
401 402

    if (kind != to) {
403
      JSObject::TransitionElementsKind(holder_obj, to);
404
    }
405 406

    // Copy the backing store if it is copy-on-write.
407
    if (IsSmiOrObjectElementsKind(to)) {
408
      JSObject::EnsureWritableFastElements(holder_obj);
409
    }
410 411
    return;
  }
412

413
  if (holder_obj->IsJSGlobalObject()) {
414
    Handle<GlobalDictionary> dictionary(
415 416 417
        JSGlobalObject::cast(*holder_obj)->global_dictionary(), isolate());
    Handle<PropertyCell> cell(dictionary->CellAt(dictionary_entry()),
                              isolate());
418
    property_details_ = cell->property_details();
419 420
    PropertyCell::PrepareForValue(isolate(), dictionary, dictionary_entry(),
                                  value, property_details_);
421 422
    return;
  }
423
  if (!holder_obj->HasFastProperties()) return;
424

425
  PropertyConstness new_constness = PropertyConstness::kConst;
426
  if (FLAG_track_constant_fields) {
427
    if (constness() == PropertyConstness::kConst) {
428 429 430
      DCHECK_EQ(kData, property_details_.kind());
      // Check that current value matches new value otherwise we should make
      // the property mutable.
431 432
      if (!IsConstFieldValueEqualTo(*value))
        new_constness = PropertyConstness::kMutable;
433 434
    }
  } else {
435
    new_constness = PropertyConstness::kMutable;
436 437
  }

438
  Handle<Map> old_map(holder_obj->map(), isolate_);
439
  Handle<Map> new_map = Map::PrepareForDataProperty(
440
      isolate(), old_map, descriptor_number(), new_constness, value);
441 442 443

  if (old_map.is_identical_to(new_map)) {
    // Update the property details if the representation was None.
444
    if (constness() != new_constness || representation().IsNone()) {
445 446
      property_details_ =
          new_map->instance_descriptors()->GetDetails(descriptor_number());
447
    }
448
    return;
449 450
  }

451
  JSObject::MigrateToMap(holder_obj, new_map);
452
  ReloadPropertyInformation<false>();
453 454 455
}


456 457
void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
                                             PropertyAttributes attributes) {
458
  DCHECK(state_ == DATA || state_ == ACCESSOR);
459
  DCHECK(HolderIsReceiverOrHiddenPrototype());
460 461 462 463 464 465 466 467 468 469

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

  // Property details can never change for private fields.
  if (holder->IsJSProxy()) {
    DCHECK(name()->IsPrivate());
    return;
  }

  Handle<JSObject> holder_obj = Handle<JSObject>::cast(holder);
470
  if (IsElement()) {
471 472
    DCHECK(!holder_obj->HasFixedTypedArrayElements());
    DCHECK(attributes != NONE || !holder_obj->HasFastElements());
473
    Handle<FixedArrayBase> elements(holder_obj->elements(), isolate());
474 475
    holder_obj->GetElementsAccessor()->Reconfigure(holder_obj, elements,
                                                   number_, value, attributes);
476
    ReloadPropertyInformation<true>();
477 478
  } else if (holder_obj->HasFastProperties()) {
    Handle<Map> old_map(holder_obj->map(), isolate_);
479
    Handle<Map> new_map = Map::ReconfigureExistingProperty(
480
        isolate_, old_map, descriptor_number(), i::kData, attributes);
481 482
    // Force mutable to avoid changing constant value by reconfiguring
    // kData -> kAccessor -> kData.
483 484 485
    new_map =
        Map::PrepareForDataProperty(isolate(), new_map, descriptor_number(),
                                    PropertyConstness::kMutable, value);
486
    JSObject::MigrateToMap(holder_obj, new_map);
487
    ReloadPropertyInformation<false>();
488 489
  }

490
  if (!IsElement() && !holder_obj->HasFastProperties()) {
491
    PropertyDetails details(kData, attributes, PropertyCellType::kMutable);
492 493 494 495 496 497 498 499
    if (holder_obj->map()->is_prototype_map() &&
        (property_details_.attributes() & READ_ONLY) == 0 &&
        (attributes & READ_ONLY) != 0) {
      // Invalidate prototype validity cell when a property is reconfigured
      // from writable to read-only as this may invalidate transitioning store
      // IC handlers.
      JSObject::InvalidatePrototypeChains(holder->map());
    }
500
    if (holder_obj->IsJSGlobalObject()) {
501
      Handle<GlobalDictionary> dictionary(
502
          JSGlobalObject::cast(*holder_obj)->global_dictionary(), isolate());
503 504

      Handle<PropertyCell> cell = PropertyCell::PrepareForValue(
505
          isolate(), dictionary, dictionary_entry(), value, details);
506 507
      cell->set_value(*value);
      property_details_ = cell->property_details();
508
    } else {
509 510
      Handle<NameDictionary> dictionary(holder_obj->property_dictionary(),
                                        isolate());
511 512 513
      PropertyDetails original_details =
          dictionary->DetailsAt(dictionary_entry());
      int enumeration_index = original_details.dictionary_index();
514
      DCHECK_GT(enumeration_index, 0);
515
      details = details.set_index(enumeration_index);
516
      dictionary->SetEntry(dictionary_entry(), *name(), *value, details);
517
      property_details_ = details;
518
    }
519
    state_ = DATA;
520 521
  }

522
  WriteDataValue(value, true);
523 524 525

#if VERIFY_HEAP
  if (FLAG_verify_heap) {
526
    holder->HeapObjectVerify(isolate());
527 528
  }
#endif
529 530
}

531 532
// 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.
533
void LookupIterator::PrepareTransitionToDataProperty(
534
    Handle<JSReceiver> receiver, Handle<Object> value,
535
    PropertyAttributes attributes, Object::StoreFromKeyed store_mode) {
536 537
  DCHECK_IMPLIES(receiver->IsJSProxy(), name()->IsPrivate());
  DCHECK(receiver.is_identical_to(GetStoreTarget<JSReceiver>()));
538
  if (state_ == TRANSITION) return;
539 540 541 542 543

  if (!IsElement() && name()->IsPrivate()) {
    attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
  }

544 545 546
  DCHECK(state_ != LookupIterator::ACCESSOR ||
         (GetAccessors()->IsAccessorInfo() &&
          AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
547
  DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
548
  DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
549

550 551 552 553 554 555 556
  Handle<Map> map(receiver->map(), isolate_);

  // Dictionary maps can always have additional data properties.
  if (map->is_dictionary_map()) {
    state_ = TRANSITION;
    if (map->IsJSGlobalObjectMap()) {
      // Install a property cell.
557 558 559 560 561 562
      Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver);
      int entry;
      Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell(
          global, name(), PropertyCellType::kUninitialized, &entry);
      Handle<GlobalDictionary> dictionary(global->global_dictionary(),
                                          isolate_);
563
      DCHECK(cell->value()->IsTheHole(isolate_));
564
      DCHECK(!value->IsTheHole(isolate_));
565
      transition_ = cell;
566 567 568 569
      // Assign an enumeration index to the property and update
      // SetNextEnumerationIndex.
      int index = dictionary->NextEnumerationIndex();
      dictionary->SetNextEnumerationIndex(index + 1);
570 571
      property_details_ = PropertyDetails(
          kData, attributes, PropertyCellType::kUninitialized, index);
572
      PropertyCellType new_type =
573
          PropertyCell::UpdatedType(isolate(), cell, value, property_details_);
574 575 576 577
      property_details_ = property_details_.set_cell_type(new_type);
      cell->set_property_details(property_details_);
      number_ = entry;
      has_property_ = true;
578
    } else {
579 580
      // Don't set enumeration index (it will be set during value store).
      property_details_ =
581
          PropertyDetails(kData, attributes, PropertyCellType::kNoCell);
582 583
      transition_ = map;
    }
584
    return;
585 586
  }

587 588 589
  Handle<Map> transition =
      Map::TransitionToDataProperty(isolate_, map, name_, value, attributes,
                                    kDefaultFieldConstness, store_mode);
590
  state_ = TRANSITION;
591 592
  transition_ = transition;

593 594 595
  if (transition->is_dictionary_map()) {
    // Don't set enumeration index (it will be set during value store).
    property_details_ =
596
        PropertyDetails(kData, attributes, PropertyCellType::kNoCell);
597
  } else {
598 599 600
    property_details_ = transition->GetLastDescriptorDetails();
    has_property_ = true;
  }
601 602
}

603 604
void LookupIterator::ApplyTransitionToDataProperty(
    Handle<JSReceiver> receiver) {
605 606
  DCHECK_EQ(TRANSITION, state_);

607
  DCHECK(receiver.is_identical_to(GetStoreTarget<JSReceiver>()));
608
  holder_ = receiver;
609
  if (receiver->IsJSGlobalObject()) {
610
    JSObject::InvalidatePrototypeChains(receiver->map());
611 612 613
    state_ = DATA;
    return;
  }
614 615
  Handle<Map> transition = transition_map();
  bool simple_transition = transition->GetBackPointer() == receiver->map();
616

617 618 619 620 621 622 623 624 625
  if (configuration_ == DEFAULT && !transition->is_dictionary_map() &&
      !transition->IsPrototypeValidityCellValid()) {
    // Only LookupIterator instances with DEFAULT (full prototype chain)
    // configuration can produce valid transition handler maps.
    Handle<Object> validity_cell =
        Map::GetOrCreatePrototypeChainValidityCell(transition, isolate());
    transition->set_prototype_validity_cell(*validity_cell);
  }

626 627 628
  if (!receiver->IsJSProxy()) {
    JSObject::MigrateToMap(Handle<JSObject>::cast(receiver), transition);
  }
629 630 631 632 633 634

  if (simple_transition) {
    int number = transition->LastAdded();
    number_ = static_cast<uint32_t>(number);
    property_details_ = transition->GetLastDescriptorDetails();
    state_ = DATA;
635 636 637 638
  } else if (receiver->map()->is_dictionary_map()) {
    Handle<NameDictionary> dictionary(receiver->property_dictionary(),
                                      isolate_);
    int entry;
639
    if (receiver->map()->is_prototype_map() && receiver->IsJSObject()) {
640 641
      JSObject::InvalidatePrototypeChains(receiver->map());
    }
642 643 644
    dictionary = NameDictionary::Add(dictionary, name(),
                                     isolate_->factory()->uninitialized_value(),
                                     property_details_, &entry);
645
    receiver->SetProperties(*dictionary);
646 647 648 649 650 651
    // Reload details containing proper enumeration index value.
    property_details_ = dictionary->DetailsAt(entry);
    number_ = entry;
    has_property_ = true;
    state_ = DATA;

652
  } else {
653
    ReloadPropertyInformation<false>();
654
  }
655 656 657
}


658
void LookupIterator::Delete() {
659
  Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
660
  if (IsElement()) {
661 662 663
    Handle<JSObject> object = Handle<JSObject>::cast(holder);
    ElementsAccessor* accessor = object->GetElementsAccessor();
    accessor->Delete(object, number_);
664
  } else {
665
    DCHECK(!name()->IsPrivateField());
666 667 668
    bool is_prototype_map = holder->map()->is_prototype_map();
    RuntimeCallTimerScope stats_scope(
        isolate_, is_prototype_map
669 670
                      ? RuntimeCallCounterId::kPrototypeObject_DeleteProperty
                      : RuntimeCallCounterId::kObject_DeleteProperty);
671 672 673

    PropertyNormalizationMode mode =
        is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
674 675

    if (holder->HasFastProperties()) {
676 677
      JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
                                    "DeletingProperty");
678
      ReloadPropertyInformation<false>();
679
    }
680
    JSReceiver::DeleteNormalizedProperty(holder, number_);
681 682 683
    if (holder->IsJSObject()) {
      JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
    }
684
  }
685
  state_ = NOT_FOUND;
686 687
}

688
void LookupIterator::TransitionToAccessorProperty(
689
    Handle<Object> getter, Handle<Object> setter,
690
    PropertyAttributes attributes) {
691
  DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
692 693 694
  // 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.
695
  Handle<JSObject> receiver = GetStoreTarget<JSObject>();
696 697 698
  if (!IsElement() && name()->IsPrivate()) {
    attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
  }
699

700
  if (!IsElement() && !receiver->map()->is_dictionary_map()) {
701
    Handle<Map> old_map(receiver->map(), isolate_);
702 703 704 705 706 707 708 709 710 711

    if (!holder_.is_identical_to(receiver)) {
      holder_ = receiver;
      state_ = NOT_FOUND;
    } else if (state_ == INTERCEPTOR) {
      LookupInRegularHolder<false>(*old_map, *holder_);
    }
    int descriptor =
        IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound;

712
    Handle<Map> new_map = Map::TransitionToAccessorProperty(
713
        isolate_, old_map, name_, descriptor, getter, setter, attributes);
714
    bool simple_transition = new_map->GetBackPointer() == receiver->map();
715
    JSObject::MigrateToMap(receiver, new_map);
716

717 718 719 720 721 722 723
    if (simple_transition) {
      int number = new_map->LastAdded();
      number_ = static_cast<uint32_t>(number);
      property_details_ = new_map->GetLastDescriptorDetails();
      state_ = ACCESSOR;
      return;
    }
724

725
    ReloadPropertyInformation<false>();
726
    if (!new_map->is_dictionary_map()) return;
727
  }
728 729

  Handle<AccessorPair> pair;
730
  if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
731 732
    pair = Handle<AccessorPair>::cast(GetAccessors());
    // If the component and attributes are identical, nothing has to be done.
733
    if (pair->Equals(*getter, *setter)) {
734 735 736 737
      if (property_details().attributes() == attributes) {
        if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver);
        return;
      }
738 739
    } else {
      pair = AccessorPair::Copy(pair);
740
      pair->SetComponents(*getter, *setter);
741 742
    }
  } else {
743
    pair = factory()->NewAccessorPair();
744
    pair->SetComponents(*getter, *setter);
745 746
  }

747
  TransitionToAccessorPair(pair, attributes);
748 749 750

#if VERIFY_HEAP
  if (FLAG_verify_heap) {
751
    receiver->JSObjectVerify(isolate());
752 753
  }
#endif
754 755 756 757 758
}


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

762
  PropertyDetails details(kAccessor, attributes, PropertyCellType::kMutable);
763

764 765
  if (IsElement()) {
    // TODO(verwaest): Move code into the element accessor.
766
    isolate_->CountUsage(v8::Isolate::kIndexAccessor);
767
    Handle<NumberDictionary> dictionary = JSObject::NormalizeElements(receiver);
768

769 770
    dictionary =
        NumberDictionary::Set(dictionary, index_, pair, receiver, details);
771
    receiver->RequireSlowElements(*dictionary);
772 773 774 775 776

    if (receiver->HasSlowArgumentsElements()) {
      FixedArray* parameter_map = FixedArray::cast(receiver->elements());
      uint32_t length = parameter_map->length() - 2;
      if (number_ < length) {
777
        parameter_map->set(number_ + 2, ReadOnlyRoots(heap()).the_hole_value());
778 779 780 781 782
      }
      FixedArray::cast(receiver->elements())->set(1, *dictionary);
    } else {
      receiver->set_elements(*dictionary);
    }
783 784

    ReloadPropertyInformation<true>();
785
  } else {
786 787 788 789 790 791
    PropertyNormalizationMode mode = CLEAR_INOBJECT_PROPERTIES;
    if (receiver->map()->is_prototype_map()) {
      JSObject::InvalidatePrototypeChains(receiver->map());
      mode = KEEP_INOBJECT_PROPERTIES;
    }

792 793 794 795
    // Normalize object to make this operation simple.
    JSObject::NormalizeProperties(receiver, mode, 0,
                                  "TransitionToAccessorPair");

796 797
    JSObject::SetNormalizedProperty(receiver, name_, pair, details);
    JSObject::ReoptimizeIfPrototype(receiver);
798

799 800
    ReloadPropertyInformation<false>();
  }
801 802
}

803 804 805 806 807 808
bool LookupIterator::HolderIsReceiver() const {
  DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
  // Optimization that only works if configuration_ is not mutable.
  if (!check_prototype_chain()) return true;
  return *receiver_ == *holder_;
}
809

810
bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
811
  DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
812
  // Optimization that only works if configuration_ is not mutable.
813
  if (!check_prototype_chain()) return true;
814
  DisallowHeapAllocation no_gc;
815
  if (*receiver_ == *holder_) return true;
816
  if (!receiver_->IsJSReceiver()) return false;
817
  JSReceiver* current = JSReceiver::cast(*receiver_);
818
  JSReceiver* object = *holder_;
819
  if (!current->map()->has_hidden_prototype()) return false;
820
  // JSProxy do not occur as hidden prototypes.
821
  if (object->IsJSProxy()) return false;
822
  PrototypeIterator iter(isolate(), current, kStartAtPrototype,
823 824
                         PrototypeIterator::END_AT_NON_HIDDEN);
  while (!iter.IsAtEnd()) {
825
    if (iter.GetCurrent<JSReceiver>() == object) return true;
826
    iter.Advance();
827
  }
828 829 830 831
  return false;
}


832
Handle<Object> LookupIterator::FetchValue() const {
833
  Object* result = nullptr;
834
  if (IsElement()) {
835
    Handle<JSObject> holder = GetHolder<JSObject>();
836
    ElementsAccessor* accessor = holder->GetElementsAccessor();
837
    return accessor->Get(holder, number_);
838
  } else if (holder_->IsJSGlobalObject()) {
839
    Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
840
    result = holder->global_dictionary()->ValueAt(number_);
841
  } else if (!holder_->HasFastProperties()) {
842
    result = holder_->property_dictionary()->ValueAt(number_);
843 844
  } else if (property_details_.location() == kField) {
    DCHECK_EQ(kData, property_details_.kind());
845
    Handle<JSObject> holder = GetHolder<JSObject>();
846
    FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
847 848 849
    return JSObject::FastPropertyAt(holder, property_details_.representation(),
                                    field_index);
  } else {
850
    result = holder_->map()->instance_descriptors()->GetStrongValue(number_);
851 852 853 854
  }
  return handle(result, isolate_);
}

855 856 857 858
bool LookupIterator::IsConstFieldValueEqualTo(Object* value) const {
  DCHECK(!IsElement());
  DCHECK(holder_->HasFastProperties());
  DCHECK_EQ(kField, property_details_.location());
859
  DCHECK_EQ(PropertyConstness::kConst, property_details_.constness());
860 861 862 863 864 865 866 867 868 869
  Handle<JSObject> holder = GetHolder<JSObject>();
  FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
  if (property_details_.representation().IsDouble()) {
    if (!value->IsNumber()) return false;
    uint64_t bits;
    if (holder->IsUnboxedDoubleField(field_index)) {
      bits = holder->RawFastDoublePropertyAsBitsAt(field_index);
    } else {
      Object* current_value = holder->RawFastPropertyAt(field_index);
      DCHECK(current_value->IsMutableHeapNumber());
870
      bits = MutableHeapNumber::cast(current_value)->value_as_bits();
871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
    }
    // Use bit representation of double to to check for hole double, since
    // manipulating the signaling NaN used for the hole in C++, e.g. with
    // bit_cast or value(), will change its value on ia32 (the x87 stack is
    // used to return values and stores to the stack silently clear the
    // signalling bit).
    if (bits == kHoleNanInt64) {
      // Uninitialized double field.
      return true;
    }
    return bit_cast<double>(bits) == value->Number();
  } else {
    Object* current_value = holder->RawFastPropertyAt(field_index);
    return current_value->IsUninitialized(isolate()) || current_value == value;
  }
}

888 889 890
int LookupIterator::GetFieldDescriptorIndex() const {
  DCHECK(has_property_);
  DCHECK(holder_->HasFastProperties());
891 892
  DCHECK_EQ(kField, property_details_.location());
  DCHECK_EQ(kData, property_details_.kind());
893 894
  return descriptor_number();
}
895

896 897
int LookupIterator::GetAccessorIndex() const {
  DCHECK(has_property_);
898
  DCHECK(holder_->HasFastProperties());
899 900
  DCHECK_EQ(kDescriptor, property_details_.location());
  DCHECK_EQ(kAccessor, property_details_.kind());
901 902 903 904
  return descriptor_number();
}


905 906
int LookupIterator::GetConstantIndex() const {
  DCHECK(has_property_);
907
  DCHECK(holder_->HasFastProperties());
908 909
  DCHECK_EQ(kDescriptor, property_details_.location());
  DCHECK_EQ(kData, property_details_.kind());
910
  DCHECK(!FLAG_track_constant_fields);
911
  DCHECK(!IsElement());
912 913 914
  return descriptor_number();
}

915 916 917 918 919 920
Handle<Map> LookupIterator::GetFieldOwnerMap() const {
  DCHECK(has_property_);
  DCHECK(holder_->HasFastProperties());
  DCHECK_EQ(kField, property_details_.location());
  DCHECK(!IsElement());
  Map* holder_map = holder_->map();
921 922
  return handle(holder_map->FindFieldOwner(isolate(), descriptor_number()),
                isolate_);
923
}
924

925
FieldIndex LookupIterator::GetFieldIndex() const {
926
  DCHECK(has_property_);
927
  DCHECK(holder_->HasFastProperties());
928
  DCHECK_EQ(kField, property_details_.location());
929
  DCHECK(!IsElement());
930
  return FieldIndex::ForDescriptor(holder_->map(), descriptor_number());
931 932
}

933
Handle<FieldType> LookupIterator::GetFieldType() const {
934
  DCHECK(has_property_);
935
  DCHECK(holder_->HasFastProperties());
936
  DCHECK_EQ(kField, property_details_.location());
937
  return handle(
938
      holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()),
939 940 941 942
      isolate_);
}


943
Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
944
  DCHECK(!IsElement());
945
  Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
946 947
  return handle(holder->global_dictionary()->CellAt(dictionary_entry()),
                isolate_);
948 949 950
}


951
Handle<Object> LookupIterator::GetAccessors() const {
952
  DCHECK_EQ(ACCESSOR, state_);
953 954 955 956 957
  return FetchValue();
}


Handle<Object> LookupIterator::GetDataValue() const {
958
  DCHECK_EQ(DATA, state_);
959 960 961 962
  Handle<Object> value = FetchValue();
  return value;
}

963 964
void LookupIterator::WriteDataValue(Handle<Object> value,
                                    bool initializing_store) {
965
  DCHECK_EQ(DATA, state_);
966
  Handle<JSReceiver> holder = GetHolder<JSReceiver>();
967
  if (IsElement()) {
968 969
    Handle<JSObject> object = Handle<JSObject>::cast(holder);
    ElementsAccessor* accessor = object->GetElementsAccessor();
970
    accessor->Set(object, number_, *value);
971
  } else if (holder->HasFastProperties()) {
972
    if (property_details_.location() == kField) {
973 974 975 976 977
      // Check that in case of VariableMode::kConst field the existing value is
      // equal to |value|.
      DCHECK_IMPLIES(!initializing_store && property_details_.constness() ==
                                                PropertyConstness::kConst,
                     IsConstFieldValueEqualTo(*value));
978 979 980
      JSObject::cast(*holder)->WriteToField(descriptor_number(),
                                            property_details_, *value);
    } else {
981
      DCHECK_EQ(kDescriptor, property_details_.location());
982
      DCHECK_EQ(PropertyConstness::kConst, property_details_.constness());
983
    }
984
  } else if (holder->IsJSGlobalObject()) {
985 986
    GlobalDictionary* dictionary =
        JSGlobalObject::cast(*holder)->global_dictionary();
987
    dictionary->CellAt(dictionary_entry())->set_value(*value);
988
  } else {
989
    DCHECK_IMPLIES(holder->IsJSProxy(), name()->IsPrivate());
990 991
    NameDictionary* dictionary = holder->property_dictionary();
    dictionary->ValueAtPut(dictionary_entry(), *value);
992 993 994
  }
}

995
template <bool is_element>
996
bool LookupIterator::SkipInterceptor(JSObject* holder) {
997
  auto info = GetInterceptor<is_element>(holder);
998 999 1000
  if (!is_element && name_->IsSymbol() && !info->can_intercept_symbols()) {
    return true;
  }
1001 1002 1003 1004
  if (info->non_masking()) {
    switch (interceptor_state_) {
      case InterceptorState::kUninitialized:
        interceptor_state_ = InterceptorState::kSkipNonMasking;
1005
        V8_FALLTHROUGH;
1006 1007 1008 1009 1010 1011 1012 1013
      case InterceptorState::kSkipNonMasking:
        return true;
      case InterceptorState::kProcessNonMasking:
        return false;
    }
  }
  return interceptor_state_ == InterceptorState::kProcessNonMasking;
}
1014 1015 1016

JSReceiver* LookupIterator::NextHolder(Map* map) {
  DisallowHeapAllocation no_gc;
1017
  if (map->prototype() == ReadOnlyRoots(heap()).null_value()) return nullptr;
1018
  if (!check_prototype_chain() && !map->has_hidden_prototype()) return nullptr;
1019
  return JSReceiver::cast(map->prototype());
1020 1021
}

1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const {
  DCHECK(!IsElement());
  if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND;

  Handle<String> name_string = Handle<String>::cast(name_);
  if (name_string->length() == 0) return NOT_FOUND;

  return IsSpecialIndex(isolate_->unicode_cache(), *name_string)
             ? INTEGER_INDEXED_EXOTIC
             : NOT_FOUND;
}
1033

1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
namespace {

template <bool is_element>
bool HasInterceptor(Map* map) {
  return is_element ? map->has_indexed_interceptor()
                    : map->has_named_interceptor();
}

}  // namespace

template <bool is_element>
LookupIterator::State LookupIterator::LookupInSpecialHolder(
    Map* const map, JSReceiver* const holder) {
1047 1048 1049
  STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
  switch (state_) {
    case NOT_FOUND:
1050
      if (map->IsJSProxyMap()) {
1051
        if (is_element || !name_->IsPrivate()) return JSPROXY;
1052
      }
1053
      if (map->is_access_check_needed()) {
1054
        if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
1055
      }
1056
      V8_FALLTHROUGH;
1057
    case ACCESS_CHECK:
1058
      if (check_interceptor() && HasInterceptor<is_element>(map) &&
1059
          !SkipInterceptor<is_element>(JSObject::cast(holder))) {
1060
        if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
1061
      }
1062
      V8_FALLTHROUGH;
1063
    case INTERCEPTOR:
1064
      if (!is_element && map->IsJSGlobalObjectMap()) {
1065 1066
        GlobalDictionary* dict =
            JSGlobalObject::cast(holder)->global_dictionary();
1067 1068 1069
        int number = dict->FindEntry(name_);
        if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
        number_ = static_cast<uint32_t>(number);
1070
        PropertyCell* cell = dict->CellAt(number_);
1071
        if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND;
1072
        property_details_ = cell->property_details();
1073 1074 1075 1076 1077 1078 1079
        has_property_ = true;
        switch (property_details_.kind()) {
          case v8::internal::kData:
            return DATA;
          case v8::internal::kAccessor:
            return ACCESSOR;
        }
1080
      }
1081
      return LookupInRegularHolder<is_element>(map, holder);
1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092
    case ACCESSOR:
    case DATA:
      return NOT_FOUND;
    case INTEGER_INDEXED_EXOTIC:
    case JSPROXY:
    case TRANSITION:
      UNREACHABLE();
  }
  UNREACHABLE();
}

1093 1094
template <bool is_element>
LookupIterator::State LookupIterator::LookupInRegularHolder(
1095
    Map* const map, JSReceiver* const holder) {
1096 1097 1098
  DisallowHeapAllocation no_gc;
  if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
    return NOT_FOUND;
verwaest's avatar
verwaest committed
1099
  }
1100 1101 1102 1103 1104

  if (is_element) {
    JSObject* js_object = JSObject::cast(holder);
    ElementsAccessor* accessor = js_object->GetElementsAccessor();
    FixedArrayBase* backing_store = js_object->elements();
1105 1106
    number_ =
        accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_);
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
    if (number_ == kMaxUInt32) {
      return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND;
    }
    property_details_ = accessor->GetDetails(js_object, number_);
  } else if (!map->is_dictionary_map()) {
    DescriptorArray* descriptors = map->instance_descriptors();
    int number = descriptors->SearchWithCache(isolate_, *name_, map);
    if (number == DescriptorArray::kNotFound) return NotFound(holder);
    number_ = static_cast<uint32_t>(number);
    property_details_ = descriptors->GetDetails(number_);
  } else {
1118
    DCHECK_IMPLIES(holder->IsJSProxy(), name()->IsPrivate());
1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132
    NameDictionary* dict = holder->property_dictionary();
    int number = dict->FindEntry(name_);
    if (number == NameDictionary::kNotFound) return NotFound(holder);
    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;
  }

1133 1134 1135
  UNREACHABLE();
}

1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck()
    const {
  DCHECK_EQ(ACCESS_CHECK, state_);
  DisallowHeapAllocation no_gc;
  AccessCheckInfo* access_check_info =
      AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_));
  if (access_check_info) {
    Object* interceptor = IsElement() ? access_check_info->indexed_interceptor()
                                      : access_check_info->named_interceptor();
    if (interceptor) {
      return handle(InterceptorInfo::cast(interceptor), isolate_);
    }
  }
  return Handle<InterceptorInfo>();
}

1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173
bool LookupIterator::TryLookupCachedProperty() {
  return state() == LookupIterator::ACCESSOR &&
         GetAccessors()->IsAccessorPair() && LookupCachedProperty();
}

bool LookupIterator::LookupCachedProperty() {
  DCHECK_EQ(state(), LookupIterator::ACCESSOR);
  DCHECK(GetAccessors()->IsAccessorPair());

  AccessorPair* accessor_pair = AccessorPair::cast(*GetAccessors());
  Handle<Object> getter(accessor_pair->getter(), isolate());
  MaybeHandle<Name> maybe_name =
      FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), getter);
  if (maybe_name.is_null()) return false;

  // We have found a cached property! Modify the iterator accordingly.
  name_ = maybe_name.ToHandleChecked();
  Restart();
  CHECK_EQ(state(), LookupIterator::DATA);
  return true;
}

1174 1175
}  // namespace internal
}  // namespace v8