lookup.cc 35.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/field-type.h"
11
#include "src/isolate-inl.h"
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
// 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.
40
    it.name_ = name;
41 42 43 44 45 46
    return it;
  }

  return LookupIterator(receiver, name, configuration);
}

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
// static
LookupIterator LookupIterator::ForTransitionHandler(
    Isolate* isolate, Handle<Object> receiver, Handle<Name> name,
    Handle<Object> value, MaybeHandle<Object> handler,
    Handle<Map> transition_map) {
  if (handler.is_null()) return LookupIterator(receiver, name);

  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;
  }
  LookupIterator it(isolate, receiver, name, transition_map, details,
                    has_property);

  if (!transition_map->is_dictionary_map()) {
    PropertyConstness new_constness = kConst;
    if (FLAG_track_constant_fields) {
      if (it.constness() == kConst) {
        DCHECK_EQ(kData, it.property_details_.kind());
        // Check that current value matches new value otherwise we should make
        // the property mutable.
        if (!it.IsConstFieldValueEqualTo(*value)) new_constness = kMutable;
      }
    } else {
      new_constness = kMutable;
    }

    int descriptor_number = transition_map->LastAdded();
    Handle<Map> new_map = Map::PrepareForDataProperty(
        transition_map, descriptor_number, new_constness, value);
    // 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_;
}

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
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>();
127

128
void LookupIterator::Next() {
129 130
  DCHECK_NE(JSPROXY, state_);
  DCHECK_NE(TRANSITION, state_);
131
  DisallowHeapAllocation no_gc;
132
  has_property_ = false;
133

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

137
  if (map->IsSpecialReceiverMap()) {
138 139 140 141 142 143 144 145
    state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder)
                         : LookupInSpecialHolder<false>(map, holder);
    if (IsFound()) return;
  }

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

147 148
template <bool is_element>
void LookupIterator::NextInternal(Map* map, JSReceiver* holder) {
149
  do {
150
    JSReceiver* maybe_holder = NextHolder(map);
151 152
    if (maybe_holder == nullptr) {
      if (interceptor_state_ == InterceptorState::kSkipNonMasking) {
153
        RestartLookupForNonMaskingInterceptors<is_element>();
154 155
        return;
      }
156 157 158
      state_ = NOT_FOUND;
      if (holder != *holder_) holder_ = handle(holder, isolate_);
      return;
159
    }
160 161
    holder = maybe_holder;
    map = holder->map();
162
    state_ = LookupInHolder<is_element>(map, holder);
163
  } while (!IsFound());
164

165
  holder_ = handle(holder, isolate_);
166 167
}

168
template <bool is_element>
169 170
void LookupIterator::RestartInternal(InterceptorState interceptor_state) {
  interceptor_state_ = interceptor_state;
171
  property_details_ = PropertyDetails::Empty();
172
  number_ = static_cast<uint32_t>(DescriptorArray::kNotFound);
173
  Start<is_element>();
174 175
}

176 177
template void LookupIterator::RestartInternal<true>(InterceptorState);
template void LookupIterator::RestartInternal<false>(InterceptorState);
178

179
// static
180 181
Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver(
    Isolate* isolate, Handle<Object> receiver, uint32_t index) {
182 183 184 185 186 187 188 189 190 191 192
  // 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;
  }
193 194
  auto root =
      handle(receiver->GetPrototypeChainRootMap(isolate)->prototype(), isolate);
195
  if (root->IsNull(isolate)) {
196 197 198
    unsigned int magic = 0xbbbbbbbb;
    isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic);
  }
199
  return Handle<JSReceiver>::cast(root);
200 201 202 203
}


Handle<Map> LookupIterator::GetReceiverMap() const {
204
  if (receiver_->IsNumber()) return factory()->heap_number_map();
205
  return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_);
206 207
}

208
bool LookupIterator::HasAccess() const {
209
  DCHECK_EQ(ACCESS_CHECK, state_);
210 211
  return isolate_->MayAccess(handle(isolate_->context()),
                             GetHolder<JSObject>());
212 213
}

214
template <bool is_element>
215 216
void LookupIterator::ReloadPropertyInformation() {
  state_ = BEFORE_PROPERTY;
217
  interceptor_state_ = InterceptorState::kUninitialized;
218
  state_ = LookupInHolder<is_element>(holder_->map(), *holder_);
219
  DCHECK(IsFound() || !holder_->HasFastProperties());
220 221
}

