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->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) { 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()->has_instance_symbol()) { 195 if (!isolate_->IsHasInstanceLookupChainIntact()) return; 196 isolate_->InvalidateHasInstanceProtector(); 197 } else if (*name_ == heap()->iterator_symbol()) { 198 if (!isolate_->IsArrayIteratorLookupChainIntact()) return; 199 if (holder_->IsJSArray()) { 200 isolate_->InvalidateArrayIteratorProtector(); 201 } 202 } 203 } 204 205 void LookupIterator::PrepareForDataProperty(Handle<Object> value) { 206 DCHECK(state_ == DATA || state_ == ACCESSOR); 207 DCHECK(HolderIsReceiverOrHiddenPrototype()); 208 209 Handle<JSObject> holder = GetHolder<JSObject>(); 210 211 if (IsElement()) { 212 ElementsKind kind = holder->GetElementsKind(); 213 ElementsKind to = value->OptimalElementsKind(); 214 if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to); 215 to = GetMoreGeneralElementsKind(kind, to); 216 217 if (kind != to) { 218 JSObject::TransitionElementsKind(holder, to); 219 } 220 221 // Copy the backing store if it is copy-on-write. 222 if (IsFastSmiOrObjectElementsKind(to)) { 223 JSObject::EnsureWritableFastElements(holder); 224 } 225 return; 226 } 227 228 if (holder->IsJSGlobalObject()) { 229 Handle<GlobalDictionary> dictionary(holder->global_dictionary()); 230 Handle<PropertyCell> cell( 231 PropertyCell::cast(dictionary->ValueAt(dictionary_entry()))); 232 DCHECK(!cell->IsTheHole(isolate_)); 233 property_details_ = cell->property_details(); 234 PropertyCell::PrepareForValue(dictionary, dictionary_entry(), value, 235 property_details_); 236 return; 237 } 238 if (!holder->HasFastProperties()) return; 239 240 Handle<Map> old_map(holder->map(), isolate_); 241 Handle<Map> new_map = 242 Map::PrepareForDataProperty(old_map, descriptor_number(), value); 243 244 if (old_map.is_identical_to(new_map)) { 245 // Update the property details if the representation was None. 246 if (representation().IsNone()) { 247 property_details_ = 248 new_map->instance_descriptors()->GetDetails(descriptor_number()); 249 } 250 return; 251 } 252 253 JSObject::MigrateToMap(holder, new_map); 254 ReloadPropertyInformation<false>(); 255 } 256 257 258 void LookupIterator::ReconfigureDataProperty(Handle<Object> value, 259 PropertyAttributes attributes) { 260 DCHECK(state_ == DATA || state_ == ACCESSOR); 261 DCHECK(HolderIsReceiverOrHiddenPrototype()); 262 Handle<JSObject> holder = GetHolder<JSObject>(); 263 if (IsElement()) { 264 DCHECK(!holder->HasFixedTypedArrayElements()); 265 DCHECK(attributes != NONE || !holder->HasFastElements()); 266 Handle<FixedArrayBase> elements(holder->elements()); 267 holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value, 268 attributes); 269 ReloadPropertyInformation<true>(); 270 } else if (holder->HasFastProperties()) { 271 Handle<Map> old_map(holder->map(), isolate_); 272 Handle<Map> new_map = Map::ReconfigureExistingProperty( 273 old_map, descriptor_number(), i::kData, attributes); 274 new_map = Map::PrepareForDataProperty(new_map, descriptor_number(), value); 275 JSObject::MigrateToMap(holder, new_map); 276 ReloadPropertyInformation<false>(); 277 } else { 278 PropertyDetails details(attributes, v8::internal::DATA, 0, 279 PropertyCellType::kMutable); 280 if (holder->IsJSGlobalObject()) { 281 Handle<GlobalDictionary> dictionary(holder->global_dictionary()); 282 283 Handle<PropertyCell> cell = PropertyCell::PrepareForValue( 284 dictionary, dictionary_entry(), value, details); 285 cell->set_value(*value); 286 property_details_ = cell->property_details(); 287 } else { 288 Handle<NameDictionary> dictionary(holder->property_dictionary()); 289 PropertyDetails original_details = 290 dictionary->DetailsAt(dictionary_entry()); 291 int enumeration_index = original_details.dictionary_index(); 292 DCHECK(enumeration_index > 0); 293 details = details.set_index(enumeration_index); 294 dictionary->SetEntry(dictionary_entry(), name(), value, details); 295 property_details_ = details; 296 } 297 state_ = DATA; 298 } 299 300 WriteDataValue(value); 301 302 #if VERIFY_HEAP 303 if (FLAG_verify_heap) { 304 holder->JSObjectVerify(); 305 } 306 #endif 307 } 308 309 // Can only be called when the receiver is a JSObject. JSProxy has to be handled 310 // via a trap. Adding properties to primitive values is not observable. 311 void LookupIterator::PrepareTransitionToDataProperty( 312 Handle<JSObject> receiver, Handle<Object> value, 313 PropertyAttributes attributes, Object::StoreFromKeyed store_mode) { 314 DCHECK(receiver.is_identical_to(GetStoreTarget())); 315 if (state_ == TRANSITION) return; 316 317 if (!IsElement() && name()->IsPrivate()) { 318 attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM); 319 } 320 321 DCHECK(state_ != LookupIterator::ACCESSOR || 322 (GetAccessors()->IsAccessorInfo() && 323 AccessorInfo::cast(*GetAccessors())->is_special_data_property())); 324 DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_); 325 DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype()); 326 327 Handle<Map> map(receiver->map(), isolate_); 328 329 // Dictionary maps can always have additional data properties. 330 if (map->is_dictionary_map()) { 331 state_ = TRANSITION; 332 if (map->IsJSGlobalObjectMap()) { 333 // Install a property cell. 334 Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(receiver); 335 int entry; 336 Handle<PropertyCell> cell = JSGlobalObject::EnsureEmptyPropertyCell( 337 global, name(), PropertyCellType::kUninitialized, &entry); 338 Handle<GlobalDictionary> dictionary(global->global_dictionary(), 339 isolate_); 340 DCHECK(cell->value()->IsTheHole(isolate_)); 341 DCHECK(!value->IsTheHole(isolate_)); 342 transition_ = cell; 343 // Assign an enumeration index to the property and update 344 // SetNextEnumerationIndex. 345 int index = dictionary->NextEnumerationIndex(); 346 dictionary->SetNextEnumerationIndex(index + 1); 347 property_details_ = PropertyDetails(attributes, i::DATA, index, 348 PropertyCellType::kUninitialized); 349 PropertyCellType new_type = 350 PropertyCell::UpdatedType(cell, value, property_details_); 351 property_details_ = property_details_.set_cell_type(new_type); 352 cell->set_property_details(property_details_); 353 number_ = entry; 354 has_property_ = true; 355 } else { 356 // Don't set enumeration index (it will be set during value store). 357 property_details_ = 358 PropertyDetails(attributes, i::DATA, 0, PropertyCellType::kNoCell); 359 transition_ = map; 360 } 361 return; 362 } 363 364 Handle<Map> transition = 365 Map::TransitionToDataProperty(map, name_, value, attributes, store_mode); 366 state_ = TRANSITION; 367 transition_ = transition; 368 369 if (transition->is_dictionary_map()) { 370 // Don't set enumeration index (it will be set during value store). 371 property_details_ = 372 PropertyDetails(attributes, i::DATA, 0, PropertyCellType::kNoCell); 373 } else { 374 property_details_ = transition->GetLastDescriptorDetails(); 375 has_property_ = true; 376 } 377 } 378 379 void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) { 380 DCHECK_EQ(TRANSITION, state_); 381 382 DCHECK(receiver.is_identical_to(GetStoreTarget())); 383 holder_ = receiver; 384 if (receiver->IsJSGlobalObject()) { 385 state_ = DATA; 386 return; 387 } 388 Handle<Map> transition = transition_map(); 389 bool simple_transition = transition->GetBackPointer() == receiver->map(); 390 JSObject::MigrateToMap(receiver, transition); 391 392 if (simple_transition) { 393 int number = transition->LastAdded(); 394 number_ = static_cast<uint32_t>(number); 395 property_details_ = transition->GetLastDescriptorDetails(); 396 state_ = DATA; 397 } else if (receiver->map()->is_dictionary_map()) { 398 Handle<NameDictionary> dictionary(receiver->property_dictionary(), 399 isolate_); 400 int entry; 401 dictionary = NameDictionary::Add(dictionary, name(), 402 isolate_->factory()->uninitialized_value(), 403 property_details_, &entry); 404 receiver->set_properties(*dictionary); 405 // Reload details containing proper enumeration index value. 406 property_details_ = dictionary->DetailsAt(entry); 407 number_ = entry; 408 has_property_ = true; 409 state_ = DATA; 410 411 } else { 412 ReloadPropertyInformation<false>(); 413 } 414 } 415 416 417 void LookupIterator::Delete() { 418 Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_); 419 if (IsElement()) { 420 Handle<JSObject> object = Handle<JSObject>::cast(holder); 421 ElementsAccessor* accessor = object->GetElementsAccessor(); 422 accessor->Delete(object, number_); 423 } else { 424 bool is_prototype_map = holder->map()->is_prototype_map(); 425 RuntimeCallTimerScope stats_scope( 426 isolate_, is_prototype_map 427 ? &RuntimeCallStats::PrototypeObject_DeleteProperty 428 : &RuntimeCallStats::Object_DeleteProperty); 429 430 PropertyNormalizationMode mode = 431 is_prototype_map ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES; 432 433 if (holder->HasFastProperties()) { 434 JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0, 435 "DeletingProperty"); 436 ReloadPropertyInformation<false>(); 437 } 438 // TODO(verwaest): Get rid of the name_ argument. 439 JSReceiver::DeleteNormalizedProperty(holder, name_, number_); 440 if (holder->IsJSObject()) { 441 JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder)); 442 } 443 } 444 state_ = NOT_FOUND; 445 } 446 447 void LookupIterator::TransitionToAccessorProperty( 448 Handle<Object> getter, Handle<Object> setter, 449 PropertyAttributes attributes) { 450 DCHECK(!getter->IsNull(isolate_) || !setter->IsNull(isolate_)); 451 // Can only be called when the receiver is a JSObject. JSProxy has to be 452 // handled via a trap. Adding properties to primitive values is not 453 // observable. 454 Handle<JSObject> receiver = GetStoreTarget(); 455 if (!IsElement() && name()->IsPrivate()) { 456 attributes = static_cast<PropertyAttributes>(attributes | DONT_ENUM); 457 } 458 459 if (!IsElement() && !receiver->map()->is_dictionary_map()) { 460 Handle<Map> old_map(receiver->map(), isolate_); 461 462 if (!holder_.is_identical_to(receiver)) { 463 holder_ = receiver; 464 state_ = NOT_FOUND; 465 } else if (state_ == INTERCEPTOR) { 466 LookupInRegularHolder<false>(*old_map, *holder_); 467 } 468 int descriptor = 469 IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound; 470 471 Handle<Map> new_map = Map::TransitionToAccessorProperty( 472 isolate_, old_map, name_, descriptor, getter, setter, attributes); 473 bool simple_transition = new_map->GetBackPointer() == receiver->map(); 474 JSObject::MigrateToMap(receiver, new_map); 475 476 if (simple_transition) { 477 int number = new_map->LastAdded(); 478 number_ = static_cast<uint32_t>(number); 479 property_details_ = new_map->GetLastDescriptorDetails(); 480 state_ = ACCESSOR; 481 return; 482 } 483 484 ReloadPropertyInformation<false>(); 485 if (!new_map->is_dictionary_map()) return; 486 } 487 488 Handle<AccessorPair> pair; 489 if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) { 490 pair = Handle<AccessorPair>::cast(GetAccessors()); 491 // If the component and attributes are identical, nothing has to be done. 492 if (pair->Equals(*getter, *setter)) { 493 if (property_details().attributes() == attributes) { 494 if (!IsElement()) JSObject::ReoptimizeIfPrototype(receiver); 495 return; 496 } 497 } else { 498 pair = AccessorPair::Copy(pair); 499 pair->SetComponents(*getter, *setter); 500 } 501 } else { 502 pair = factory()->NewAccessorPair(); 503 pair->SetComponents(*getter, *setter); 504 } 505 506 TransitionToAccessorPair(pair, attributes); 507 508 #if VERIFY_HEAP 509 if (FLAG_verify_heap) { 510 receiver->JSObjectVerify(); 511 } 512 #endif 513 } 514 515 516 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair, 517 PropertyAttributes attributes) { 518 Handle<JSObject> receiver = GetStoreTarget(); 519 holder_ = receiver; 520 521 PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0, 522 PropertyCellType::kMutable); 523 524 if (IsElement()) { 525 // TODO(verwaest): Move code into the element accessor. 526 Handle<SeededNumberDictionary> dictionary = 527 JSObject::NormalizeElements(receiver); 528 529 // We unconditionally pass used_as_prototype=false here because the call 530 // to RequireSlowElements takes care of the required IC clearing and 531 // we don't want to walk the heap twice. 532 dictionary = 533 SeededNumberDictionary::Set(dictionary, index_, pair, details, false); 534 receiver->RequireSlowElements(*dictionary); 535 536 if (receiver->HasSlowArgumentsElements()) { 537 FixedArray* parameter_map = FixedArray::cast(receiver->elements()); 538 uint32_t length = parameter_map->length() - 2; 539 if (number_ < length) { 540 parameter_map->set(number_ + 2, heap()->the_hole_value()); 541 } 542 FixedArray::cast(receiver->elements())->set(1, *dictionary); 543 } else { 544 receiver->set_elements(*dictionary); 545 } 546 547 ReloadPropertyInformation<true>(); 548 } else { 549 PropertyNormalizationMode mode = receiver->map()->is_prototype_map() 550 ? KEEP_INOBJECT_PROPERTIES 551 : CLEAR_INOBJECT_PROPERTIES; 552 // Normalize object to make this operation simple. 553 JSObject::NormalizeProperties(receiver, mode, 0, 554 "TransitionToAccessorPair"); 555 556 JSObject::SetNormalizedProperty(receiver, name_, pair, details); 557 JSObject::ReoptimizeIfPrototype(receiver); 558 559 ReloadPropertyInformation<false>(); 560 } 561 } 562 563 564 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const { 565 DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY); 566 // Optimization that only works if configuration_ is not mutable. 567 if (!check_prototype_chain()) return true; 568 DisallowHeapAllocation no_gc; 569 if (*receiver_ == *holder_) return true; 570 if (!receiver_->IsJSReceiver()) return false; 571 JSReceiver* current = JSReceiver::cast(*receiver_); 572 JSReceiver* object = *holder_; 573 if (!current->map()->has_hidden_prototype()) return false; 574 // JSProxy do not occur as hidden prototypes. 575 if (object->IsJSProxy()) return false; 576 PrototypeIterator iter(isolate(), current, kStartAtPrototype, 577 PrototypeIterator::END_AT_NON_HIDDEN); 578 while (!iter.IsAtEnd()) { 579 if (iter.GetCurrent<JSReceiver>() == object) return true; 580 iter.Advance(); 581 } 582 return false; 583 } 584 585 586 Handle<Object> LookupIterator::FetchValue() const { 587 Object* result = NULL; 588 if (IsElement()) { 589 Handle<JSObject> holder = GetHolder<JSObject>(); 590 ElementsAccessor* accessor = holder->GetElementsAccessor(); 591 return accessor->Get(holder, number_); 592 } else if (holder_->IsJSGlobalObject()) { 593 Handle<JSObject> holder = GetHolder<JSObject>(); 594 result = holder->global_dictionary()->ValueAt(number_); 595 DCHECK(result->IsPropertyCell()); 596 result = PropertyCell::cast(result)->value(); 597 } else if (!holder_->HasFastProperties()) { 598 result = holder_->property_dictionary()->ValueAt(number_); 599 } else if (property_details_.type() == v8::internal::DATA) { 600 Handle<JSObject> holder = GetHolder<JSObject>(); 601 FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_); 602 return JSObject::FastPropertyAt(holder, property_details_.representation(), 603 field_index); 604 } else { 605 result = holder_->map()->instance_descriptors()->GetValue(number_); 606 } 607 return handle(result, isolate_); 608 } 609 610 int LookupIterator::GetFieldDescriptorIndex() const { 611 DCHECK(has_property_); 612 DCHECK(holder_->HasFastProperties()); 613 DCHECK_EQ(v8::internal::DATA, property_details_.type()); 614 return descriptor_number(); 615 } 616 617 int LookupIterator::GetAccessorIndex() const { 618 DCHECK(has_property_); 619 DCHECK(holder_->HasFastProperties()); 620 DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type()); 621 return descriptor_number(); 622 } 623 624 625 int LookupIterator::GetConstantIndex() const { 626 DCHECK(has_property_); 627 DCHECK(holder_->HasFastProperties()); 628 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type()); 629 DCHECK(!IsElement()); 630 return descriptor_number(); 631 } 632 633 634 FieldIndex LookupIterator::GetFieldIndex() const { 635 DCHECK(has_property_); 636 DCHECK(holder_->HasFastProperties()); 637 DCHECK_EQ(v8::internal::DATA, property_details_.type()); 638 DCHECK(!IsElement()); 639 Map* holder_map = holder_->map(); 640 int index = 641 holder_map->instance_descriptors()->GetFieldIndex(descriptor_number()); 642 bool is_double = representation().IsDouble(); 643 return FieldIndex::ForPropertyIndex(holder_map, index, is_double); 644 } 645 646 Handle<FieldType> LookupIterator::GetFieldType() const { 647 DCHECK(has_property_); 648 DCHECK(holder_->HasFastProperties()); 649 DCHECK_EQ(v8::internal::DATA, property_details_.type()); 650 return handle( 651 holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()), 652 isolate_); 653 } 654 655 656 Handle<PropertyCell> LookupIterator::GetPropertyCell() const { 657 DCHECK(!IsElement()); 658 Handle<JSGlobalObject> holder = GetHolder<JSGlobalObject>(); 659 Object* value = holder->global_dictionary()->ValueAt(dictionary_entry()); 660 DCHECK(value->IsPropertyCell()); 661 return handle(PropertyCell::cast(value), isolate_); 662 } 663 664 665 Handle<Object> LookupIterator::GetAccessors() const { 666 DCHECK_EQ(ACCESSOR, state_); 667 return FetchValue(); 668 } 669 670 671 Handle<Object> LookupIterator::GetDataValue() const { 672 DCHECK_EQ(DATA, state_); 673 Handle<Object> value = FetchValue(); 674 return value; 675 } 676 677 678 void LookupIterator::WriteDataValue(Handle<Object> value) { 679 DCHECK_EQ(DATA, state_); 680 Handle<JSReceiver> holder = GetHolder<JSReceiver>(); 681 if (IsElement()) { 682 Handle<JSObject> object = Handle<JSObject>::cast(holder); 683 ElementsAccessor* accessor = object->GetElementsAccessor(); 684 accessor->Set(object, number_, *value); 685 } else if (holder->HasFastProperties()) { 686 if (property_details_.type() == v8::internal::DATA) { 687 JSObject::cast(*holder)->WriteToField(descriptor_number(), 688 property_details_, *value); 689 } else { 690 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type()); 691 } 692 } else if (holder->IsJSGlobalObject()) { 693 GlobalDictionary* dictionary = JSObject::cast(*holder)->global_dictionary(); 694 Object* cell = dictionary->ValueAt(dictionary_entry()); 695 DCHECK(cell->IsPropertyCell()); 696 PropertyCell::cast(cell)->set_value(*value); 697 } else { 698 NameDictionary* dictionary = holder->property_dictionary(); 699 dictionary->ValueAtPut(dictionary_entry(), *value); 700 } 701 } 702 703 template <bool is_element> 704 bool LookupIterator::SkipInterceptor(JSObject* holder) { 705 auto info = GetInterceptor<is_element>(holder); 706 // TODO(dcarney): check for symbol/can_intercept_symbols here as well. 707 if (info->non_masking()) { 708 switch (interceptor_state_) { 709 case InterceptorState::kUninitialized: 710 interceptor_state_ = InterceptorState::kSkipNonMasking; 711 // Fall through. 712 case InterceptorState::kSkipNonMasking: 713 return true; 714 case InterceptorState::kProcessNonMasking: 715 return false; 716 } 717 } 718 return interceptor_state_ == InterceptorState::kProcessNonMasking; 719 } 720 721 JSReceiver* LookupIterator::NextHolder(Map* map) { 722 DisallowHeapAllocation no_gc; 723 if (map->prototype() == heap()->null_value()) return NULL; 724 if (!check_prototype_chain() && !map->has_hidden_prototype()) return NULL; 725 return JSReceiver::cast(map->prototype()); 726 } 727 728 LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const { 729 DCHECK(!IsElement()); 730 if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND; 731 732 Handle<String> name_string = Handle<String>::cast(name_); 733 if (name_string->length() == 0) return NOT_FOUND; 734 735 return IsSpecialIndex(isolate_->unicode_cache(), *name_string) 736 ? INTEGER_INDEXED_EXOTIC 737 : NOT_FOUND; 738 } 739 740 namespace { 741 742 template <bool is_element> 743 bool HasInterceptor(Map* map) { 744 return is_element ? map->has_indexed_interceptor() 745 : map->has_named_interceptor(); 746 } 747 748 } // namespace 749 750 template <bool is_element> 751 LookupIterator::State LookupIterator::LookupInSpecialHolder( 752 Map* const map, JSReceiver* const holder) { 753 STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY); 754 switch (state_) { 755 case NOT_FOUND: 756 if (map->IsJSProxyMap()) { 757 if (is_element || !name_->IsPrivate()) return JSPROXY; 758 } 759 if (map->is_access_check_needed()) { 760 if (is_element || !name_->IsPrivate()) return ACCESS_CHECK; 761 } 762 // Fall through. 763 case ACCESS_CHECK: 764 if (check_interceptor() && HasInterceptor<is_element>(map) && 765 !SkipInterceptor<is_element>(JSObject::cast(holder))) { 766 if (is_element || !name_->IsPrivate()) return INTERCEPTOR; 767 } 768 // Fall through. 769 case INTERCEPTOR: 770 if (!is_element && map->IsJSGlobalObjectMap()) { 771 GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary(); 772 int number = dict->FindEntry(name_); 773 if (number == GlobalDictionary::kNotFound) return NOT_FOUND; 774 number_ = static_cast<uint32_t>(number); 775 DCHECK(dict->ValueAt(number_)->IsPropertyCell()); 776 PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_)); 777 if (cell->value()->IsTheHole(isolate_)) return NOT_FOUND; 778 property_details_ = cell->property_details(); 779 has_property_ = true; 780 switch (property_details_.kind()) { 781 case v8::internal::kData: 782 return DATA; 783 case v8::internal::kAccessor: 784 return ACCESSOR; 785 } 786 } 787 return LookupInRegularHolder<is_element>(map, holder); 788 case ACCESSOR: 789 case DATA: 790 return NOT_FOUND; 791 case INTEGER_INDEXED_EXOTIC: 792 case JSPROXY: 793 case TRANSITION: 794 UNREACHABLE(); 795 } 796 UNREACHABLE(); 797 return NOT_FOUND; 798 } 799 800 template <bool is_element> 801 LookupIterator::State LookupIterator::LookupInRegularHolder( 802 Map* const map, JSReceiver* const holder) { 803 DisallowHeapAllocation no_gc; 804 if (interceptor_state_ == InterceptorState::kProcessNonMasking) { 805 return NOT_FOUND; 806 } 807 808 if (is_element) { 809 JSObject* js_object = JSObject::cast(holder); 810 ElementsAccessor* accessor = js_object->GetElementsAccessor(); 811 FixedArrayBase* backing_store = js_object->elements(); 812 number_ = 813 accessor->GetEntryForIndex(isolate_, js_object, backing_store, index_); 814 if (number_ == kMaxUInt32) { 815 return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND; 816 } 817 property_details_ = accessor->GetDetails(js_object, number_); 818 } else if (!map->is_dictionary_map()) { 819 DescriptorArray* descriptors = map->instance_descriptors(); 820 int number = descriptors->SearchWithCache(isolate_, *name_, map); 821 if (number == DescriptorArray::kNotFound) return NotFound(holder); 822 number_ = static_cast<uint32_t>(number); 823 property_details_ = descriptors->GetDetails(number_); 824 } else { 825 NameDictionary* dict = holder->property_dictionary(); 826 int number = dict->FindEntry(name_); 827 if (number == NameDictionary::kNotFound) return NotFound(holder); 828 number_ = static_cast<uint32_t>(number); 829 property_details_ = dict->DetailsAt(number_); 830 } 831 has_property_ = true; 832 switch (property_details_.kind()) { 833 case v8::internal::kData: 834 return DATA; 835 case v8::internal::kAccessor: 836 return ACCESSOR; 837 } 838 839 UNREACHABLE(); 840 return state_; 841 } 842 843 Handle<InterceptorInfo> LookupIterator::GetInterceptorForFailedAccessCheck() 844 const { 845 DCHECK_EQ(ACCESS_CHECK, state_); 846 DisallowHeapAllocation no_gc; 847 AccessCheckInfo* access_check_info = 848 AccessCheckInfo::Get(isolate_, Handle<JSObject>::cast(holder_)); 849 if (access_check_info) { 850 Object* interceptor = IsElement() ? access_check_info->indexed_interceptor() 851 : access_check_info->named_interceptor(); 852 if (interceptor) { 853 return handle(InterceptorInfo::cast(interceptor), isolate_); 854 } 855 } 856 return Handle<InterceptorInfo>(); 857 } 858 859 bool LookupIterator::TryLookupCachedProperty() { 860 return state() == LookupIterator::ACCESSOR && 861 GetAccessors()->IsAccessorPair() && LookupCachedProperty(); 862 } 863 864 bool LookupIterator::LookupCachedProperty() { 865 DCHECK_EQ(state(), LookupIterator::ACCESSOR); 866 DCHECK(GetAccessors()->IsAccessorPair()); 867 868 AccessorPair* accessor_pair = AccessorPair::cast(*GetAccessors()); 869 Handle<Object> getter(accessor_pair->getter(), isolate()); 870 MaybeHandle<Name> maybe_name = 871 FunctionTemplateInfo::TryGetCachedPropertyName(isolate(), getter); 872 if (maybe_name.is_null()) return false; 873 874 // We have found a cached property! Modify the iterator accordingly. 875 name_ = maybe_name.ToHandleChecked(); 876 Restart(); 877 CHECK_EQ(state(), LookupIterator::DATA); 878 return true; 879 } 880 881 } // namespace internal 882 } // namespace v8 883