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/isolate-inl.h" 11 12 namespace v8 { 13 namespace internal { 14 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 48 void LookupIterator::Next() { 49 DCHECK_NE(JSPROXY, state_); 50 DCHECK_NE(TRANSITION, state_); 51 DisallowHeapAllocation no_gc; 52 has_property_ = false; 53 54 JSReceiver* holder = *holder_; 55 Map* map = *holder_map_; 56 57 // Perform lookup on current holder. 58 state_ = LookupInHolder(map, holder); 59 if (IsFound()) return; 60 61 // Continue lookup if lookup on current holder failed. 62 do { 63 JSReceiver* maybe_holder = NextHolder(map); 64 if (maybe_holder == nullptr) { 65 if (interceptor_state_ == InterceptorState::kSkipNonMasking) { 66 RestartLookupForNonMaskingInterceptors(); 67 return; 68 } 69 break; 70 } 71 holder = maybe_holder; 72 map = holder->map(); 73 state_ = LookupInHolder(map, holder); 74 } while (!IsFound()); 75 76 if (holder != *holder_) { 77 holder_ = handle(holder, isolate_); 78 holder_map_ = handle(map, isolate_); 79 } 80 } 81 82 83 void LookupIterator::RestartInternal(InterceptorState interceptor_state) { 84 state_ = NOT_FOUND; 85 interceptor_state_ = interceptor_state; 86 property_details_ = PropertyDetails::Empty(); 87 holder_ = initial_holder_; 88 holder_map_ = handle(holder_->map(), isolate_); 89 number_ = DescriptorArray::kNotFound; 90 Next(); 91 } 92 93 94 // static 95 Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver( 96 Isolate* isolate, Handle<Object> receiver, uint32_t index) { 97 // Strings are the only objects with properties (only elements) directly on 98 // the wrapper. Hence we can skip generating the wrapper for all other cases. 99 if (index != kMaxUInt32 && receiver->IsString() && 100 index < static_cast<uint32_t>(String::cast(*receiver)->length())) { 101 // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native 102 // context, ensuring that we don't leak it into JS? 103 Handle<JSFunction> constructor = isolate->string_function(); 104 Handle<JSObject> result = isolate->factory()->NewJSObject(constructor); 105 Handle<JSValue>::cast(result)->set_value(*receiver); 106 return result; 107 } 108 auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate); 109 if (root->IsNull()) { 110 unsigned int magic = 0xbbbbbbbb; 111 isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic); 112 } 113 return Handle<JSReceiver>::cast(root); 114 } 115 116 117 Handle<Map> LookupIterator::GetReceiverMap() const { 118 if (receiver_->IsNumber()) return factory()->heap_number_map(); 119 return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_); 120 } 121 122 123 Handle<JSObject> LookupIterator::GetStoreTarget() const { 124 if (receiver_->IsJSGlobalProxy()) { 125 PrototypeIterator iter(isolate(), receiver_); 126 if (iter.IsAtEnd()) return Handle<JSGlobalProxy>::cast(receiver_); 127 return PrototypeIterator::GetCurrent<JSGlobalObject>(iter); 128 } 129 return Handle<JSObject>::cast(receiver_); 130 } 131 132 133 bool LookupIterator::HasAccess() const { 134 DCHECK_EQ(ACCESS_CHECK, state_); 135 return isolate_->MayAccess(handle(isolate_->context()), 136 GetHolder<JSObject>()); 137 } 138 139 140 void LookupIterator::ReloadPropertyInformation() { 141 state_ = BEFORE_PROPERTY; 142 interceptor_state_ = InterceptorState::kUninitialized; 143 state_ = LookupInHolder(*holder_map_, *holder_); 144 DCHECK(IsFound() || holder_map_->is_dictionary_map()); 145 } 146 147 148 void LookupIterator::ReloadHolderMap() { 149 DCHECK_EQ(DATA, state_); 150 DCHECK(IsElement()); 151 DCHECK(JSObject::cast(*holder_)->HasFixedTypedArrayElements()); 152 if (*holder_map_ != holder_->map()) { 153 holder_map_ = handle(holder_->map(), isolate_); 154 } 155 } 156 157 158 void LookupIterator::PrepareForDataProperty(Handle<Object> value) { 159 DCHECK(state_ == DATA || state_ == ACCESSOR); 160 DCHECK(HolderIsReceiverOrHiddenPrototype()); 161 162 Handle<JSObject> holder = GetHolder<JSObject>(); 163 164 if (IsElement()) { 165 ElementsKind kind = holder_map_->elements_kind(); 166 ElementsKind to = value->OptimalElementsKind(); 167 if (IsHoleyElementsKind(kind)) to = GetHoleyElementsKind(to); 168 to = GetMoreGeneralElementsKind(kind, to); 169 JSObject::TransitionElementsKind(holder, to); 170 holder_map_ = handle(holder->map(), isolate_); 171 172 // Copy the backing store if it is copy-on-write. 173 if (IsFastSmiOrObjectElementsKind(to)) { 174 JSObject::EnsureWritableFastElements(holder); 175 } 176 177 } else { 178 if (holder_map_->is_dictionary_map()) return; 179 holder_map_ = 180 Map::PrepareForDataProperty(holder_map_, descriptor_number(), value); 181 } 182 183 JSObject::MigrateToMap(holder, holder_map_); 184 ReloadPropertyInformation(); 185 } 186 187 188 void LookupIterator::ReconfigureDataProperty(Handle<Object> value, 189 PropertyAttributes attributes) { 190 DCHECK(state_ == DATA || state_ == ACCESSOR); 191 DCHECK(HolderIsReceiverOrHiddenPrototype()); 192 Handle<JSObject> holder = GetHolder<JSObject>(); 193 if (IsElement()) { 194 DCHECK(!holder->HasFixedTypedArrayElements()); 195 DCHECK(attributes != NONE || !holder->HasFastElements()); 196 Handle<FixedArrayBase> elements(holder->elements()); 197 holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value, 198 attributes); 199 } else if (holder_map_->is_dictionary_map()) { 200 PropertyDetails details(attributes, v8::internal::DATA, 0, 201 PropertyCellType::kMutable); 202 JSObject::SetNormalizedProperty(holder, name(), value, details); 203 } else { 204 holder_map_ = Map::ReconfigureExistingProperty( 205 holder_map_, descriptor_number(), i::kData, attributes); 206 holder_map_ = 207 Map::PrepareForDataProperty(holder_map_, descriptor_number(), value); 208 JSObject::MigrateToMap(holder, holder_map_); 209 } 210 211 ReloadPropertyInformation(); 212 WriteDataValue(value); 213 214 #if VERIFY_HEAP 215 if (FLAG_verify_heap) { 216 holder->JSObjectVerify(); 217 } 218 #endif 219 } 220 221 222 void LookupIterator::PrepareTransitionToDataProperty( 223 Handle<Object> value, PropertyAttributes attributes, 224 Object::StoreFromKeyed store_mode) { 225 if (state_ == TRANSITION) return; 226 DCHECK(state_ != LookupIterator::ACCESSOR || 227 (GetAccessors()->IsAccessorInfo() && 228 AccessorInfo::cast(*GetAccessors())->is_special_data_property())); 229 DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_); 230 DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype()); 231 // Can only be called when the receiver is a JSObject. JSProxy has to be 232 // handled via a trap. Adding properties to primitive values is not 233 // observable. 234 Handle<JSObject> receiver = GetStoreTarget(); 235 236 if (!isolate()->IsInternallyUsedPropertyName(name()) && 237 !receiver->map()->is_extensible()) { 238 return; 239 } 240 241 auto transition = Map::TransitionToDataProperty( 242 handle(receiver->map(), isolate_), name_, value, attributes, store_mode); 243 state_ = TRANSITION; 244 transition_ = transition; 245 246 if (receiver->IsJSGlobalObject()) { 247 // Install a property cell. 248 InternalizeName(); 249 auto cell = JSGlobalObject::EnsurePropertyCell( 250 Handle<JSGlobalObject>::cast(receiver), name()); 251 DCHECK(cell->value()->IsTheHole()); 252 transition_ = cell; 253 } else if (!transition->is_dictionary_map()) { 254 property_details_ = transition->GetLastDescriptorDetails(); 255 has_property_ = true; 256 } 257 } 258 259 260 void LookupIterator::ApplyTransitionToDataProperty() { 261 DCHECK_EQ(TRANSITION, state_); 262 263 Handle<JSObject> receiver = GetStoreTarget(); 264 if (receiver->IsJSGlobalObject()) return; 265 holder_ = receiver; 266 holder_map_ = transition_map(); 267 JSObject::MigrateToMap(receiver, holder_map_); 268 ReloadPropertyInformation(); 269 } 270 271 272 void LookupIterator::Delete() { 273 Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_); 274 if (IsElement()) { 275 Handle<JSObject> object = Handle<JSObject>::cast(holder); 276 ElementsAccessor* accessor = object->GetElementsAccessor(); 277 accessor->Delete(object, number_); 278 } else { 279 PropertyNormalizationMode mode = holder->map()->is_prototype_map() 280 ? KEEP_INOBJECT_PROPERTIES 281 : CLEAR_INOBJECT_PROPERTIES; 282 283 if (holder->HasFastProperties()) { 284 JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0, 285 "DeletingProperty"); 286 holder_map_ = handle(holder->map(), isolate_); 287 ReloadPropertyInformation(); 288 } 289 // TODO(verwaest): Get rid of the name_ argument. 290 JSReceiver::DeleteNormalizedProperty(holder, name_, number_); 291 if (holder->IsJSObject()) { 292 JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder)); 293 } 294 } 295 } 296 297 298 void LookupIterator::TransitionToAccessorProperty( 299 AccessorComponent component, Handle<Object> accessor, 300 PropertyAttributes attributes) { 301 DCHECK(!accessor->IsNull()); 302 // Can only be called when the receiver is a JSObject. JSProxy has to be 303 // handled via a trap. Adding properties to primitive values is not 304 // observable. 305 Handle<JSObject> receiver = GetStoreTarget(); 306 307 if (!IsElement() && !receiver->map()->is_dictionary_map()) { 308 holder_ = receiver; 309 holder_map_ = Map::TransitionToAccessorProperty( 310 handle(receiver->map(), isolate_), name_, component, accessor, 311 attributes); 312 JSObject::MigrateToMap(receiver, holder_map_); 313 314 ReloadPropertyInformation(); 315 316 if (!holder_map_->is_dictionary_map()) return; 317 } 318 319 Handle<AccessorPair> pair; 320 if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) { 321 pair = Handle<AccessorPair>::cast(GetAccessors()); 322 // If the component and attributes are identical, nothing has to be done. 323 if (pair->get(component) == *accessor) { 324 if (property_details().attributes() == attributes) return; 325 } else { 326 pair = AccessorPair::Copy(pair); 327 pair->set(component, *accessor); 328 } 329 } else { 330 pair = factory()->NewAccessorPair(); 331 pair->set(component, *accessor); 332 } 333 334 TransitionToAccessorPair(pair, attributes); 335 336 #if VERIFY_HEAP 337 if (FLAG_verify_heap) { 338 receiver->JSObjectVerify(); 339 } 340 #endif 341 } 342 343 344 void LookupIterator::TransitionToAccessorPair(Handle<Object> pair, 345 PropertyAttributes attributes) { 346 Handle<JSObject> receiver = GetStoreTarget(); 347 holder_ = receiver; 348 349 PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0, 350 PropertyCellType::kMutable); 351 352 if (IsElement()) { 353 // TODO(verwaest): Move code into the element accessor. 354 Handle<SeededNumberDictionary> dictionary = 355 JSObject::NormalizeElements(receiver); 356 357 // We unconditionally pass used_as_prototype=false here because the call 358 // to RequireSlowElements takes care of the required IC clearing and 359 // we don't want to walk the heap twice. 360 dictionary = 361 SeededNumberDictionary::Set(dictionary, index_, pair, details, false); 362 receiver->RequireSlowElements(*dictionary); 363 364 if (receiver->HasSlowArgumentsElements()) { 365 FixedArray* parameter_map = FixedArray::cast(receiver->elements()); 366 uint32_t length = parameter_map->length() - 2; 367 if (number_ < length) { 368 parameter_map->set(number_ + 2, heap()->the_hole_value()); 369 } 370 FixedArray::cast(receiver->elements())->set(1, *dictionary); 371 } else { 372 receiver->set_elements(*dictionary); 373 } 374 } else { 375 PropertyNormalizationMode mode = receiver->map()->is_prototype_map() 376 ? KEEP_INOBJECT_PROPERTIES 377 : CLEAR_INOBJECT_PROPERTIES; 378 // Normalize object to make this operation simple. 379 JSObject::NormalizeProperties(receiver, mode, 0, 380 "TransitionToAccessorPair"); 381 382 JSObject::SetNormalizedProperty(receiver, name_, pair, details); 383 JSObject::ReoptimizeIfPrototype(receiver); 384 } 385 386 holder_map_ = handle(receiver->map(), isolate_); 387 ReloadPropertyInformation(); 388 } 389 390 391 bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const { 392 DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY); 393 return InternalHolderIsReceiverOrHiddenPrototype(); 394 } 395 396 bool LookupIterator::InternalHolderIsReceiverOrHiddenPrototype() const { 397 // Optimization that only works if configuration_ is not mutable. 398 if (!check_prototype_chain()) return true; 399 DisallowHeapAllocation no_gc; 400 if (!receiver_->IsJSReceiver()) return false; 401 Object* current = *receiver_; 402 JSReceiver* holder = *holder_; 403 // JSProxy do not occur as hidden prototypes. 404 if (current->IsJSProxy()) { 405 return JSReceiver::cast(current) == holder; 406 } 407 PrototypeIterator iter(isolate(), current, 408 PrototypeIterator::START_AT_RECEIVER); 409 do { 410 if (iter.GetCurrent<JSReceiver>() == holder) return true; 411 DCHECK(!current->IsJSProxy()); 412 iter.Advance(); 413 } while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN)); 414 return false; 415 } 416 417 418 Handle<Object> LookupIterator::FetchValue() const { 419 Object* result = NULL; 420 if (IsElement()) { 421 Handle<JSObject> holder = GetHolder<JSObject>(); 422 // TODO(verwaest): Optimize. 423 if (holder->IsStringObjectWithCharacterAt(index_)) { 424 Handle<JSValue> js_value = Handle<JSValue>::cast(holder); 425 Handle<String> string(String::cast(js_value->value())); 426 return factory()->LookupSingleCharacterStringFromCode( 427 String::Flatten(string)->Get(index_)); 428 } 429 430 ElementsAccessor* accessor = holder->GetElementsAccessor(); 431 return accessor->Get(handle(holder->elements()), number_); 432 } else if (holder_map_->IsJSGlobalObjectMap()) { 433 Handle<JSObject> holder = GetHolder<JSObject>(); 434 result = holder->global_dictionary()->ValueAt(number_); 435 DCHECK(result->IsPropertyCell()); 436 result = PropertyCell::cast(result)->value(); 437 } else if (holder_map_->is_dictionary_map()) { 438 result = holder_->property_dictionary()->ValueAt(number_); 439 } else if (property_details_.type() == v8::internal::DATA) { 440 Handle<JSObject> holder = GetHolder<JSObject>(); 441 FieldIndex field_index = FieldIndex::ForDescriptor(*holder_map_, number_); 442 return JSObject::FastPropertyAt(holder, property_details_.representation(), 443 field_index); 444 } else { 445 result = holder_map_->instance_descriptors()->GetValue(number_); 446 } 447 return handle(result, isolate_); 448 } 449 450 451 int LookupIterator::GetAccessorIndex() const { 452 DCHECK(has_property_); 453 DCHECK(!holder_map_->is_dictionary_map()); 454 DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type()); 455 return descriptor_number(); 456 } 457 458 459 int LookupIterator::GetConstantIndex() const { 460 DCHECK(has_property_); 461 DCHECK(!holder_map_->is_dictionary_map()); 462 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type()); 463 DCHECK(!IsElement()); 464 return descriptor_number(); 465 } 466 467 468 FieldIndex LookupIterator::GetFieldIndex() const { 469 DCHECK(has_property_); 470 DCHECK(!holder_map_->is_dictionary_map()); 471 DCHECK_EQ(v8::internal::DATA, property_details_.type()); 472 DCHECK(!IsElement()); 473 int index = 474 holder_map_->instance_descriptors()->GetFieldIndex(descriptor_number()); 475 bool is_double = representation().IsDouble(); 476 return FieldIndex::ForPropertyIndex(*holder_map_, index, is_double); 477 } 478 479 480 Handle<HeapType> LookupIterator::GetFieldType() const { 481 DCHECK(has_property_); 482 DCHECK(!holder_map_->is_dictionary_map()); 483 DCHECK_EQ(v8::internal::DATA, property_details_.type()); 484 return handle( 485 holder_map_->instance_descriptors()->GetFieldType(descriptor_number()), 486 isolate_); 487 } 488 489 490 Handle<PropertyCell> LookupIterator::GetPropertyCell() const { 491 DCHECK(!IsElement()); 492 Handle<JSObject> holder = GetHolder<JSObject>(); 493 Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(holder); 494 Object* value = global->global_dictionary()->ValueAt(dictionary_entry()); 495 DCHECK(value->IsPropertyCell()); 496 return handle(PropertyCell::cast(value)); 497 } 498 499 500 Handle<Object> LookupIterator::GetAccessors() const { 501 DCHECK_EQ(ACCESSOR, state_); 502 return FetchValue(); 503 } 504 505 506 Handle<Object> LookupIterator::GetDataValue() const { 507 DCHECK_EQ(DATA, state_); 508 Handle<Object> value = FetchValue(); 509 return value; 510 } 511 512 513 void LookupIterator::WriteDataValue(Handle<Object> value) { 514 DCHECK_EQ(DATA, state_); 515 Handle<JSReceiver> holder = GetHolder<JSReceiver>(); 516 if (IsElement()) { 517 Handle<JSObject> object = Handle<JSObject>::cast(holder); 518 ElementsAccessor* accessor = object->GetElementsAccessor(); 519 accessor->Set(object->elements(), number_, *value); 520 } else if (holder->IsJSGlobalObject()) { 521 Handle<GlobalDictionary> property_dictionary = 522 handle(JSObject::cast(*holder)->global_dictionary()); 523 PropertyCell::UpdateCell(property_dictionary, dictionary_entry(), value, 524 property_details_); 525 } else if (holder_map_->is_dictionary_map()) { 526 NameDictionary* property_dictionary = holder->property_dictionary(); 527 property_dictionary->ValueAtPut(dictionary_entry(), *value); 528 } else if (property_details_.type() == v8::internal::DATA) { 529 JSObject::cast(*holder)->WriteToField(descriptor_number(), *value); 530 } else { 531 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type()); 532 } 533 } 534 535 536 bool LookupIterator::IsIntegerIndexedExotic(JSReceiver* holder) { 537 DCHECK(exotic_index_state_ != ExoticIndexState::kNotExotic); 538 if (exotic_index_state_ == ExoticIndexState::kExotic) return true; 539 if (!InternalHolderIsReceiverOrHiddenPrototype()) { 540 exotic_index_state_ = ExoticIndexState::kNotExotic; 541 return false; 542 } 543 DCHECK(exotic_index_state_ == ExoticIndexState::kUninitialized); 544 bool result = false; 545 // Compute and cache result. 546 if (IsElement()) { 547 result = index_ >= JSTypedArray::cast(holder)->length_value(); 548 } else if (name()->IsString()) { 549 Handle<String> name_string = Handle<String>::cast(name()); 550 if (name_string->length() != 0) { 551 result = IsSpecialIndex(isolate_->unicode_cache(), *name_string); 552 } 553 } 554 exotic_index_state_ = 555 result ? ExoticIndexState::kExotic : ExoticIndexState::kNotExotic; 556 return result; 557 } 558 559 560 void LookupIterator::InternalizeName() { 561 if (name_->IsUniqueName()) return; 562 name_ = factory()->InternalizeString(Handle<String>::cast(name_)); 563 } 564 565 566 bool LookupIterator::HasInterceptor(Map* map) const { 567 if (IsElement()) return map->has_indexed_interceptor(); 568 return map->has_named_interceptor(); 569 } 570 571 572 bool LookupIterator::SkipInterceptor(JSObject* holder) { 573 auto info = GetInterceptor(holder); 574 // TODO(dcarney): check for symbol/can_intercept_symbols here as well. 575 if (info->non_masking()) { 576 switch (interceptor_state_) { 577 case InterceptorState::kUninitialized: 578 interceptor_state_ = InterceptorState::kSkipNonMasking; 579 // Fall through. 580 case InterceptorState::kSkipNonMasking: 581 return true; 582 case InterceptorState::kProcessNonMasking: 583 return false; 584 } 585 } 586 return interceptor_state_ == InterceptorState::kProcessNonMasking; 587 } 588 589 590 JSReceiver* LookupIterator::NextHolder(Map* map) { 591 DisallowHeapAllocation no_gc; 592 if (!map->prototype()->IsJSReceiver()) return NULL; 593 594 JSReceiver* next = JSReceiver::cast(map->prototype()); 595 DCHECK(!next->map()->IsJSGlobalObjectMap() || 596 next->map()->is_hidden_prototype()); 597 598 if (!check_prototype_chain() && 599 !(check_hidden() && next->map()->is_hidden_prototype()) && 600 // Always lookup behind the JSGlobalProxy into the JSGlobalObject, even 601 // when not checking other hidden prototypes. 602 !map->IsJSGlobalProxyMap()) { 603 return NULL; 604 } 605 606 return next; 607 } 608 609 610 LookupIterator::State LookupIterator::LookupInHolder(Map* const map, 611 JSReceiver* const holder) { 612 STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY); 613 DisallowHeapAllocation no_gc; 614 if (interceptor_state_ == InterceptorState::kProcessNonMasking) { 615 return LookupNonMaskingInterceptorInHolder(map, holder); 616 } 617 switch (state_) { 618 case NOT_FOUND: 619 if (map->IsJSProxyMap()) { 620 // Do not leak private property names. 621 if (IsElement() || !name_->IsPrivate()) return JSPROXY; 622 } 623 if (map->is_access_check_needed() && 624 (IsElement() || !isolate_->IsInternallyUsedPropertyName(name_))) { 625 return ACCESS_CHECK; 626 } 627 // Fall through. 628 case ACCESS_CHECK: 629 if (exotic_index_state_ != ExoticIndexState::kNotExotic && 630 holder->IsJSTypedArray() && IsIntegerIndexedExotic(holder)) { 631 return INTEGER_INDEXED_EXOTIC; 632 } 633 if (check_interceptor() && HasInterceptor(map) && 634 !SkipInterceptor(JSObject::cast(holder))) { 635 // Do not leak private property names. 636 if (!name_.is_null() && name_->IsPrivate()) return NOT_FOUND; 637 return INTERCEPTOR; 638 } 639 // Fall through. 640 case INTERCEPTOR: 641 if (IsElement()) { 642 // TODO(verwaest): Optimize. 643 if (holder->IsStringObjectWithCharacterAt(index_)) { 644 PropertyAttributes attributes = 645 static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE); 646 property_details_ = PropertyDetails(attributes, v8::internal::DATA, 0, 647 PropertyCellType::kNoCell); 648 } else { 649 JSObject* js_object = JSObject::cast(holder); 650 if (js_object->elements() == isolate()->heap()->empty_fixed_array()) { 651 return NOT_FOUND; 652 } 653 654 ElementsAccessor* accessor = js_object->GetElementsAccessor(); 655 FixedArrayBase* backing_store = js_object->elements(); 656 number_ = 657 accessor->GetEntryForIndex(js_object, backing_store, index_); 658 if (number_ == kMaxUInt32) return NOT_FOUND; 659 property_details_ = accessor->GetDetails(backing_store, number_); 660 } 661 } else if (!map->is_dictionary_map()) { 662 DescriptorArray* descriptors = map->instance_descriptors(); 663 int number = descriptors->SearchWithCache(*name_, map); 664 if (number == DescriptorArray::kNotFound) return NOT_FOUND; 665 number_ = static_cast<uint32_t>(number); 666 property_details_ = descriptors->GetDetails(number_); 667 } else if (map->IsJSGlobalObjectMap()) { 668 GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary(); 669 int number = dict->FindEntry(name_); 670 if (number == GlobalDictionary::kNotFound) return NOT_FOUND; 671 number_ = static_cast<uint32_t>(number); 672 DCHECK(dict->ValueAt(number_)->IsPropertyCell()); 673 PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_)); 674 if (cell->value()->IsTheHole()) return NOT_FOUND; 675 property_details_ = cell->property_details(); 676 } else { 677 NameDictionary* dict = holder->property_dictionary(); 678 int number = dict->FindEntry(name_); 679 if (number == NameDictionary::kNotFound) return NOT_FOUND; 680 number_ = static_cast<uint32_t>(number); 681 property_details_ = dict->DetailsAt(number_); 682 } 683 has_property_ = true; 684 switch (property_details_.kind()) { 685 case v8::internal::kData: 686 return DATA; 687 case v8::internal::kAccessor: 688 return ACCESSOR; 689 } 690 case ACCESSOR: 691 case DATA: 692 return NOT_FOUND; 693 case INTEGER_INDEXED_EXOTIC: 694 case JSPROXY: 695 case TRANSITION: 696 UNREACHABLE(); 697 } 698 UNREACHABLE(); 699 return state_; 700 } 701 702 703 LookupIterator::State LookupIterator::LookupNonMaskingInterceptorInHolder( 704 Map* const map, JSReceiver* const holder) { 705 switch (state_) { 706 case NOT_FOUND: 707 if (check_interceptor() && HasInterceptor(map) && 708 !SkipInterceptor(JSObject::cast(holder))) { 709 return INTERCEPTOR; 710 } 711 // Fall through. 712 default: 713 return NOT_FOUND; 714 } 715 UNREACHABLE(); 716 return state_; 717 } 718 719 } // namespace internal 720 } // namespace v8 721