222
void LookupIterator::InternalUpdateProtector() {
223 224
  if (isolate_->bootstrapper()->IsActive()) return;

225
  if (*name_ == heap()->constructor_string()) {
226
    if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
227 228 229 230 231 232
    // Setting the constructor property could change an instance's @@species
    if (holder_->IsJSArray()) {
      isolate_->CountUsage(
          v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified);
      isolate_->InvalidateArraySpeciesProtector();
    } else if (holder_->map()->is_prototype_map()) {
233
      DisallowHeapAllocation no_gc;
234 235
      // Setting the constructor of Array.prototype of any realm also needs
      // to invalidate the species protector
236 237
      if (isolate_->IsInAnyContext(*holder_,
                                   Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) {
238 239 240 241 242
        isolate_->CountUsage(v8::Isolate::UseCounterFeature::
                                 kArrayPrototypeConstructorModified);
        isolate_->InvalidateArraySpeciesProtector();
      }
    }
243
  } else if (*name_ == heap()->species_symbol()) {
244
    if (!isolate_->IsArraySpeciesLookupChainIntact()) return;
245 246
    // Setting the Symbol.species property of any Array constructor invalidates
    // the species protector
247
    if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) {
248 249 250 251
      isolate_->CountUsage(
          v8::Isolate::UseCounterFeature::kArraySpeciesModified);
      isolate_->InvalidateArraySpeciesProtector();
    }
252 253 254
  } else if (*name_ == heap()->is_concat_spreadable_symbol()) {
    if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return;
    isolate_->InvalidateIsConcatSpreadableProtector();
255 256 257 258 259
  } else if (*name_ == heap()->iterator_symbol()) {
    if (!isolate_->IsArrayIteratorLookupChainIntact()) return;
    if (holder_->IsJSArray()) {
      isolate_->InvalidateArrayIteratorProtector();
    }
260 261
  }
}
262

263
void LookupIterator::PrepareForDataProperty(Handle<Object> value) {
264
  DCHECK(state_ == DATA || state_ == ACCESSOR);
265
  DCHECK(HolderIsReceiverOrHiddenPrototype());
266 267 268 269

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

  if (IsElement()) {
270
    ElementsKind kind = holder->GetElementsKind();
271
    ElementsKind to = value->OptimalElementsKind();
272
    if (IsHoleyOrDictionaryElementsKind(kind)) to = GetHoleyElementsKind(to);
273
    to = GetMoreGeneralElementsKind(kind, to);
274 275 276 277

    if (kind != to) {
      JSObject::TransitionElementsKind(holder, to);
    }
278 279

    // Copy the backing store if it is copy-on-write.
280
    if (IsSmiOrObjectElementsKind(to)) {
281 282
      JSObject::EnsureWritableFastElements(holder);
    }
283 284
    return;
  }
285

286
  if (holder->IsJSGlobalObject()) {
287 288
    Handle<GlobalDictionary> dictionary(
        JSGlobalObject::cast(*holder)->global_dictionary());
289
    Handle<PropertyCell> cell(dictionary->CellAt(dictionary_entry()));
290 291 292 293 294
    property_details_ = cell->property_details();
    PropertyCell::PrepareForValue(dictionary, dictionary_entry(), value,
                                  property_details_);
    return;
  }
295
  if (!holder->HasFastProperties()) return;
296

297 298 299 300 301 302 303 304 305 306 307 308
  PropertyConstness new_constness = kConst;
  if (FLAG_track_constant_fields) {
    if (constness() == kConst) {
      DCHECK_EQ(kData, property_details_.kind());
      // Check that current value matches new value otherwise we should make
      // the property mutable.
      if (!IsConstFieldValueEqualTo(*value)) new_constness = kMutable;
    }
  } else {
    new_constness = kMutable;
  }

309
  Handle<Map> old_map(holder->map(), isolate_);
310 311
  Handle<Map> new_map = Map::PrepareForDataProperty(
      old_map, descriptor_number(), new_constness, value);
312 313 314

  if (old_map.is_identical_to(new_map)) {
    // Update the property details if the representation was None.
315
    if (constness() != new_constness || representation().IsNone()) {
316 317
      property_details_ =
          new_map->instance_descriptors()->GetDetails(descriptor_number());
318
    }
319
    return;
320 321
  }

322
  JSObject::MigrateToMap(holder, new_map);
323
  ReloadPropertyInformation<false>();
324 325 326
}


