1 // Copyright 2014 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/lookup.h" 6 7 #include "src/bootstrapper.h" 8 #include "src/deoptimizer.h" 9 #include "src/elements.h" 10 #include "src/field-type.h" 11 #include "src/isolate-inl.h" 12 13 namespace v8 { 14 namespace internal { 15 16 // static 17 LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate, 18 Handle<Object> receiver, 19 Handle<Object> key, 20 bool* success, 21 Configuration configuration) { 22 uint32_t index = 0; 23 if (key->ToArrayIndex(&index)) { 24 *success = true; 25 return LookupIterator(isolate, receiver, index, configuration); 26 } 27 28 Handle<Name> name; 29 *success = Object::ToName(isolate, key).ToHandle(&name); 30 if (!*success) { 31 DCHECK(isolate->has_pending_exception()); 32 // Return an unusable dummy. 33 return LookupIterator(receiver, isolate->factory()->empty_string()); 34 } 35 36 if (name->AsArrayIndex(&index)) { 37 LookupIterator it(isolate, receiver, index, configuration); 38 // Here we try to avoid having to rebuild the string later 39 // by storing it on the indexed LookupIterator. 40 it.name_ = name; 41 return it; 42 } 43 44 return LookupIterator(receiver, name, configuration); 45 } 46 47 template <bool is_element> 48 void LookupIterator::Start() { 49 DisallowHeapAllocation no_gc; 50 51 has_property_ = false; 52 state_ = NOT_FOUND; 53 holder_ = initial_holder_; 54 55 JSReceiver* holder = *holder_; 56 Map* map = holder->map(); 57 58 state_ = LookupInHolder<is_element>(map, holder); 59 if (IsFound()) return; 60 61 NextInternal<is_element>(map, holder); 62 } 63 64 template void LookupIterator::Start<true>(); 65 template void LookupIterator::Start<false>(); 66 67 void LookupIterator::Next() { 68 DCHECK_NE(JSPROXY, state_); 69 DCHECK_NE(TRANSITION, state_); 70 DisallowHeapAllocation no_gc; 71 has_property_ = false; 72 73 JSReceiver* holder = *holder_; 74 Map* map = holder->map(); 75 76 if (map->IsSpecialReceiverMap()) { 77 state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder) 78 : LookupInSpecialHolder<false>(map, holder); 79 if (IsFound()) return; 80 } 81 82 IsElement() ? NextInternal<true>(map, holder) 83 : NextInternal<false>(map, holder); 84 } 85 86 template <bool is_element> 87 void LookupIterator::NextInternal(Map* map, JSReceiver* holder) { 88 do { 89 JSReceiver* maybe_holder = NextHolder(map); 90 if (maybe_holder == nullptr) { 91 if (interceptor_state_ == InterceptorState::kSkipNonMasking) { 92 RestartLookupForNonMaskingInterceptors<is_element>(); 93 return; 94 } 95 state_ = NOT_FOUND; 96 if (holder != *holder_) holder_ = handle(holder, isolate_); 97 return; 98 } 99 holder = maybe_holder; 100 map = holder->map(); 101 state_ = LookupInHolder<is_element>(map, holder); 102 } while (!IsFound()); 103 104 holder_ = handle(holder, isolate_); 105 } 106 107 template <bool is_element> 108 void LookupIterator::RestartInternal(InterceptorState interceptor_state) { 109 interceptor_state_ = interceptor_state; 110 property_details_ = PropertyDetails::Empty(); 111 number_ = DescriptorArray::kNotFound; 112 Start<is_element>(); 113 } 114 115 template void LookupIterator::RestartInternal<true>(InterceptorState); 116 template void LookupIterator::RestartInternal<false>(InterceptorState); 117 118 // static 119 Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver( 120 Isolate* isolate, Handle<Object> receiver, uint32_t index) { 121 // Strings are the only objects with properties (only elements) directly on 122 // the wrapper. Hence we can skip generating the wrapper for all other cases. 123 if (index != kMaxUInt32 && receiver->IsString() && 124 index < static_cast<uint32_t>(String::cast(*receiver)->length())) { 125 // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native 126 // context, ensuring that we don't leak it into JS? 127 Handle<JSFunction> constructor = isolate->string_function(); 128 Handle<JSObject> result = isolate->factory()->NewJSObject(constructor); 129 Handle<JSValue>::cast(result)->set_value(*receiver); 130 return result; 131 } 132 auto root = 133 handle(receiver->GetPrototypeChainRootMap(isolate)->prototype(), isolate); 134 if (root->IsNull(isolate)) { 135 unsigned int magic = 0xbbbbbbbb; 136 isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic); 137 } 138 return Handle<JSReceiver>::cast(root); 139 } 140 141 142 Handle<Map> LookupIterator::GetReceiverMap() const { 143 if (receiver_->IsNumber()) return factory()->heap_number_map(); 144 return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_); 145 } 146 147 bool LookupIterator::HasAccess() const { 148 DCHECK_EQ(ACCESS_CHECK, state_); 149 return isolate_->MayAccess(handle(isolate_->context()), 150 GetHolder<JSObject>()); 151 } 152 153 template <bool is_element> 154 void LookupIterator::ReloadPropertyInformation() { 155 state_ = BEFORE_PROPERTY; 156 interceptor_state_ = InterceptorState::kUninitialized; 157 state_ = LookupInHolder<is_element>(holder_->map(), *holder_); 158 DCHECK(IsFound() || !holder_->HasFastProperties()); 159 } 160 161 void LookupIterator::InternalUpdateProtector() { 162 if (isolate_->bootstrapper()->IsActive()) return; 163 164 if (*name_ == heap()->constructor_string()) { 165 if (!isolate_->IsArraySpeciesLookupChainIntact()) return; 166 // Setting the constructor property could change an instance's @@species 167 if (holder_->IsJSArray()) { 168 isolate_->CountUsage( 169 v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified); 170 isolate_->InvalidateArraySpeciesProtector(); 171 } else if (holder_->map()->is_prototype_map()) { 172 DisallowHeapAllocation no_gc; 173 // Setting the constructor of Array.prototype of any realm also needs 174 // to invalidate the species protector 175 if (isolate_->IsInAnyContext(*holder_, 176 Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) { 177 isolate_->CountUsage(v8::Isolate::UseCounterFeature:: 178 kArrayPrototypeConstructorModified); 179 isolate_->InvalidateArraySpeciesProtector(); 180 } 181 } 182 } else if (*name_ == heap()->species_symbol()) { 183 if (!isolate_->IsArraySpeciesLookupChainIntact()) return; 184 // Setting the Symbol.species property of any Array constructor invalidates 185 // the species protector 186 if (isolate_->IsInAnyContext(*holder_, Context::ARRAY_FUNCTION_INDEX)) { 187 isolate_->CountUsage( 188 v8::Isolate::UseCounterFeature::kArraySpeciesModified); 189 isolate_->InvalidateArraySpeciesProtector(); 190 } 191 } else if (*name_ == heap()->is_concat_spreadable_symbol()) { 192 if (!isolate_->IsIsConcatSpreadableLookupChainIntact()) return; 193 isolate_->InvalidateIsConcatSpreadableProtector(); 194 } else if (*name_ == heap()->iterator_symbol()) { 195 if (!isolate_->IsArrayIteratorLookupChainIntact()) return; 196 if (holder_->IsJSArray()) { 197 isolate_->InvalidateArrayIteratorProtector(); 198 } 199 } 200 } 201 202 void LookupIterator::PrepareForDataProperty(Handle<Object> value) { 203 DCHECK(state_ == DATA || state_ == ACCESSOR); 204 DCHECK(HolderIsReceiverOrHiddenPrototype()); 205 206 Handle<JSObject> holder = GetHolder<JSObject>(); 207 208 if (IsElement()) { 209 ElementsKind kind = holder->GetElementsKind(); 210 ElementsKind to = value->OptimalElementsKind(); 211 if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to); 212 to = GetMoreGeneralElementsKind(kind, to); 213 214 if (kind != to) { 215 JSObject::TransitionElementsKind(holder, to); 216 } 217 218 // Copy the backing store if it is copy-on-write. 219 if (IsFastSmiOrObjectElementsKind(to)) { 220 JSObject::EnsureWritableFastElements(holder); 221 } 222 return; 223 } 224 225 if (holder->IsJSGlobalObject()) { 226 Handle<GlobalDictionary> dictionary(holder->global_dictionary()); 227 Handle<PropertyCell> cell( 228 PropertyCell::cast(dictionary->ValueAt(dictionary_entry()))); 229 DCHECK(!cell->IsTheHole(isolate_)); 230 property_details_ = cell->property_details(); 231 PropertyCell::PrepareForValue(dictionary, dictionary_entry(), value, 232 property_details_); 233 return; 234 } 235 if (!holder->HasFastProperties()) return; 236 237 PropertyConstness new_constness = kConst; 238 if (FLAG_track_constant_fields) { 239 if (constness() == kConst) { 240 DCHECK_EQ(kData, property_details_.kind()); 241 // Check that current value matches new value otherwise we should make 242 // the property mutable. 243 if (!IsConstFieldValueEqualTo(*value)) new_constness = kMutable; 244 } 245 } else { 246 new_constness = kMutable; 247 } 248 249 Handle<Map> old_map(holder->map(), isolate_); 250 Handle<Map> new_map = Map::PrepareForDataProperty( 251 old_map, descriptor_number(), new_constness, value); 252 253 if (old_map.is_identical_to(new_map)) { 254 // Update the property details if the representation was None. 255 if (representation().IsNone()) { 256 property_details_ = 257 new_map->instance_descriptors()->GetDetails(descriptor_number()); 258 } 259 return; 260 } 261 262 JSObject::MigrateToMap(holder, new_map); 263 ReloadPropertyInformation<false>(); 264 } 265 266 267 void LookupIterator::ReconfigureDataProperty(Handle<Object> value, 268 PropertyAttributes attributes) { 269 DCHECK(state_ == DATA || state_ == ACCESSOR); 270 DCHECK(HolderIsReceiverOrHiddenPrototype()); 271 Handle<JSObject> holder = GetHolder<JSObject>(); 272 if (IsElement()) { 273 DCHECK(!holder->HasFixedTypedArrayElements()); 274 DCHECK(attributes != NONE || !holder->HasFastElements()); 275 Handle<FixedArrayBase> elements(holder->elements()); 276 holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value, 277 attributes); 278 ReloadPropertyInformation<true>(); 279 } else if (holder->HasFastProperties()) { 280 Handle<Map> old_map(holder->map(), isolate_); 281 Handle<Map> new_map = Map::ReconfigureExistingProperty( 282 old_map, descriptor_number(), i::kData, attributes); 283 // Force mutable to avoid changing constant value by reconfiguring 284 // kData -> kAccessor -> kData. 285 new_map = Map::PrepareForDataProperty(new_map, descriptor_number(), 286 kMutable, value); 287 JSObject::MigrateToMap(holder, new_map); 288 ReloadPropertyInformation<false>(); 289 } else { 290 PropertyDetails details(kData, attributes, 0, PropertyCellType::kMutable); 291 if (holder->IsJSGlobalObject()) { 292 Handle<GlobalDictionary> dictionary(holder->global_dictionary()); 293 294 Handle<PropertyCell> cell = PropertyCell::PrepareForValue( 295 dictionary, dictionary_entry(), value, details); 296 cell->set_value(*value); 297 property_details_ = cell->property_details(); 298 } else { 299 Handle<NameDictionary> dictionary(holder->property_dictionary()); 300 PropertyDetails original_details = 301 dictionary->DetailsAt(dictionary_entry()); 302 int enumeration_index = original_details.dictionary_index(); 303 DCHECK(enumeration_index > 0); 304 details = details.set_index(enumeration_index); 305 dictionary->SetEntry(dictionary_entry(), name(), value, details); 306 property_details_ = details; 307 } 308 state_ = DATA; 309 } 310 311 WriteDataValue(value, true); 312 313 #if VERIFY_HEAP 314 if (FLAG_verify_heap) { 315 holder->JSObjectVerify(); 316 } 317 #endif 318 } 319 320 // Can only be called when the receiver is a JSObject. JSProxy has to be handled 321 // via a trap. Adding properties to primitive values is not observable. 322 void LookupIterator::PrepareTransitionToDataProperty( 323 Handle<JSObject> receiver, Handle<Object> value, 324 PropertyAttributes attributes, Object::StoreFromKeyed store_mode) { 325 DCHECK(receiver.is_identical_to(GetStoreTarget())); 326 if (state_ == TRANSITION) return; 327 328 if (!IsElement() && name()->IsPrivate()) { 329 attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM); 330 } 331 332 DCHECK(state_ != LookupIterator::ACCESSOR || 333 (GetAccessors()->IsAccessorInfo() && 334 AccessorInfo::cast(*GetAccessors())->is_special_data_property())); 335 DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_); 336 DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype()); 337 338 Handle<Map> map(receiver->map(), isolate_); 339 340 // Dictionary maps can always have additional data properties. 341 if (map->is_dictionary_map()) { 342 state_ = TRANSITION; 343 if (map->IsJSGlobalObjectMap()) { 344 // Install a property cell. 345 Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver); 346 int entry; 347 Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell( 348 global, name(), PropertyCellType::kUninitialized, &entry); 349 Handle<GlobalDictionary> dictionary(global->global_dictionary(), 350 isolate_); 351 DCHECK(cell->value()->IsTheHole(isolate_)); 352 DCHECK(!value->IsTheHole(isolate_)); 353 transition_ = cell; 354 // Assign an enumeration index to the property and update 355 // SetNextEnumerationIndex. 356 int index = dictionary->NextEnumerationIndex(); 357 dictionary->SetNextEnumerationIndex(index + 1); 358 property_details_ = PropertyDetails(kData, attributes, index, 359 PropertyCellType::kUninitialized); 360 PropertyCellType new_type = 361 PropertyCell::UpdatedType(cell, value, property_details_); 362 property_details_ = property_details_.set_cell_type(new_type); 363 cell->set_property_details(property_details_); 364 number_ = entry; 365 has_property_ = true; 366 } else { 367 // Don't set enumeration index (it will be set during value store). 368 property_details_ = 369 PropertyDetails(kData, attributes, 0, PropertyCellType::kNoCell); 370 transition_ = map; 371 } 372 return; 373 } 374 375 Handle<Map> transition = Map::TransitionToDataProperty( 376 map, name_, value, attributes, kDefaultFieldConstness, store_mode); 377 state_ = TRANSITION; 378 transition_ = transition; 379 380 if (transition->is_dictionary_map()) { 381 // Don't set enumeration index (it will be set during value store). 382 property_details_ = 383 PropertyDetails(kData, attributes, 0, PropertyCellType::kNoCell); 384 } else { 385 property_details_ = transition->GetLastDescriptorDetails(); 386 has_property_ = true; 387 } 388 } 389 390 void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) { 391 DCHECK_EQ(TRANSITION, state_); 392 393 DCHECK(receiver.is_identical_to(GetStoreTarget())); 394 holder_ = receiver; 395 if (receiver->IsJSGlobalObject()) { 396 state_ = DATA; 397 return; 398 } 399 Handle<Map> transition = transition_map(); 400 bool simple_transition = transition->GetBackPointer() == receiver->map(); 401 JSObject::MigrateToMap(receiver, transition); 402 403 if (simple_transition) { 404 int number = transition->LastAdded(); 405 number_ = static_cast<uint32_t>(number); 406 property_details_ = transition->GetLastDescriptorDetails(); 407 state_ = DATA; 408 } else if (receiver->map()->is_dictionary_map()) { 409 Handle<NameDictionary> dictionary(receiver->property_dictionary(), 410 isolate_); 411 int entry; 412 dictionary = NameDictionary::Add(dictionary, name(), 413 isolate_->factory()->uninitialized_value(), 414 property_details_, &entry); 415 receiver->set_properties(*dictionary); 416 // Reload details containing proper enumeration index value. 417 property_details_ = dictionary->DetailsAt(entry); 418 number_ = entry; 419 has_property_ = true; 420 state_ = DATA; 421 422 } else { 423 ReloadPropertyInformation<false>(); 424 } 425 } 426 427 428 void LookupIterator::Delete() { 429 Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_); 430 if (IsElement()) { 431 Handle<JSObject> object = Handle<JSObject>::cast(holder); 432 ElementsAccessor* accessor = object->GetElementsAccessor(); 433 accessor->Delete(object, number_); 434 } else { 435 bool is_prototype_map = holder->map()->is_prototype_map(); 436 RuntimeCallTimerScope stats_scope( 437 isolate_, is_prototype_map 438 ? &RuntimeCallStats::PrototypeObject_DeleteProperty 439 : &RuntimeCallStats::Object_DeleteProperty); 440 441 PropertyNormalizationMode mode = 442 is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES; 443 444 if (holder->HasFastProperties()) { 445 JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0, 446 "DeletingProperty"); 447 ReloadPropertyInformation<false>(); 448 } 449 // TODO(verwaest): Get rid of the name_ argument. 450 JSReceiver::DeleteNormalizedProperty(holder, name_, number_); 451 if (holder->IsJSObject()) { 452 JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder)); 453 } 454 } 455 state_ = NOT_FOUND; 456 } 457 458 void LookupIterator::TransitionToAccessorProperty( 459 Handle<Object> getter, Handle<Object> setter, 460 PropertyAttributes attributes) { 461 DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_)); 462 // Can only be called when the receiver is a JSObject. JSProxy has to be 463 // handled via a trap. Adding properties to primitive values is not 464 // observable. 465 Handle<JSObject> receiver = GetStoreTarget(); 466 if (!IsElement() && name()->IsPrivate()) { 467 attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM); 468 } 469 470 if (!IsElement() && !receiver->map()->is_dictionary_map()) { 471 Handle<Map> old_map(receiver->map(), isolate_); 472 473 if (!holder_.is_identical_to(receiver)) { 474 holder_ = receiver; 475 state_ = NOT_FOUND; 476 } else if (state_ == INTERCEPTOR) { 477 LookupInRegularHolder<false>(*old_map, *holder_); 478 } 479 int descriptor = 480 IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound; 481 482 Handle<Map> new_map = Map::TransitionToAccessorProperty( 483 isolate_, old_map, name_, descriptor, getter, setter, attributes); 484 bool simple_transition = new_map->GetBackPointer() == receiver->map(); 485 JSObject::MigrateToMap(receiver, new_map); 486 487 if (simple_transition) { 488 int number = new_map->LastAdded(); 489 number_ = static_cast<uint32_t>(number); 490 property_details_ = new_map->GetLastDescriptorDetails(); 491 state_ = ACCESSOR; 492 return; 493 } 494 495 ReloadPropertyInformation<false>(); 496 if (!new_map->is_dictionary_map()) return; 497 } 498 499 Handle<AccessorPair> pair; 500 if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) { 501 pair = Handle<AccessorPair>::cast(GetAccessors()); 502 // If the component and attributes are identical, nothing has to be done. 503 if (pair->Equals(*getter, *setter)) { 504 if (property_details().attributes() == attributes) { 505 if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver); 506 return; 507 } 508 } else { 509 pair = AccessorPair::Copy(pair); 510 pair->SetComponents(*getter, *setter); 511 } 512 } else { 513 pair = factory()->NewAccessorPair(); 514 pair->SetComponents(*getter, *setter); 515 } 516 517 TransitionToAccessorPair(pair, attributes); 518 519 #if VERIFY_HEAP 520 if (FLAG_verify_heap) { 521 receiver->JSObjectVerify(); 522 } 523 #endif 524 } 525 526 527 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair, 528 PropertyAttributes attributes) { 529 Handle<JSObject> receiver = GetStoreTarget(); 530 holder_ = receiver; 531 532 PropertyDetails details(kAccessor, attributes, 0, PropertyCellType::kMutable); 533 534 if (IsElement()) { 535 // TODO(verwaest): Move code into the element accessor. 536 Handle<SeededNumberDictionary> dictionary = 537 JSObject::NormalizeElements(receiver); 538 539 dictionary = SeededNumberDictionary::Set(dictionary, index_, pair, details, 540 receiver); 541 receiver->RequireSlowElements(*dictionary); 542 543 if (receiver->HasSlowArgumentsElements()) { 544 FixedArray* parameter_map = FixedArray::cast(receiver->elements()); 545 uint32_t length = parameter_map->length() - 2; 546 if (number_ < length) { 547 parameter_map->set(number_ + 2, heap()->the_hole_value()); 548 } 549 FixedArray::cast(receiver->elements())->set(1, *dictionary); 550 } else { 551 receiver->set_elements(*dictionary); 552 } 553 554 ReloadPropertyInformation<true>(); 555 } else { 556 PropertyNormalizationMode mode = receiver->map()->is_prototype_map() 557 ? KEEP_INOBJECT_PROPERTIES 558 : CLEAR_INOBJECT_PROPERTIES; 559 // Normalize object to make this operation simple. 560 JSObject::NormalizeProperties(receiver, mode, 0, 561 "TransitionToAccessorPair"); 562 563 JSObject::SetNormalizedProperty(receiver, name_, pair, details); 564 JSObject::ReoptimizeIfPrototype(receiver); 565 566 ReloadPropertyInformation<false>(); 567 } 568 } 569 570 571 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const { 572 DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY); 573 // Optimization that only works if configuration_ is not mutable. 574 if (!check_prototype_chain()) return true; 575 DisallowHeapAllocation no_gc; 576 if (*receiver_ == *holder_) return true; 577 if (!receiver_->IsJSReceiver()) return false; 578 JSReceiver* current = JSReceiver::cast(*receiver_); 579 JSReceiver* object = *holder_; 580 if (!current->map()->has_hidden_prototype()) return false; 581 // JSProxy do not occur as hidden prototypes. 582 if (object->IsJSProxy()) return false; 583 PrototypeIterator iter(isolate(), current, kStartAtPrototype, 584 PrototypeIterator::END_AT_NON_HIDDEN); 585 while (!iter.IsAtEnd()) { 586 if (iter.GetCurrent<JSReceiver>() == object) return true; 587 iter.Advance(); 588 } 589 return false; 590 } 591 592 593 Handle<Object> LookupIterator::FetchValue() const { 594 Object* result = NULL; 595 if (IsElement()) { 596 Handle<JSObject> holder = GetHolder<JSObject>(); 597 ElementsAccessor* accessor = holder->GetElementsAccessor(); 598 return accessor->Get(holder, number_); 599 } else if (holder_->IsJSGlobalObject()) { 600 Handle<JSObject> holder = GetHolder<JSObject>(); 601 result = holder->global_dictionary()->ValueAt(number_); 602 DCHECK(result->IsPropertyCell()); 603 result = PropertyCell::cast(result)->value(); 604 } else if (!holder_->HasFastProperties()) { 605 result = holder_->property_dictionary()->ValueAt(number_); 606 } else if (property_details_.location() == kField) { 607 DCHECK_EQ(kData, property_details_.kind()); 608 Handle<JSObject> holder = GetHolder<JSObject>(); 609 FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_); 610 return JSObject::FastPropertyAt(holder, property_details_.representation(), 611 field_index); 612 } else { 613 result = holder_->map()->instance_descriptors()->GetValue(number_); 614 } 615 return handle(result, isolate_); 616 } 617 618 bool LookupIterator::IsConstFieldValueEqualTo(Object* value) const { 619 DCHECK(!IsElement()); 620 DCHECK(holder_->HasFastProperties()); 621 DCHECK_EQ(kField, property_details_.location()); 622 DCHECK_EQ(kConst, property_details_.constness()); 623 Handle<JSObject> holder = GetHolder<JSObject>(); 624 FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_); 625 if (property_details_.representation().IsDouble()) { 626 if (!value->IsNumber()) return false; 627 uint64_t bits; 628 if (holder->IsUnboxedDoubleField(field_index)) { 629 bits = holder->RawFastDoublePropertyAsBitsAt(field_index); 630 } else { 631 Object* current_value = holder->RawFastPropertyAt(field_index); 632 DCHECK(current_value->IsMutableHeapNumber()); 633 bits = HeapNumber::cast(current_value)->value_as_bits(); 634 } 635 // Use bit representation of double to to check for hole double, since 636 // manipulating the signaling NaN used for the hole in C++, e.g. with 637 // bit_cast or value(), will change its value on ia32 (the x87 stack is 638 // used to return values and stores to the stack silently clear the 639 // signalling bit). 640 if (bits == kHoleNanInt64) { 641 // Uninitialized double field. 642 return true; 643 } 644 return bit_cast<double>(bits) == value->Number(); 645 } else { 646 Object* current_value = holder->RawFastPropertyAt(field_index); 647 return current_value->IsUninitialized(isolate()) || current_value == value; 648 } 649 } 650 651 int LookupIterator::GetFieldDescriptorIndex() const { 652 DCHECK(has_property_); 653 DCHECK(holder_->HasFastProperties()); 654 DCHECK_EQ(kField, property_details_.location()); 655 DCHECK_EQ(kData, property_details_.kind()); 656 return descriptor_number(); 657 } 658 659 int LookupIterator::GetAccessorIndex() const { 660 DCHECK(has_property_); 661 DCHECK(holder_->HasFastProperties()); 662 DCHECK_EQ(kDescriptor, property_details_.location()); 663 DCHECK_EQ(kAccessor, property_details_.kind()); 664 return descriptor_number(); 665 } 666 667 668 int LookupIterator::GetConstantIndex() const { 669 DCHECK(has_property_); 670 DCHECK(holder_->HasFastProperties()); 671 DCHECK_EQ(kDescriptor, property_details_.location()); 672 DCHECK_EQ(kData, property_details_.kind()); 673 DCHECK(!FLAG_track_constant_fields); 674 DCHECK(!IsElement()); 675 return descriptor_number(); 676 } 677 678 Handle<Map> LookupIterator::GetFieldOwnerMap() const { 679 DCHECK(has_property_); 680 DCHECK(holder_->HasFastProperties()); 681 DCHECK_EQ(kField, property_details_.location()); 682 DCHECK(!IsElement()); 683 Map* holder_map = holder_->map(); 684 return handle(holder_map->FindFieldOwner(descriptor_number()), isolate_); 685 } 686 687 FieldIndex LookupIterator::GetFieldIndex() const { 688 DCHECK(has_property_); 689 DCHECK(holder_->HasFastProperties()); 690 DCHECK_EQ(kField, property_details_.location()); 691 DCHECK(!IsElement()); 692 Map* holder_map = holder_->map(); 693 int index = 694 holder_map->instance_descriptors()->GetFieldIndex(descriptor_number()); 695 bool is_double = representation().IsDouble(); 696 return FieldIndex::ForPropertyIndex(holder_map, index, is_double); 697 } 698 699 Handle<FieldType> LookupIterator::GetFieldType() const { 700 DCHECK(has_property_); 701 DCHECK(holder_->HasFastProperties()); 702 DCHECK_EQ(kField, property_details_.location()); 703 return handle( 704 holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()), 705 isolate_); 706 } 707 708 709 Handle<PropertyCell> LookupIterator::GetPropertyCell() const { 710 DCHECK(!IsElement()); 711 Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>(); 712 Object* value = holder->global_dictionary()->ValueAt(dictionary_entry()); 713 DCHECK(value->IsPropertyCell()); 714 return handle(PropertyCell::cast(value), isolate_); 715 } 716 717 718 Handle<Object> LookupIterator::GetAccessors() const { 719 DCHECK_EQ(ACCESSOR, state_); 720 return FetchValue(); 721 } 722 723 724 Handle<Object> LookupIterator::GetDataValue() const { 725 DCHECK_EQ(DATA, state_); 726 Handle<Object> value = FetchValue(); 727 return value; 728 } 729 730 void LookupIterator::WriteDataValue(Handle<Object> value, 731 bool initializing_store) { 732 DCHECK_EQ(DATA, state_); 733 Handle<JSReceiver> holder = GetHolder<JSReceiver>(); 734 if (IsElement()) { 735 Handle<JSObject> object = Handle<JSObject>::cast(holder); 736 ElementsAccessor* accessor = object->GetElementsAccessor(); 737 accessor->Set(object, number_, *value); 738 } else if (holder->HasFastProperties()) { 739 if (property_details_.location() == kField) { 740 // Check that in case of kConst field the existing value is equal to 741 // |value|. 742 DCHECK_IMPLIES( 743 !initializing_store && property_details_.constness() == kConst, 744 IsConstFieldValueEqualTo(*value)); 745 JSObject::cast(*holder)->WriteToField(descriptor_number(), 746 property_details_, *value); 747 } else { 748 DCHECK_EQ(kDescriptor, property_details_.location()); 749 DCHECK_EQ(kConst, property_details_.constness()); 750 } 751 } else if (holder->IsJSGlobalObject()) { 752 GlobalDictionary* dictionary = JSObject::cast(*holder)->global_dictionary(); 753 Object* cell = dictionary->ValueAt(dictionary_entry()); 754 DCHECK(cell->IsPropertyCell()); 755 PropertyCell::cast(cell)->set_value(*value); 756 } else { 757 NameDictionary* dictionary = holder->property_dictionary(); 758 dictionary->ValueAtPut(dictionary_entry(), *value); 759 } 760 } 761 762 template <bool is_element> 763 bool LookupIterator::SkipInterceptor(JSObject* holder) { 764 auto info = GetInterceptor<is_element>(holder); 765 // TODO(dcarney): check for symbol/can_intercept_symbols here as well. 766 if (info->non_masking()) { 767 switch (interceptor_state_) { 768 case InterceptorState::kUninitialized: 769 interceptor_state_ = InterceptorState::kSkipNonMasking; 770 // Fall through. 771 case InterceptorState::kSkipNonMasking: 772 return true; 773 case InterceptorState::kProcessNonMasking: 774 return false; 775 } 776 } 777 return interceptor_state_ == InterceptorState::kProcessNonMasking; 778 } 779 780 JSReceiver* LookupIterator::NextHolder(Map* map) { 781 DisallowHeapAllocation no_gc; 782 if (map->prototype() == heap()->null_value()) return NULL; 783 if (!check_prototype_chain() && !map->has_hidden_prototype()) return NULL; 784 return JSReceiver::cast(map->prototype()); 785 } 786 787 LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const { 788 DCHECK(!IsElement()); 789 if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND; 790 791 Handle<String> name_string = Handle<String>::cast(name_); 792 if (name_string->length() == 0) return NOT_FOUND; 793 794 return IsSpecialIndex(isolate_->unicode_cache(), *name_string) 795 ? INTEGER_INDEXED_EXOTIC 796 : NOT_FOUND; 797 } 798 799 namespace { 800 801 template <bool is_element> 802 bool HasInterceptor(Map* map) { 803 return is_element ? map->has_indexed_interceptor() 804 : map->has_named_interceptor(); 805 } 806 807 } // namespace 808 809 template <bool is_element> 810 LookupIterator::State LookupIterator::LookupInSpecialHolder( 811 Map* const map, JSReceiver* const holder) { 812 STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY); 813 switch (state_) { 814 case NOT_FOUND: 815 if (map->IsJSProxyMap()) { 816 if (is_element || !name_->IsPrivate()) return JSPROXY; 817 } 818 if (map->is_access_check_needed()) { 819 if (is_element || !name_->IsPrivate()) return ACCESS_CHECK; 820 } 821 // Fall through. 822 case ACCESS_CHECK: 823 if (check_interceptor() && HasInterceptor<is_element>(map) && 824 !SkipInterceptor<is_element>(JSObject::cast(holder))) { 825 if (is_element || !name_->IsPrivate()) return INTERCEPTOR; 826 } 827 // Fall through. 828 case INTERCEPTOR: 829 if (!is_element && map->IsJSGlobalObjectMap()) { 830 GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary(); 831 int number = dict->FindEntry(name_); 832 if (number == GlobalDictionary::kNotFound) return NOT_FOUND; 833 number_ = static_cast<uint32_t>(number); 834 DCHECK(dict->ValueAt(number_)->IsPropertyCell()); 835 PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_)); 836 if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND; 837 property_details_ = cell->property_details(); 838 has_property_ = true; 839 switch (property_details_.kind()) { 840 case v8::internal::kData: 841 return DATA; 842 case v8::internal::kAccessor: 843 return ACCESSOR; 844 } 845 } 846 return LookupInRegularHolder<is_element>(map, holder); 847 case ACCESSOR: 848 case DATA: 849 return NOT_FOUND; 850 case INTEGER_INDEXED_EXOTIC: 851 case JSPROXY: 852 case TRANSITION: 853 UNREACHABLE(); 854 } 855 UNREACHABLE(); 856 return NOT_FOUND; 857 } 858 859 template <bool is_element> 860 LookupIterator::State LookupIterator::LookupInRegularHolder( 861 Map* const map, JSReceiver* const holder) { 862 DisallowHeapAllocation no_gc; 863 if (interceptor_state_ == InterceptorState::kProcessNonMasking) { 864 return NOT_FOUND; 865 } 866 867 if (is_element) { 868 JSObject* js_object = JSObject::cast(holder); 869 ElementsAccessor* accessor = js_object->GetElementsAccessor(); 870 FixedArrayBase* backing_store = js_object->elements(); 871 number_ = 872 accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_); 873 if (number_ == kMaxUInt32) { 874 return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND; 875 } 876 property_details_ = accessor->GetDetails(js_object, number_); 877 } else if (!map->is_dictionary_map()) { 878 DescriptorArray* descriptors = map->instance_descriptors(); 879 int number = descriptors->SearchWithCache(isolate_, *name_, map); 880 if (number == DescriptorArray::kNotFound) return NotFound(holder); 881 number_ = static_cast<uint32_t>(number); 882 property_details_ = descriptors->GetDetails(number_); 883 } else { 884 NameDictionary* dict = holder->property_dictionary(); 885 int number = dict->FindEntry(name_); 886 if (number == NameDictionary::kNotFound) return NotFound(holder); 887 number_ = static_cast<uint32_t>(number); 888 property_details_ = dict->DetailsAt(number_); 889 } 890 has_property_ = true; 891 switch (property_details_.kind()) { 892 case v8::internal::kData: 893 return DATA; 894 case v8::internal::kAccessor: 895 return ACCESSOR; 896 } 897 898 UNREACHABLE(); 899 return state_; 900 } 901 902 Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck() 903 const { 904 DCHECK_EQ(ACCESS_CHECK, state_); 905 DisallowHeapAllocation no_gc; 906 AccessCheckInfo* access_check_info = 907 AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_)); 908 if (access_check_info) { 909 Object* interceptor = IsElement() ? access_check_info->indexed_interceptor() 910 : access_check_info->named_interceptor(); 911 if (interceptor) { 912 return handle(InterceptorInfo::cast(interceptor), isolate_); 913 } 914 } 915 return Handle<InterceptorInfo>(); 916 } 917 918 bool LookupIterator::TryLookupCachedProperty() { 919 return state() == LookupIterator::ACCESSOR && 920 GetAccessors()->IsAccessorPair() && LookupCachedProperty(); 921 } 922 923 bool LookupIterator::LookupCachedProperty() { 924 DCHECK_EQ(state(), LookupIterator::ACCESSOR); 925 DCHECK(GetAccessors()->IsAccessorPair()); 926 927 AccessorPair* accessor_pair = AccessorPair::cast(*GetAccessors()); 928 Handle<Object> getter(accessor_pair->getter(), isolate()); 929 MaybeHandle<Name> maybe_name = 930 FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), getter); 931 if (maybe_name.is_null()) return false; 932 933 // We have found a cached property! Modify the iterator accordingly. 934 name_ = maybe_name.ToHandleChecked(); 935 Restart(); 936 CHECK_EQ(state(), LookupIterator::DATA); 937 return true; 938 } 939 940 } // namespace internal 941 } // namespace v8 942