lookup.cc revision 3b9bc31999c9787eb726ecdbfd5796bfdec32a18
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 13namespace v8 { 14namespace internal { 15 16 17// static 18LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate, 19 Handle<Object> receiver, 20 Handle<Object> key, 21 bool* success, 22 Configuration configuration) { 23 uint32_t index = 0; 24 if (key->ToArrayIndex(&index)) { 25 *success = true; 26 return LookupIterator(isolate, receiver, index, configuration); 27 } 28 29 Handle<Name> name; 30 *success = Object::ToName(isolate, key).ToHandle(&name); 31 if (!*success) { 32 DCHECK(isolate->has_pending_exception()); 33 // Return an unusable dummy. 34 return LookupIterator(receiver, isolate->factory()->empty_string()); 35 } 36 37 if (name->AsArrayIndex(&index)) { 38 LookupIterator it(isolate, receiver, index, configuration); 39 // Here we try to avoid having to rebuild the string later 40 // by storing it on the indexed LookupIterator. 41 it.name_ = name; 42 return it; 43 } 44 45 return LookupIterator(receiver, name, configuration); 46} 47 48template <bool is_element> 49void LookupIterator::Start() { 50 DisallowHeapAllocation no_gc; 51 52 has_property_ = false; 53 state_ = NOT_FOUND; 54 holder_ = initial_holder_; 55 56 JSReceiver* holder = *holder_; 57 Map* map = holder->map(); 58 59 state_ = LookupInHolder<is_element>(map, holder); 60 if (IsFound()) return; 61 62 NextInternal<is_element>(map, holder); 63} 64 65template void LookupIterator::Start<true>(); 66template void LookupIterator::Start<false>(); 67 68void LookupIterator::Next() { 69 DCHECK_NE(JSPROXY, state_); 70 DCHECK_NE(TRANSITION, state_); 71 DisallowHeapAllocation no_gc; 72 has_property_ = false; 73 74 JSReceiver* holder = *holder_; 75 Map* map = holder->map(); 76 77 if (map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) { 78 state_ = IsElement() ? LookupInSpecialHolder<true>(map, holder) 79 : LookupInSpecialHolder<false>(map, holder); 80 if (IsFound()) return; 81 } 82 83 IsElement() ? NextInternal<true>(map, holder) 84 : NextInternal<false>(map, holder); 85} 86 87template <bool is_element> 88void LookupIterator::NextInternal(Map* map, JSReceiver* holder) { 89 do { 90 JSReceiver* maybe_holder = NextHolder(map); 91 if (maybe_holder == nullptr) { 92 if (interceptor_state_ == InterceptorState::kSkipNonMasking) { 93 RestartLookupForNonMaskingInterceptors<is_element>(); 94 return; 95 } 96 state_ = NOT_FOUND; 97 if (holder != *holder_) holder_ = handle(holder, isolate_); 98 return; 99 } 100 holder = maybe_holder; 101 map = holder->map(); 102 state_ = LookupInHolder<is_element>(map, holder); 103 } while (!IsFound()); 104 105 holder_ = handle(holder, isolate_); 106} 107 108template <bool is_element> 109void LookupIterator::RestartInternal(InterceptorState interceptor_state) { 110 interceptor_state_ = interceptor_state; 111 property_details_ = PropertyDetails::Empty(); 112 number_ = DescriptorArray::kNotFound; 113 Start<is_element>(); 114} 115 116template void LookupIterator::RestartInternal<true>(InterceptorState); 117template void LookupIterator::RestartInternal<false>(InterceptorState); 118 119// static 120Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver( 121 Isolate* isolate, Handle<Object> receiver, uint32_t index) { 122 // Strings are the only objects with properties (only elements) directly on 123 // the wrapper. Hence we can skip generating the wrapper for all other cases. 124 if (index != kMaxUInt32 && receiver->IsString() && 125 index < static_cast<uint32_t>(String::cast(*receiver)->length())) { 126 // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native 127 // context, ensuring that we don't leak it into JS? 128 Handle<JSFunction> constructor = isolate->string_function(); 129 Handle<JSObject> result = isolate->factory()->NewJSObject(constructor); 130 Handle<JSValue>::cast(result)->set_value(*receiver); 131 return result; 132 } 133 auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate); 134 if (root->IsNull()) { 135 unsigned int magic = 0xbbbbbbbb; 136 isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic); 137 } 138 return Handle<JSReceiver>::cast(root); 139} 140 141 142Handle<Map> LookupIterator::GetReceiverMap() const { 143 if (receiver_->IsNumber()) return factory()->heap_number_map(); 144 return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_); 145} 146 147bool LookupIterator::HasAccess() const { 148 DCHECK_EQ(ACCESS_CHECK, state_); 149 return isolate_->MayAccess(handle(isolate_->context()), 150 GetHolder<JSObject>()); 151} 152 153template <bool is_element> 154void 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 161bool LookupIterator::HolderIsInContextIndex(uint32_t index) const { 162 DisallowHeapAllocation no_gc; 163 164 Object* context = heap()->native_contexts_list(); 165 while (!context->IsUndefined()) { 166 Context* current_context = Context::cast(context); 167 if (current_context->get(index) == *holder_) { 168 return true; 169 } 170 context = current_context->get(Context::NEXT_CONTEXT_LINK); 171 } 172 return false; 173} 174 175void LookupIterator::InternalUpdateProtector() { 176 if (isolate_->bootstrapper()->IsActive()) return; 177 if (!isolate_->IsArraySpeciesLookupChainIntact()) return; 178 179 if (*name_ == heap()->constructor_string()) { 180 // Setting the constructor property could change an instance's @@species 181 if (holder_->IsJSArray()) { 182 isolate_->CountUsage( 183 v8::Isolate::UseCounterFeature::kArrayInstanceConstructorModified); 184 isolate_->InvalidateArraySpeciesProtector(); 185 } else if (holder_->map()->is_prototype_map()) { 186 // Setting the constructor of Array.prototype of any realm also needs 187 // to invalidate the species protector 188 if (HolderIsInContextIndex(Context::INITIAL_ARRAY_PROTOTYPE_INDEX)) { 189 isolate_->CountUsage(v8::Isolate::UseCounterFeature:: 190 kArrayPrototypeConstructorModified); 191 isolate_->InvalidateArraySpeciesProtector(); 192 } 193 } 194 } else if (*name_ == heap()->species_symbol()) { 195 // Setting the Symbol.species property of any Array constructor invalidates 196 // the species protector 197 if (HolderIsInContextIndex(Context::ARRAY_FUNCTION_INDEX)) { 198 isolate_->CountUsage( 199 v8::Isolate::UseCounterFeature::kArraySpeciesModified); 200 isolate_->InvalidateArraySpeciesProtector(); 201 } 202 } 203} 204 205void 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->HasFastProperties()) return; 229 230 Handle<Map> old_map(holder->map(), isolate_); 231 Handle<Map> new_map = 232 Map::PrepareForDataProperty(old_map, descriptor_number(), value); 233 234 if (old_map.is_identical_to(new_map)) { 235 // Update the property details if the representation was None. 236 if (representation().IsNone()) { 237 property_details_ = 238 new_map->instance_descriptors()->GetDetails(descriptor_number()); 239 } 240 return; 241 } 242 243 JSObject::MigrateToMap(holder, new_map); 244 ReloadPropertyInformation<false>(); 245} 246 247 248void LookupIterator::ReconfigureDataProperty(Handle<Object> value, 249 PropertyAttributes attributes) { 250 DCHECK(state_ == DATA || state_ == ACCESSOR); 251 DCHECK(HolderIsReceiverOrHiddenPrototype()); 252 Handle<JSObject> holder = GetHolder<JSObject>(); 253 if (IsElement()) { 254 DCHECK(!holder->HasFixedTypedArrayElements()); 255 DCHECK(attributes != NONE || !holder->HasFastElements()); 256 Handle<FixedArrayBase> elements(holder->elements()); 257 holder->GetElementsAccessor()->Reconfigure(holder, elements, number_, value, 258 attributes); 259 ReloadPropertyInformation<true>(); 260 } else { 261 if (!holder->HasFastProperties()) { 262 PropertyDetails details(attributes, v8::internal::DATA, 0, 263 PropertyCellType::kMutable); 264 JSObject::SetNormalizedProperty(holder, name(), value, details); 265 } else { 266 Handle<Map> old_map(holder->map(), isolate_); 267 Handle<Map> new_map = Map::ReconfigureExistingProperty( 268 old_map, descriptor_number(), i::kData, attributes); 269 new_map = 270 Map::PrepareForDataProperty(new_map, descriptor_number(), value); 271 JSObject::MigrateToMap(holder, new_map); 272 } 273 ReloadPropertyInformation<false>(); 274 } 275 276 WriteDataValue(value); 277 278#if VERIFY_HEAP 279 if (FLAG_verify_heap) { 280 holder->JSObjectVerify(); 281 } 282#endif 283} 284 285// Can only be called when the receiver is a JSObject. JSProxy has to be handled 286// via a trap. Adding properties to primitive values is not observable. 287void LookupIterator::PrepareTransitionToDataProperty( 288 Handle<JSObject> receiver, Handle<Object> value, 289 PropertyAttributes attributes, Object::StoreFromKeyed store_mode) { 290 DCHECK(receiver.is_identical_to(GetStoreTarget())); 291 if (state_ == TRANSITION) return; 292 DCHECK(state_ != LookupIterator::ACCESSOR || 293 (GetAccessors()->IsAccessorInfo() && 294 AccessorInfo::cast(*GetAccessors())->is_special_data_property())); 295 DCHECK_NE(INTEGER_INDEXED_EXOTIC, state_); 296 DCHECK(state_ == NOT_FOUND || !HolderIsReceiverOrHiddenPrototype()); 297 298 Handle<Map> map(receiver->map(), isolate_); 299 300 // Dictionary maps can always have additional data properties. 301 if (map->is_dictionary_map()) { 302 state_ = TRANSITION; 303 if (map->IsJSGlobalObjectMap()) { 304 // Install a property cell. 305 auto cell = JSGlobalObject::EnsurePropertyCell( 306 Handle<JSGlobalObject>::cast(receiver), name()); 307 DCHECK(cell->value()->IsTheHole()); 308 transition_ = cell; 309 } else { 310 transition_ = map; 311 } 312 return; 313 } 314 315 Handle<Map> transition = 316 Map::TransitionToDataProperty(map, name_, value, attributes, store_mode); 317 state_ = TRANSITION; 318 transition_ = transition; 319 320 if (!transition->is_dictionary_map()) { 321 property_details_ = transition->GetLastDescriptorDetails(); 322 has_property_ = true; 323 } 324} 325 326void LookupIterator::ApplyTransitionToDataProperty(Handle<JSObject> receiver) { 327 DCHECK_EQ(TRANSITION, state_); 328 329 DCHECK(receiver.is_identical_to(GetStoreTarget())); 330 331 if (receiver->IsJSGlobalObject()) return; 332 holder_ = receiver; 333 Handle<Map> transition = transition_map(); 334 bool simple_transition = transition->GetBackPointer() == receiver->map(); 335 JSObject::MigrateToMap(receiver, transition); 336 337 if (simple_transition) { 338 int number = transition->LastAdded(); 339 number_ = static_cast<uint32_t>(number); 340 property_details_ = transition->GetLastDescriptorDetails(); 341 state_ = DATA; 342 } else { 343 ReloadPropertyInformation<false>(); 344 } 345} 346 347 348void LookupIterator::Delete() { 349 Handle<JSReceiver> holder = Handle<JSReceiver>::cast(holder_); 350 if (IsElement()) { 351 Handle<JSObject> object = Handle<JSObject>::cast(holder); 352 ElementsAccessor* accessor = object->GetElementsAccessor(); 353 accessor->Delete(object, number_); 354 } else { 355 PropertyNormalizationMode mode = holder->map()->is_prototype_map() 356 ? KEEP_INOBJECT_PROPERTIES 357 : CLEAR_INOBJECT_PROPERTIES; 358 359 if (holder->HasFastProperties()) { 360 JSObject::NormalizeProperties(Handle<JSObject>::cast(holder), mode, 0, 361 "DeletingProperty"); 362 ReloadPropertyInformation<false>(); 363 } 364 // TODO(verwaest): Get rid of the name_ argument. 365 JSReceiver::DeleteNormalizedProperty(holder, name_, number_); 366 if (holder->IsJSObject()) { 367 JSObject::ReoptimizeIfPrototype(Handle<JSObject>::cast(holder)); 368 } 369 } 370 state_ = NOT_FOUND; 371} 372 373 374void LookupIterator::TransitionToAccessorProperty( 375 AccessorComponent component, Handle<Object> accessor, 376 PropertyAttributes attributes) { 377 DCHECK(!accessor->IsNull()); 378 // Can only be called when the receiver is a JSObject. JSProxy has to be 379 // handled via a trap. Adding properties to primitive values is not 380 // observable. 381 Handle<JSObject> receiver = GetStoreTarget(); 382 383 if (!IsElement() && !receiver->map()->is_dictionary_map()) { 384 Handle<Map> old_map(receiver->map(), isolate_); 385 386 if (!holder_.is_identical_to(receiver)) { 387 holder_ = receiver; 388 state_ = NOT_FOUND; 389 } else if (state_ == INTERCEPTOR) { 390 LookupInRegularHolder<false>(*old_map, *holder_); 391 } 392 int descriptor = 393 IsFound() ? static_cast<int>(number_) : DescriptorArray::kNotFound; 394 395 Handle<Map> new_map = Map::TransitionToAccessorProperty( 396 old_map, name_, descriptor, component, accessor, attributes); 397 bool simple_transition = new_map->GetBackPointer() == receiver->map(); 398 JSObject::MigrateToMap(receiver, new_map); 399 400 if (simple_transition) { 401 int number = new_map->LastAdded(); 402 number_ = static_cast<uint32_t>(number); 403 property_details_ = new_map->GetLastDescriptorDetails(); 404 state_ = ACCESSOR; 405 return; 406 } 407 408 ReloadPropertyInformation<false>(); 409 if (!new_map->is_dictionary_map()) return; 410 } 411 412 Handle<AccessorPair> pair; 413 if (state() == ACCESSOR && GetAccessors()->IsAccessorPair()) { 414 pair = Handle<AccessorPair>::cast(GetAccessors()); 415 // If the component and attributes are identical, nothing has to be done. 416 if (pair->get(component) == *accessor) { 417 if (property_details().attributes() == attributes) return; 418 } else { 419 pair = AccessorPair::Copy(pair); 420 pair->set(component, *accessor); 421 } 422 } else { 423 pair = factory()->NewAccessorPair(); 424 pair->set(component, *accessor); 425 } 426 427 TransitionToAccessorPair(pair, attributes); 428 429#if VERIFY_HEAP 430 if (FLAG_verify_heap) { 431 receiver->JSObjectVerify(); 432 } 433#endif 434} 435 436 437void LookupIterator::TransitionToAccessorPair(Handle<Object> pair, 438 PropertyAttributes attributes) { 439 Handle<JSObject> receiver = GetStoreTarget(); 440 holder_ = receiver; 441 442 PropertyDetails details(attributes, ACCESSOR_CONSTANT, 0, 443 PropertyCellType::kMutable); 444 445 if (IsElement()) { 446 // TODO(verwaest): Move code into the element accessor. 447 Handle<SeededNumberDictionary> dictionary = 448 JSObject::NormalizeElements(receiver); 449 450 // We unconditionally pass used_as_prototype=false here because the call 451 // to RequireSlowElements takes care of the required IC clearing and 452 // we don't want to walk the heap twice. 453 dictionary = 454 SeededNumberDictionary::Set(dictionary, index_, pair, details, false); 455 receiver->RequireSlowElements(*dictionary); 456 457 if (receiver->HasSlowArgumentsElements()) { 458 FixedArray* parameter_map = FixedArray::cast(receiver->elements()); 459 uint32_t length = parameter_map->length() - 2; 460 if (number_ < length) { 461 parameter_map->set(number_ + 2, heap()->the_hole_value()); 462 } 463 FixedArray::cast(receiver->elements())->set(1, *dictionary); 464 } else { 465 receiver->set_elements(*dictionary); 466 } 467 468 ReloadPropertyInformation<true>(); 469 } else { 470 PropertyNormalizationMode mode = receiver->map()->is_prototype_map() 471 ? KEEP_INOBJECT_PROPERTIES 472 : CLEAR_INOBJECT_PROPERTIES; 473 // Normalize object to make this operation simple. 474 JSObject::NormalizeProperties(receiver, mode, 0, 475 "TransitionToAccessorPair"); 476 477 JSObject::SetNormalizedProperty(receiver, name_, pair, details); 478 JSObject::ReoptimizeIfPrototype(receiver); 479 480 ReloadPropertyInformation<false>(); 481 } 482} 483 484 485bool LookupIterator::HolderIsReceiverOrHiddenPrototype() const { 486 DCHECK(has_property_ || state_ == INTERCEPTOR || state_ == JSPROXY); 487 // Optimization that only works if configuration_ is not mutable. 488 if (!check_prototype_chain()) return true; 489 DisallowHeapAllocation no_gc; 490 if (*receiver_ == *holder_) return true; 491 if (!receiver_->IsJSReceiver()) return false; 492 JSReceiver* current = JSReceiver::cast(*receiver_); 493 JSReceiver* object = *holder_; 494 if (!current->map()->has_hidden_prototype()) return false; 495 // JSProxy do not occur as hidden prototypes. 496 if (object->IsJSProxy()) return false; 497 PrototypeIterator iter(isolate(), current, 498 PrototypeIterator::START_AT_PROTOTYPE, 499 PrototypeIterator::END_AT_NON_HIDDEN); 500 while (!iter.IsAtEnd()) { 501 if (iter.GetCurrent<JSReceiver>() == object) return true; 502 iter.Advance(); 503 } 504 return false; 505} 506 507 508Handle<Object> LookupIterator::FetchValue() const { 509 Object* result = NULL; 510 if (IsElement()) { 511 Handle<JSObject> holder = GetHolder<JSObject>(); 512 ElementsAccessor* accessor = holder->GetElementsAccessor(); 513 return accessor->Get(holder, number_); 514 } else if (holder_->IsJSGlobalObject()) { 515 Handle<JSObject> holder = GetHolder<JSObject>(); 516 result = holder->global_dictionary()->ValueAt(number_); 517 DCHECK(result->IsPropertyCell()); 518 result = PropertyCell::cast(result)->value(); 519 } else if (!holder_->HasFastProperties()) { 520 result = holder_->property_dictionary()->ValueAt(number_); 521 } else if (property_details_.type() == v8::internal::DATA) { 522 Handle<JSObject> holder = GetHolder<JSObject>(); 523 FieldIndex field_index = FieldIndex::ForDescriptor(holder->map(), number_); 524 return JSObject::FastPropertyAt(holder, property_details_.representation(), 525 field_index); 526 } else { 527 result = holder_->map()->instance_descriptors()->GetValue(number_); 528 } 529 return handle(result, isolate_); 530} 531 532 533int LookupIterator::GetAccessorIndex() const { 534 DCHECK(has_property_); 535 DCHECK(holder_->HasFastProperties()); 536 DCHECK_EQ(v8::internal::ACCESSOR_CONSTANT, property_details_.type()); 537 return descriptor_number(); 538} 539 540 541int LookupIterator::GetConstantIndex() const { 542 DCHECK(has_property_); 543 DCHECK(holder_->HasFastProperties()); 544 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type()); 545 DCHECK(!IsElement()); 546 return descriptor_number(); 547} 548 549 550FieldIndex LookupIterator::GetFieldIndex() const { 551 DCHECK(has_property_); 552 DCHECK(holder_->HasFastProperties()); 553 DCHECK_EQ(v8::internal::DATA, property_details_.type()); 554 DCHECK(!IsElement()); 555 Map* holder_map = holder_->map(); 556 int index = 557 holder_map->instance_descriptors()->GetFieldIndex(descriptor_number()); 558 bool is_double = representation().IsDouble(); 559 return FieldIndex::ForPropertyIndex(holder_map, index, is_double); 560} 561 562Handle<FieldType> LookupIterator::GetFieldType() const { 563 DCHECK(has_property_); 564 DCHECK(holder_->HasFastProperties()); 565 DCHECK_EQ(v8::internal::DATA, property_details_.type()); 566 return handle( 567 holder_->map()->instance_descriptors()->GetFieldType(descriptor_number()), 568 isolate_); 569} 570 571 572Handle<PropertyCell> LookupIterator::GetPropertyCell() const { 573 DCHECK(!IsElement()); 574 Handle<JSObject> holder = GetHolder<JSObject>(); 575 Handle<JSGlobalObject> global = Handle<JSGlobalObject>::cast(holder); 576 Object* value = global->global_dictionary()->ValueAt(dictionary_entry()); 577 DCHECK(value->IsPropertyCell()); 578 return handle(PropertyCell::cast(value)); 579} 580 581 582Handle<Object> LookupIterator::GetAccessors() const { 583 DCHECK_EQ(ACCESSOR, state_); 584 return FetchValue(); 585} 586 587 588Handle<Object> LookupIterator::GetDataValue() const { 589 DCHECK_EQ(DATA, state_); 590 Handle<Object> value = FetchValue(); 591 return value; 592} 593 594 595void LookupIterator::WriteDataValue(Handle<Object> value) { 596 DCHECK_EQ(DATA, state_); 597 Handle<JSReceiver> holder = GetHolder<JSReceiver>(); 598 if (IsElement()) { 599 Handle<JSObject> object = Handle<JSObject>::cast(holder); 600 ElementsAccessor* accessor = object->GetElementsAccessor(); 601 accessor->Set(object, number_, *value); 602 } else if (holder->HasFastProperties()) { 603 if (property_details_.type() == v8::internal::DATA) { 604 JSObject::cast(*holder)->WriteToField(descriptor_number(), 605 property_details_, *value); 606 } else { 607 DCHECK_EQ(v8::internal::DATA_CONSTANT, property_details_.type()); 608 } 609 } else if (holder->IsJSGlobalObject()) { 610 Handle<GlobalDictionary> property_dictionary = 611 handle(JSObject::cast(*holder)->global_dictionary()); 612 PropertyCell::UpdateCell(property_dictionary, dictionary_entry(), value, 613 property_details_); 614 } else { 615 NameDictionary* property_dictionary = holder->property_dictionary(); 616 property_dictionary->ValueAtPut(dictionary_entry(), *value); 617 } 618} 619 620template <bool is_element> 621bool LookupIterator::SkipInterceptor(JSObject* holder) { 622 auto info = GetInterceptor<is_element>(holder); 623 // TODO(dcarney): check for symbol/can_intercept_symbols here as well. 624 if (info->non_masking()) { 625 switch (interceptor_state_) { 626 case InterceptorState::kUninitialized: 627 interceptor_state_ = InterceptorState::kSkipNonMasking; 628 // Fall through. 629 case InterceptorState::kSkipNonMasking: 630 return true; 631 case InterceptorState::kProcessNonMasking: 632 return false; 633 } 634 } 635 return interceptor_state_ == InterceptorState::kProcessNonMasking; 636} 637 638JSReceiver* LookupIterator::NextHolder(Map* map) { 639 DisallowHeapAllocation no_gc; 640 if (map->prototype() == heap()->null_value()) return NULL; 641 642 DCHECK(!map->IsJSGlobalProxyMap() || map->has_hidden_prototype()); 643 644 if (!check_prototype_chain() && 645 !(check_hidden() && map->has_hidden_prototype()) && 646 // Always lookup behind the JSGlobalProxy into the JSGlobalObject, even 647 // when not checking other hidden prototypes. 648 !map->IsJSGlobalProxyMap()) { 649 return NULL; 650 } 651 652 return JSReceiver::cast(map->prototype()); 653} 654 655LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const { 656 DCHECK(!IsElement()); 657 if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND; 658 659 Handle<String> name_string = Handle<String>::cast(name_); 660 if (name_string->length() == 0) return NOT_FOUND; 661 662 return IsSpecialIndex(isolate_->unicode_cache(), *name_string) 663 ? INTEGER_INDEXED_EXOTIC 664 : NOT_FOUND; 665} 666 667namespace { 668 669template <bool is_element> 670bool HasInterceptor(Map* map) { 671 return is_element ? map->has_indexed_interceptor() 672 : map->has_named_interceptor(); 673} 674 675} // namespace 676 677template <bool is_element> 678LookupIterator::State LookupIterator::LookupInSpecialHolder( 679 Map* const map, JSReceiver* const holder) { 680 STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY); 681 switch (state_) { 682 case NOT_FOUND: 683 if (map->IsJSProxyMap()) { 684 if (is_element || !name_->IsPrivate()) return JSPROXY; 685 } 686 if (map->is_access_check_needed()) { 687 if (is_element || !name_->IsPrivate()) return ACCESS_CHECK; 688 } 689 // Fall through. 690 case ACCESS_CHECK: 691 if (check_interceptor() && HasInterceptor<is_element>(map) && 692 !SkipInterceptor<is_element>(JSObject::cast(holder))) { 693 if (is_element || !name_->IsPrivate()) return INTERCEPTOR; 694 } 695 // Fall through. 696 case INTERCEPTOR: 697 if (!is_element && map->IsJSGlobalObjectMap()) { 698 GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary(); 699 int number = dict->FindEntry(name_); 700 if (number == GlobalDictionary::kNotFound) return NOT_FOUND; 701 number_ = static_cast<uint32_t>(number); 702 DCHECK(dict->ValueAt(number_)->IsPropertyCell()); 703 PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_)); 704 if (cell->value()->IsTheHole()) return NOT_FOUND; 705 property_details_ = cell->property_details(); 706 has_property_ = true; 707 switch (property_details_.kind()) { 708 case v8::internal::kData: 709 return DATA; 710 case v8::internal::kAccessor: 711 return ACCESSOR; 712 } 713 } 714 return LookupInRegularHolder<is_element>(map, holder); 715 case ACCESSOR: 716 case DATA: 717 return NOT_FOUND; 718 case INTEGER_INDEXED_EXOTIC: 719 case JSPROXY: 720 case TRANSITION: 721 UNREACHABLE(); 722 } 723 UNREACHABLE(); 724 return NOT_FOUND; 725} 726 727template <bool is_element> 728LookupIterator::State LookupIterator::LookupInRegularHolder( 729 Map* const map, JSReceiver* const holder) { 730 DisallowHeapAllocation no_gc; 731 if (interceptor_state_ == InterceptorState::kProcessNonMasking) { 732 return NOT_FOUND; 733 } 734 735 if (is_element) { 736 JSObject* js_object = JSObject::cast(holder); 737 ElementsAccessor* accessor = js_object->GetElementsAccessor(); 738 FixedArrayBase* backing_store = js_object->elements(); 739 number_ = accessor->GetEntryForIndex(js_object, backing_store, index_); 740 if (number_ == kMaxUInt32) { 741 return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND; 742 } 743 property_details_ = accessor->GetDetails(js_object, number_); 744 } else if (!map->is_dictionary_map()) { 745 DescriptorArray* descriptors = map->instance_descriptors(); 746 int number = descriptors->SearchWithCache(isolate_, *name_, map); 747 if (number == DescriptorArray::kNotFound) return NotFound(holder); 748 number_ = static_cast<uint32_t>(number); 749 property_details_ = descriptors->GetDetails(number_); 750 } else { 751 NameDictionary* dict = holder->property_dictionary(); 752 int number = dict->FindEntry(name_); 753 if (number == NameDictionary::kNotFound) return NotFound(holder); 754 number_ = static_cast<uint32_t>(number); 755 property_details_ = dict->DetailsAt(number_); 756 } 757 has_property_ = true; 758 switch (property_details_.kind()) { 759 case v8::internal::kData: 760 return DATA; 761 case v8::internal::kAccessor: 762 return ACCESSOR; 763 } 764 765 UNREACHABLE(); 766 return state_; 767} 768 769} // namespace internal 770} // namespace v8 771