327 328
void LookupIterator::ReconfigureDataProperty(Handle<Object> value,
                                             PropertyAttributes attributes) {
329
  DCHECK(state_ == DATA || state_ == ACCESSOR);
330 331
  DCHECK(HolderIsReceiverOrHiddenPrototype());
  Handle<JSObject> holder = GetHolder<JSObject>();
332
  if (IsElement()) {
333
    DCHECK(!holder->HasFixedTypedArrayElements());
334
    DCHECK(attributes != NONE || !holder->HasFastElements());
335 336 337
    Handle<FixedArrayBase> elements(holder->elements());
    holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value,
                                               attributes);
338
    ReloadPropertyInformation<true>();
339 340 341 342
  } else if (holder->HasFastProperties()) {
    Handle<Map> old_map(holder->map(), isolate_);
    Handle<Map> new_map = Map::ReconfigureExistingProperty(
        old_map, descriptor_number(), i::kData, attributes);
343 344 345 346
    // Force mutable to avoid changing constant value by reconfiguring
    // kData -> kAccessor -> kData.
    new_map = Map::PrepareForDataProperty(new_map, descriptor_number(),
                                          kMutable, value);
347 348
    JSObject::MigrateToMap(holder, new_map);
    ReloadPropertyInformation<false>();
349 350 351
  }

  if (!IsElement() && !holder->HasFastProperties()) {
352
    PropertyDetails details(kData, attributes, PropertyCellType::kMutable);
353
    if (holder->IsJSGlobalObject()) {
354 355
      Handle<GlobalDictionary> dictionary(
          JSGlobalObject::cast(*holder)->global_dictionary());
356 357 358 359 360

      Handle<PropertyCell> cell = PropertyCell::PrepareForValue(
          dictionary, dictionary_entry(), value, details);
      cell->set_value(*value);
      property_details_ = cell->property_details();
361
    } else {
362 363 364 365 366 367
      Handle<NameDictionary> dictionary(holder->property_dictionary());
      PropertyDetails original_details =
          dictionary->DetailsAt(dictionary_entry());
      int enumeration_index = original_details.dictionary_index();
      DCHECK(enumeration_index > 0);
      details = details.set_index(enumeration_index);
368
      dictionary->SetEntry(dictionary_entry(), *name(), *value, details);
369
      property_details_ = details;
370
    }
371
    state_ = DATA;
372 373
  }

374
  WriteDataValue(value, true);
375 376 377 378 379 380

#if VERIFY_HEAP
  if (FLAG_verify_heap) {
    holder->JSObjectVerify();
  }
#endif
381 382
}

383 384
// 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.
385 386 387
// Returns true if a new transition has been created, or false if an existing
// transition was followed.
bool LookupIterator::PrepareTransitionToDataProperty(
388 389 390
    Handle<JSObject> receiver, Handle<Object> value,
    PropertyAttributes attributes, Object::StoreFromKeyed store_mode) {
  DCHECK(receiver.is_identical_to(GetStoreTarget()));
391
  if (state_ == TRANSITION) return false;
392 393 394 395 396

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

397 398 399
  DCHECK(state_ != LookupIterator::ACCESSOR ||
         (GetAccessors()->IsAccessorInfo() &&
          AccessorInfo::cast(*GetAccessors())->is_special_data_property()));
400
  DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_);
401
  DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype());
402

403 404 405 406 407 408 409
  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.
410 411 412 413 414 415
      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_);
416
      DCHECK(cell->value()->IsTheHole(isolate_));
417
      DCHECK(!value->IsTheHole(isolate_));
418
      transition_ = cell;
419 420 421 422
      // Assign an enumeration index to the property and update
      // SetNextEnumerationIndex.
      int index = dictionary->NextEnumerationIndex();
      dictionary->SetNextEnumerationIndex(index + 1);
423 424
      property_details_ = PropertyDetails(
          kData, attributes, PropertyCellType::kUninitialized, index);
425 426 427 428 429 430
      PropertyCellType new_type =
          PropertyCell::UpdatedType(cell, value, property_details_);
      property_details_ = property_details_.set_cell_type(new_type);
      cell->set_property_details(property_details_);
      number_ = entry;
      has_property_ = true;
431
    } else {
432 433
      // Don't set enumeration index (it will be set during value store).
      property_details_ =
434
          PropertyDetails(kData, attributes, PropertyCellType::kNoCell);
435 436
      transition_ = map;
    }
437
    return false;
438 439
  }

440
  bool created_new_map;
441
  Handle<Map> transition = Map::TransitionToDataProperty(
442 443
      map, name_, value, attributes, kDefaultFieldConstness, store_mode,
      &created_new_map);
444
  state_ = TRANSITION;
445 446
  transition_ = transition;

447 448 449
  if (transition->is_dictionary_map()) {
    // Don't set enumeration index (it will be set during value store).
    property_details_ =
450
        PropertyDetails(kData, attributes, PropertyCellType::kNoCell);
451
  } else {
452 453 454
    property_details_ = transition->GetLastDescriptorDetails();
    has_property_ = true;
  }
455
  return created_new_map;
456 457
}

458
void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) {
459 460
  DCHECK_EQ(TRANSITION, state_);

461
  DCHECK(receiver.is_identical_to(GetStoreTarget()));
462
  holder_ = receiver;
463 464 465 466
  if (receiver->IsJSGlobalObject()) {
    state_ = DATA;
    return;
  }
467 468
  Handle<Map> transition = transition_map();
  bool simple_transition = transition->GetBackPointer() == receiver->map();
469
  JSObject::MigrateToMap(receiver, transition);
470 471 472 473 474 475

  if (simple_transition) {
    int number = transition->LastAdded();
    number_ = static_cast<uint32_t>(number);
    property_details_ = transition->GetLastDescriptorDetails();
    state_ = DATA;
476 477 478 479 480 481 482
  } else if (receiver->map()->is_dictionary_map()) {
    Handle<NameDictionary> dictionary(receiver->property_dictionary(),
                                      isolate_);
    int entry;
    dictionary = NameDictionary::Add(dictionary, name(),
                                     isolate_->factory()->uninitialized_value(),
                                     property_details_, &entry);
483
    receiver->SetProperties(*dictionary);
484 485 486 487 488 489
    // Reload details containing proper enumeration index value.
    property_details_ = dictionary->DetailsAt(entry);
    number_ = entry;
    has_property_ = true;
    state_ = DATA;

490
  } else {
491
    ReloadPropertyInformation<false>();
492
  }
493 494 495
}


496
void LookupIterator::Delete() {
497
  Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_);
498
  if (IsElement()) {
499 500 501
    Handle<JSObject> object = Handle<JSObject>::cast(holder);
    ElementsAccessor* accessor = object->GetElementsAccessor();
    accessor->Delete(object, number_);
502
  } else {
503 504 505 506 507 508 509 510
    bool is_prototype_map = holder->map()->is_prototype_map();
    RuntimeCallTimerScope stats_scope(
        isolate_, is_prototype_map
                      ? &RuntimeCallStats::PrototypeObject_DeleteProperty
                      : &RuntimeCallStats::Object_DeleteProperty);

    PropertyNormalizationMode mode =
        is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES;
511 512

    if (holder->HasFastProperties()) {
513 514
      JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0,
                                    "DeletingProperty");
515
      ReloadPropertyInformation<false>();
516
    }
517
    JSReceiver::DeleteNormalizedProperty(holder, number_);
518 519 520
    if (holder->IsJSObject()) {
      JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder));
    }
521
  }
522
  state_ = NOT_FOUND;
523 524
}

525
void LookupIterator::TransitionToAccessorProperty(
526
    Handle<Object> getter, Handle<Object> setter,
527
    PropertyAttributes attributes) {
528
  DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_));
529 530 531
  // 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.
532
  Handle<JSObject> receiver = GetStoreTarget();
533 534 535
  if (!IsElement() && name()->IsPrivate()) {
    attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM);
  }
536

537
  if (!IsElement() && !receiver->map()->is_dictionary_map()) {
538
    Handle<Map> old_map(receiver->map(), isolate_);
539 540 541 542 543 544 545 546 547 548

    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;

549
    Handle<Map> new_map = Map::TransitionToAccessorProperty(
550
        isolate_, old_map, name_, descriptor, getter, setter, attributes);
551
    bool simple_transition = new_map->GetBackPointer() == receiver->map();
552
    JSObject::MigrateToMap(receiver, new_map);
553

554 555 556 557 558 559 560
    if (simple_transition) {
      int number = new_map->LastAdded();
      number_ = static_cast<uint32_t>(number);
      property_details_ = new_map->GetLastDescriptorDetails();
      state_ = ACCESSOR;
      return;
    }
561

562
    ReloadPropertyInformation<false>();
563
    if (!new_map->is_dictionary_map()) return;
564
  }
565 566

  Handle<AccessorPair> pair;
567
  if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) {
568 569
    pair = Handle<AccessorPair>::cast(GetAccessors());
    // If the component and attributes are identical, nothing has to be done.
570
    if (pair->Equals(*getter, *setter)) {
571 572 573 574
      if (property_details().attributes() == attributes) {
        if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver);
        return;
      }
575 576
    } else {
      pair = AccessorPair::Copy(pair);
577
      pair->SetComponents(*getter, *setter);
578 579
    }
  } else {
580
    pair = factory()->NewAccessorPair();
581
    pair->SetComponents(*getter, *setter);
582 583
  }

584
  TransitionToAccessorPair(pair, attributes);
585 586 587 588 589 590

#if VERIFY_HEAP
  if (FLAG_verify_heap) {
    receiver->JSObjectVerify();
  }
#endif
591 592 593 594 595 596 597 598
}


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

599
  PropertyDetails details(kAccessor, attributes, PropertyCellType::kMutable);
600

601 602 603 604 605
  if (IsElement()) {
    // TODO(verwaest): Move code into the element accessor.
    Handle<SeededNumberDictionary> dictionary =
        JSObject::NormalizeElements(receiver);

606 607
    dictionary = SeededNumberDictionary::Set(dictionary, index_, pair, receiver,
                                             details);
608
    receiver->RequireSlowElements(*dictionary);
609 610 611 612 613 614 615 616 617 618 619

    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);
    }
620 621

    ReloadPropertyInformation<true>();
622
  } else {
623 624 625 626 627 628 629
    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");

630 631
    JSObject::SetNormalizedProperty(receiver, name_, pair, details);
    JSObject::ReoptimizeIfPrototype(receiver);
632

633 634
    ReloadPropertyInformation<false>();
  }
635 636
}

637 638 639 640 641 642
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_;
}
643

644
bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const {
645
  DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY);
646
  // Optimization that only works if configuration_ is not mutable.
647
  if (!check_prototype_chain()) return true;
648
  DisallowHeapAllocation no_gc;
649
  if (*receiver_ == *holder_) return true;
650
  if (!receiver_->IsJSReceiver()) return false;
651
  JSReceiver* current = JSReceiver::cast(*receiver_);
652
  JSReceiver* object = *holder_;
653
  if (!current->map()->has_hidden_prototype()) return false;
654
  // JSProxy do not occur as hidden prototypes.
655
  if (object->IsJSProxy()) return false;
656
  PrototypeIterator iter(isolate(), current, kStartAtPrototype,
657 658
                         PrototypeIterator::END_AT_NON_HIDDEN);
  while (!iter.IsAtEnd()) {
659
    if (iter.GetCurrent<JSReceiver>() == object) return true;
660
    iter.Advance();
661
  }
662 663 664 665
  return false;
}


666 667
Handle<Object> LookupIterator::FetchValue() const {
  Object* result = NULL;
668
  if (IsElement()) {
669
    Handle<JSObject> holder = GetHolder<JSObject>();
670
    ElementsAccessor* accessor = holder->GetElementsAccessor();
671
    return accessor->Get(holder, number_);
672
  } else if (holder_->IsJSGlobalObject()) {
673
    Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
674
    result = holder->global_dictionary()->ValueAt(number_);
675
  } else if (!holder_->HasFastProperties()) {
676
    result = holder_->property_dictionary()->ValueAt(number_);
677 678
  } else if (property_details_.location() == kField) {
    DCHECK_EQ(kData, property_details_.kind());
679
    Handle<JSObject> holder = GetHolder<JSObject>();
680
    FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_);
681 682 683
    return JSObject::FastPropertyAt(holder, property_details_.representation(),
                                    field_index);
  } else {
684
    result = holder_->map()->instance_descriptors()->GetValue(number_);
685 686 687 688
  }
  return handle(result, isolate_);
}

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 718 719 720 721
bool LookupIterator::IsConstFieldValueEqualTo(Object* value) const {
  DCHECK(!IsElement());
  DCHECK(holder_->HasFastProperties());
  DCHECK_EQ(kField, property_details_.location());
  DCHECK_EQ(kConst, property_details_.constness());
  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());
      bits = HeapNumber::cast(current_value)->value_as_bits();
    }
    // 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;
  }
}

722 723 724
int LookupIterator::GetFieldDescriptorIndex() const {
  DCHECK(has_property_);
  DCHECK(holder_->HasFastProperties());
725 726
  DCHECK_EQ(kField, property_details_.location());
  DCHECK_EQ(kData, property_details_.kind());
727 728
  return descriptor_number();
}
729

730 731
int LookupIterator::GetAccessorIndex() const {
  DCHECK(has_property_);
732
  DCHECK(holder_->HasFastProperties());
733 734
  DCHECK_EQ(kDescriptor, property_details_.location());
  DCHECK_EQ(kAccessor, property_details_.kind());
735 736 737 738
  return descriptor_number();
}


739 740
int LookupIterator::GetConstantIndex() const {
  DCHECK(has_property_);
741
  DCHECK(holder_->HasFastProperties());
742 743
  DCHECK_EQ(kDescriptor, property_details_.location());
  DCHECK_EQ(kData, property_details_.kind());
744
  DCHECK(!FLAG_track_constant_fields);
745
  DCHECK(!IsElement());
746 747 748
  return descriptor_number();
}

749 750 751 752 753 754 755 756
Handle<Map> LookupIterator::GetFieldOwnerMap() const {
  DCHECK(has_property_);
  DCHECK(holder_->HasFastProperties());
  DCHECK_EQ(kField, property_details_.location());
  DCHECK(!IsElement());
  Map* holder_map = holder_->map();
  return handle(holder_map->FindFieldOwner(descriptor_number()), isolate_);
}
757

758
FieldIndex LookupIterator::GetFieldIndex() const {
759
  DCHECK(has_property_);
760
  DCHECK(holder_->HasFastProperties());
761
  DCHECK_EQ(kField, property_details_.location());
762
  DCHECK(!IsElement());
763
  Map* holder_map = holder_->map();
764
  int index =
765
      holder_map->instance_descriptors()->GetFieldIndex(descriptor_number());
766
  bool is_double = representation().IsDouble();
767
  return FieldIndex::ForPropertyIndex(holder_map, index, is_double);
768 769
}

770
Handle<FieldType> LookupIterator::GetFieldType() const {
771
  DCHECK(has_property_);
772
  DCHECK(holder_->HasFastProperties());
773
  DCHECK_EQ(kField, property_details_.location());
774
  return handle(
775
      holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()),
776 777 778 779
      isolate_);
}


780
Handle<PropertyCell> LookupIterator::GetPropertyCell() const {
781
  DCHECK(!IsElement());
782
  Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>();
783 784
  return handle(holder->global_dictionary()->CellAt(dictionary_entry()),
                isolate_);
785 786 787
}


788
Handle<Object> LookupIterator::GetAccessors() const {
789
  DCHECK_EQ(ACCESSOR, state_);
790 791 792 793 794
  return FetchValue();
}


Handle<Object> LookupIterator::GetDataValue() const {
795
  DCHECK_EQ(DATA, state_);
796 797 798 799
  Handle<Object> value = FetchValue();
  return value;
}

800 801
void LookupIterator::WriteDataValue(Handle<Object> value,
                                    bool initializing_store) {
802
  DCHECK_EQ(DATA, state_);
803
  Handle<JSReceiver> holder = GetHolder<JSReceiver>();
804
  if (IsElement()) {
805 806
    Handle<JSObject> object = Handle<JSObject>::cast(holder);
    ElementsAccessor* accessor = object->GetElementsAccessor();
807
    accessor->Set(object, number_, *value);
808
  } else if (holder->HasFastProperties()) {
809
    if (property_details_.location() == kField) {
810 811 812 813 814
      // Check that in case of kConst field the existing value is equal to
      // |value|.
      DCHECK_IMPLIES(
          !initializing_store && property_details_.constness() == kConst,
          IsConstFieldValueEqualTo(*value));
815 816 817
      JSObject::cast(*holder)->WriteToField(descriptor_number(),
                                            property_details_, *value);
    } else {
818
      DCHECK_EQ(kDescriptor, property_details_.location());
819
      DCHECK_EQ(kConst, property_details_.constness());
820
    }
821
  } else if (holder->IsJSGlobalObject()) {
822 823
    GlobalDictionary* dictionary =
        JSGlobalObject::cast(*holder)->global_dictionary();
824
    dictionary->CellAt(dictionary_entry())->set_value(*value);
825
  } else {
826 827
    NameDictionary* dictionary = holder->property_dictionary();
    dictionary->ValueAtPut(dictionary_entry(), *value);
828 829 830
  }
}

831
template <bool is_element>
832
bool LookupIterator::SkipInterceptor(JSObject* holder) {
833
  auto info = GetInterceptor<is_element>(holder);
834 835 836
  if (!is_element && name_->IsSymbol() && !info->can_intercept_symbols()) {
    return true;
  }
837 838 839 840 841 842 843 844 845 846 847 848 849
  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;
}
850 851 852

JSReceiver* LookupIterator::NextHolder(Map* map) {
  DisallowHeapAllocation no_gc;
853
  if (map->prototype() == heap()->null_value()) return NULL;
854
  if (!check_prototype_chain() && !map->has_hidden_prototype()) return NULL;
855
  return JSReceiver::cast(map->prototype());
856 857
}

858 859 860 861 862 863 864 865 866 867 868
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;
}
869

870 871 872 873 874 875 876 877 878 879 880 881 882
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) {
883 884 885
  STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY);
  switch (state_) {
    case NOT_FOUND:
886
      if (map->IsJSProxyMap()) {
887
        if (is_element || !name_->IsPrivate()) return JSPROXY;
888
      }
889
      if (map->is_access_check_needed()) {
890
        if (is_element || !name_->IsPrivate()) return ACCESS_CHECK;
891 892 893
      }
    // Fall through.
    case ACCESS_CHECK:
894
      if (check_interceptor() && HasInterceptor<is_element>(map) &&
895
          !SkipInterceptor<is_element>(JSObject::cast(holder))) {
896
        if (is_element || !name_->IsPrivate()) return INTERCEPTOR;
897 898 899
      }
    // Fall through.
    case INTERCEPTOR:
900
      if (!is_element && map->IsJSGlobalObjectMap()) {
901 902
        GlobalDictionary* dict =
            JSGlobalObject::cast(holder)->global_dictionary();
903 904 905
        int number = dict->FindEntry(name_);
        if (number == GlobalDictionary::kNotFound) return NOT_FOUND;
        number_ = static_cast<uint32_t>(number);
906
        PropertyCell* cell = dict->CellAt(number_);
907
        if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND;
908
        property_details_ = cell->property_details();
909 910 911 912 913 914 915
        has_property_ = true;
        switch (property_details_.kind()) {
          case v8::internal::kData:
            return DATA;
          case v8::internal::kAccessor:
            return ACCESSOR;
        }
916
      }
917
      return LookupInRegularHolder<is_element>(map, holder);
918 919 920 921 922 923 924 925 926 927 928
    case ACCESSOR:
    case DATA:
      return NOT_FOUND;
    case INTEGER_INDEXED_EXOTIC:
    case JSPROXY:
    case TRANSITION:
      UNREACHABLE();
  }
  UNREACHABLE();
}

929 930
template <bool is_element>
LookupIterator::State LookupIterator::LookupInRegularHolder(
931
    Map* const map, JSReceiver* const holder) {
932 933 934
  DisallowHeapAllocation no_gc;
  if (interceptor_state_ == InterceptorState::kProcessNonMasking) {
    return NOT_FOUND;
verwaest's avatar
verwaest committed
935
  }
936 937 938 939 940

  if (is_element) {
    JSObject* js_object = JSObject::cast(holder);
    ElementsAccessor* accessor = js_object->GetElementsAccessor();
    FixedArrayBase* backing_store = js_object->elements();
941 942
    number_ =
        accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_);
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967
    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 {
    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;
  }

968 969 970
  UNREACHABLE();
}

971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986
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>();
}

987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
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;
}

1009 1010
}  // namespace internal
}  // namespace v8