| OLD | NEW |
| 1 // Copyright 2014 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/lookup.h" | 5 #include "src/lookup.h" |
| 6 | 6 |
| 7 #include "src/bootstrapper.h" | 7 #include "src/bootstrapper.h" |
| 8 #include "src/deoptimizer.h" | 8 #include "src/deoptimizer.h" |
| 9 #include "src/elements.h" | 9 #include "src/elements.h" |
| 10 #include "src/field-type.h" | 10 #include "src/field-type.h" |
| (...skipping 27 matching lines...) Expand all Loading... |
| 38 LookupIterator it(isolate, receiver, index, configuration); | 38 LookupIterator it(isolate, receiver, index, configuration); |
| 39 // Here we try to avoid having to rebuild the string later | 39 // Here we try to avoid having to rebuild the string later |
| 40 // by storing it on the indexed LookupIterator. | 40 // by storing it on the indexed LookupIterator. |
| 41 it.name_ = name; | 41 it.name_ = name; |
| 42 return it; | 42 return it; |
| 43 } | 43 } |
| 44 | 44 |
| 45 return LookupIterator(receiver, name, configuration); | 45 return LookupIterator(receiver, name, configuration); |
| 46 } | 46 } |
| 47 | 47 |
| 48 void LookupIterator::Start() { |
| 49 DisallowHeapAllocation no_gc; |
| 50 |
| 51 has_property_ = false; |
| 52 state_ = NOT_FOUND; |
| 53 number_ = DescriptorArray::kNotFound; |
| 54 holder_ = initial_holder_; |
| 55 |
| 56 JSReceiver* holder = *holder_; |
| 57 Map* map = holder->map(); |
| 58 |
| 59 state_ = LookupInHolder(map, holder); |
| 60 if (IsFound()) return; |
| 61 |
| 62 NextInternal(map, holder); |
| 63 } |
| 48 | 64 |
| 49 void LookupIterator::Next() { | 65 void LookupIterator::Next() { |
| 50 DCHECK_NE(JSPROXY, state_); | 66 DCHECK_NE(JSPROXY, state_); |
| 51 DCHECK_NE(TRANSITION, state_); | 67 DCHECK_NE(TRANSITION, state_); |
| 52 DisallowHeapAllocation no_gc; | 68 DisallowHeapAllocation no_gc; |
| 53 has_property_ = false; | 69 has_property_ = false; |
| 54 | 70 |
| 55 JSReceiver* holder = *holder_; | 71 JSReceiver* holder = *holder_; |
| 56 Map* map = holder->map(); | 72 Map* map = holder->map(); |
| 57 | 73 |
| 58 // Perform lookup on current holder. | 74 if (map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) { |
| 59 state_ = LookupInHolder(map, holder); | 75 state_ = LookupInSpecialHolder(map, holder); |
| 60 if (IsFound()) return; | 76 if (IsFound()) return; |
| 77 } |
| 61 | 78 |
| 62 // Continue lookup if lookup on current holder failed. | 79 NextInternal(map, holder); |
| 80 } |
| 81 |
| 82 void LookupIterator::NextInternal(Map* map, JSReceiver* holder) { |
| 63 do { | 83 do { |
| 64 JSReceiver* maybe_holder = NextHolder(map); | 84 JSReceiver* maybe_holder = NextHolder(map); |
| 65 if (maybe_holder == nullptr) { | 85 if (maybe_holder == nullptr) { |
| 66 if (interceptor_state_ == InterceptorState::kSkipNonMasking) { | 86 if (interceptor_state_ == InterceptorState::kSkipNonMasking) { |
| 67 RestartLookupForNonMaskingInterceptors(); | 87 RestartLookupForNonMaskingInterceptors(); |
| 68 return; | 88 return; |
| 69 } | 89 } |
| 70 break; | 90 if (holder != *holder_) holder_ = handle(holder, isolate_); |
| 91 return; |
| 71 } | 92 } |
| 72 holder = maybe_holder; | 93 holder = maybe_holder; |
| 73 map = holder->map(); | 94 map = holder->map(); |
| 74 state_ = LookupInHolder(map, holder); | 95 state_ = LookupInHolder(map, holder); |
| 75 } while (!IsFound()); | 96 } while (!IsFound()); |
| 76 | 97 |
| 77 if (holder != *holder_) holder_ = handle(holder, isolate_); | 98 holder_ = handle(holder, isolate_); |
| 99 } |
| 100 |
| 101 void LookupIterator::RestartInternal(InterceptorState interceptor_state) { |
| 102 interceptor_state_ = interceptor_state; |
| 103 property_details_ = PropertyDetails::Empty(); |
| 104 Start(); |
| 78 } | 105 } |
| 79 | 106 |
| 80 | 107 |
| 81 void LookupIterator::RestartInternal(InterceptorState interceptor_state) { | |
| 82 state_ = NOT_FOUND; | |
| 83 interceptor_state_ = interceptor_state; | |
| 84 property_details_ = PropertyDetails::Empty(); | |
| 85 holder_ = initial_holder_; | |
| 86 number_ = DescriptorArray::kNotFound; | |
| 87 Next(); | |
| 88 } | |
| 89 | |
| 90 | |
| 91 // static | 108 // static |
| 92 Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver( | 109 Handle<JSReceiver> LookupIterator::GetRootForNonJSReceiver( |
| 93 Isolate* isolate, Handle<Object> receiver, uint32_t index) { | 110 Isolate* isolate, Handle<Object> receiver, uint32_t index) { |
| 94 // Strings are the only objects with properties (only elements) directly on | 111 // Strings are the only objects with properties (only elements) directly on |
| 95 // the wrapper. Hence we can skip generating the wrapper for all other cases. | 112 // the wrapper. Hence we can skip generating the wrapper for all other cases. |
| 96 if (index != kMaxUInt32 && receiver->IsString() && | 113 if (index != kMaxUInt32 && receiver->IsString() && |
| 97 index < static_cast<uint32_t>(String::cast(*receiver)->length())) { | 114 index < static_cast<uint32_t>(String::cast(*receiver)->length())) { |
| 98 // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native | 115 // TODO(verwaest): Speed this up. Perhaps use a cached wrapper on the native |
| 99 // context, ensuring that we don't leak it into JS? | 116 // context, ensuring that we don't leak it into JS? |
| 100 Handle<JSFunction> constructor = isolate->string_function(); | 117 Handle<JSFunction> constructor = isolate->string_function(); |
| 101 Handle<JSObject> result = isolate->factory()->NewJSObject(constructor); | 118 Handle<JSObject> result = isolate->factory()->NewJSObject(constructor); |
| 102 Handle<JSValue>::cast(result)->set_value(*receiver); | 119 Handle<JSValue>::cast(result)->set_value(*receiver); |
| 103 return result; | 120 return result; |
| 104 } | 121 } |
| 105 auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate); | 122 auto root = handle(receiver->GetRootMap(isolate)->prototype(), isolate); |
| 106 if (root->IsNull()) { | 123 if (root->IsNull()) { |
| 107 unsigned int magic = 0xbbbbbbbb; | 124 unsigned int magic = 0xbbbbbbbb; |
| 108 isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic); | 125 isolate->PushStackTraceAndDie(magic, *receiver, NULL, magic); |
| 109 } | 126 } |
| 110 return Handle<JSReceiver>::cast(root); | 127 return Handle<JSReceiver>::cast(root); |
| 111 } | 128 } |
| 112 | 129 |
| 113 | 130 |
| 114 Handle<Map> LookupIterator::GetReceiverMap() const { | 131 Handle<Map> LookupIterator::GetReceiverMap() const { |
| 115 if (receiver_->IsNumber()) return factory()->heap_number_map(); | 132 if (receiver_->IsNumber()) return factory()->heap_number_map(); |
| 116 return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_); | 133 return handle(Handle<HeapObject>::cast(receiver_)->map(), isolate_); |
| 117 } | 134 } |
| 118 | 135 |
| 119 | |
| 120 Handle<JSObject> LookupIterator::GetStoreTarget() const { | |
| 121 if (receiver_->IsJSGlobalProxy()) { | |
| 122 Object* prototype = JSGlobalProxy::cast(*receiver_)->map()->prototype(); | |
| 123 if (!prototype->IsNull()) { | |
| 124 return handle(JSGlobalObject::cast(prototype), isolate_); | |
| 125 } | |
| 126 } | |
| 127 return Handle<JSObject>::cast(receiver_); | |
| 128 } | |
| 129 | |
| 130 | |
| 131 bool LookupIterator::HasAccess() const { | 136 bool LookupIterator::HasAccess() const { |
| 132 DCHECK_EQ(ACCESS_CHECK, state_); | 137 DCHECK_EQ(ACCESS_CHECK, state_); |
| 133 return isolate_->MayAccess(handle(isolate_->context()), | 138 return isolate_->MayAccess(handle(isolate_->context()), |
| 134 GetHolder<JSObject>()); | 139 GetHolder<JSObject>()); |
| 135 } | 140 } |
| 136 | 141 |
| 137 | 142 |
| 138 void LookupIterator::ReloadPropertyInformation() { | 143 void LookupIterator::ReloadPropertyInformation() { |
| 139 state_ = BEFORE_PROPERTY; | 144 state_ = BEFORE_PROPERTY; |
| 140 interceptor_state_ = InterceptorState::kUninitialized; | 145 interceptor_state_ = InterceptorState::kUninitialized; |
| (...skipping 457 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 598 // Fall through. | 603 // Fall through. |
| 599 case InterceptorState::kSkipNonMasking: | 604 case InterceptorState::kSkipNonMasking: |
| 600 return true; | 605 return true; |
| 601 case InterceptorState::kProcessNonMasking: | 606 case InterceptorState::kProcessNonMasking: |
| 602 return false; | 607 return false; |
| 603 } | 608 } |
| 604 } | 609 } |
| 605 return interceptor_state_ == InterceptorState::kProcessNonMasking; | 610 return interceptor_state_ == InterceptorState::kProcessNonMasking; |
| 606 } | 611 } |
| 607 | 612 |
| 608 | |
| 609 JSReceiver* LookupIterator::NextHolder(Map* map) { | 613 JSReceiver* LookupIterator::NextHolder(Map* map) { |
| 610 DisallowHeapAllocation no_gc; | 614 DisallowHeapAllocation no_gc; |
| 611 if (!map->prototype()->IsJSReceiver()) return NULL; | 615 if (map->prototype() == heap()->null_value()) return NULL; |
| 612 | 616 |
| 613 DCHECK(!map->IsJSGlobalProxyMap() || map->has_hidden_prototype()); | 617 DCHECK(!map->IsJSGlobalProxyMap() || map->has_hidden_prototype()); |
| 614 | 618 |
| 615 if (!check_prototype_chain() && | 619 if (!check_prototype_chain() && |
| 616 !(check_hidden() && map->has_hidden_prototype()) && | 620 !(check_hidden() && map->has_hidden_prototype()) && |
| 617 // Always lookup behind the JSGlobalProxy into the JSGlobalObject, even | 621 // Always lookup behind the JSGlobalProxy into the JSGlobalObject, even |
| 618 // when not checking other hidden prototypes. | 622 // when not checking other hidden prototypes. |
| 619 !map->IsJSGlobalProxyMap()) { | 623 !map->IsJSGlobalProxyMap()) { |
| 620 return NULL; | 624 return NULL; |
| 621 } | 625 } |
| 622 | 626 |
| 623 return JSReceiver::cast(map->prototype()); | 627 return JSReceiver::cast(map->prototype()); |
| 624 } | 628 } |
| 625 | 629 |
| 626 LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const { | 630 LookupIterator::State LookupIterator::NotFound(JSReceiver* const holder) const { |
| 627 DCHECK(!IsElement()); | 631 DCHECK(!IsElement()); |
| 628 if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND; | 632 if (!holder->IsJSTypedArray() || !name_->IsString()) return NOT_FOUND; |
| 629 | 633 |
| 630 Handle<String> name_string = Handle<String>::cast(name_); | 634 Handle<String> name_string = Handle<String>::cast(name_); |
| 631 if (name_string->length() == 0) return NOT_FOUND; | 635 if (name_string->length() == 0) return NOT_FOUND; |
| 632 | 636 |
| 633 return IsSpecialIndex(isolate_->unicode_cache(), *name_string) | 637 return IsSpecialIndex(isolate_->unicode_cache(), *name_string) |
| 634 ? INTEGER_INDEXED_EXOTIC | 638 ? INTEGER_INDEXED_EXOTIC |
| 635 : NOT_FOUND; | 639 : NOT_FOUND; |
| 636 } | 640 } |
| 637 | 641 |
| 638 LookupIterator::State LookupIterator::LookupInHolder(Map* const map, | 642 LookupIterator::State LookupIterator::LookupInSpecialHolder( |
| 639 JSReceiver* const holder) { | 643 Map* const map, JSReceiver* const holder) { |
| 640 STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY); | 644 STATIC_ASSERT(INTERCEPTOR == BEFORE_PROPERTY); |
| 641 DisallowHeapAllocation no_gc; | |
| 642 if (interceptor_state_ == InterceptorState::kProcessNonMasking) { | |
| 643 return LookupNonMaskingInterceptorInHolder(map, holder); | |
| 644 } | |
| 645 switch (state_) { | 645 switch (state_) { |
| 646 case NOT_FOUND: | 646 case NOT_FOUND: |
| 647 if (map->IsJSProxyMap()) { | 647 if (map->IsJSProxyMap()) { |
| 648 if (IsElement() || !name_->IsPrivate()) return JSPROXY; | 648 if (IsElement() || !name_->IsPrivate()) return JSPROXY; |
| 649 } | 649 } |
| 650 if (map->is_access_check_needed()) { | 650 if (map->is_access_check_needed()) { |
| 651 if (IsElement() || !name_->IsPrivate()) return ACCESS_CHECK; | 651 if (IsElement() || !name_->IsPrivate()) return ACCESS_CHECK; |
| 652 } | 652 } |
| 653 // Fall through. | 653 // Fall through. |
| 654 case ACCESS_CHECK: | 654 case ACCESS_CHECK: |
| 655 if (check_interceptor() && HasInterceptor(map) && | 655 if (check_interceptor() && HasInterceptor(map) && |
| 656 !SkipInterceptor(JSObject::cast(holder))) { | 656 !SkipInterceptor(JSObject::cast(holder))) { |
| 657 if (IsElement() || !name_->IsPrivate()) return INTERCEPTOR; | 657 if (IsElement() || !name_->IsPrivate()) return INTERCEPTOR; |
| 658 } | 658 } |
| 659 // Fall through. | 659 // Fall through. |
| 660 case INTERCEPTOR: | 660 case INTERCEPTOR: |
| 661 if (IsElement()) { | 661 if (!IsElement() && map->IsJSGlobalObjectMap()) { |
| 662 JSObject* js_object = JSObject::cast(holder); | |
| 663 ElementsAccessor* accessor = js_object->GetElementsAccessor(); | |
| 664 FixedArrayBase* backing_store = js_object->elements(); | |
| 665 number_ = accessor->GetEntryForIndex(js_object, backing_store, index_); | |
| 666 if (number_ == kMaxUInt32) { | |
| 667 return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND; | |
| 668 } | |
| 669 property_details_ = accessor->GetDetails(js_object, number_); | |
| 670 } else if (!map->is_dictionary_map()) { | |
| 671 DescriptorArray* descriptors = map->instance_descriptors(); | |
| 672 int number = descriptors->SearchWithCache(isolate_, *name_, map); | |
| 673 if (number == DescriptorArray::kNotFound) return NotFound(holder); | |
| 674 number_ = static_cast<uint32_t>(number); | |
| 675 property_details_ = descriptors->GetDetails(number_); | |
| 676 } else if (map->IsJSGlobalObjectMap()) { | |
| 677 GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary(); | 662 GlobalDictionary* dict = JSObject::cast(holder)->global_dictionary(); |
| 678 int number = dict->FindEntry(name_); | 663 int number = dict->FindEntry(name_); |
| 679 if (number == GlobalDictionary::kNotFound) return NOT_FOUND; | 664 if (number == GlobalDictionary::kNotFound) return NOT_FOUND; |
| 680 number_ = static_cast<uint32_t>(number); | 665 number_ = static_cast<uint32_t>(number); |
| 681 DCHECK(dict->ValueAt(number_)->IsPropertyCell()); | 666 DCHECK(dict->ValueAt(number_)->IsPropertyCell()); |
| 682 PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_)); | 667 PropertyCell* cell = PropertyCell::cast(dict->ValueAt(number_)); |
| 683 if (cell->value()->IsTheHole()) return NOT_FOUND; | 668 if (cell->value()->IsTheHole()) return NOT_FOUND; |
| 684 property_details_ = cell->property_details(); | 669 property_details_ = cell->property_details(); |
| 685 } else { | 670 has_property_ = true; |
| 686 NameDictionary* dict = holder->property_dictionary(); | 671 switch (property_details_.kind()) { |
| 687 int number = dict->FindEntry(name_); | 672 case v8::internal::kData: |
| 688 if (number == NameDictionary::kNotFound) return NotFound(holder); | 673 return DATA; |
| 689 number_ = static_cast<uint32_t>(number); | 674 case v8::internal::kAccessor: |
| 690 property_details_ = dict->DetailsAt(number_); | 675 return ACCESSOR; |
| 676 } |
| 691 } | 677 } |
| 692 has_property_ = true; | 678 return LookupInRegularHolder(map, holder); |
| 693 switch (property_details_.kind()) { | |
| 694 case v8::internal::kData: | |
| 695 return DATA; | |
| 696 case v8::internal::kAccessor: | |
| 697 return ACCESSOR; | |
| 698 } | |
| 699 case ACCESSOR: | 679 case ACCESSOR: |
| 700 case DATA: | 680 case DATA: |
| 701 return NOT_FOUND; | 681 return NOT_FOUND; |
| 702 case INTEGER_INDEXED_EXOTIC: | 682 case INTEGER_INDEXED_EXOTIC: |
| 703 case JSPROXY: | 683 case JSPROXY: |
| 704 case TRANSITION: | 684 case TRANSITION: |
| 705 UNREACHABLE(); | 685 UNREACHABLE(); |
| 706 } | 686 } |
| 707 UNREACHABLE(); | 687 UNREACHABLE(); |
| 708 return state_; | 688 return NOT_FOUND; |
| 709 } | 689 } |
| 710 | 690 |
| 691 LookupIterator::State LookupIterator::LookupInRegularHolder( |
| 692 Map* const map, JSReceiver* const holder) { |
| 693 DisallowHeapAllocation no_gc; |
| 694 if (interceptor_state_ == InterceptorState::kProcessNonMasking) { |
| 695 return NOT_FOUND; |
| 696 } |
| 711 | 697 |
| 712 LookupIterator::State LookupIterator::LookupNonMaskingInterceptorInHolder( | 698 if (IsElement()) { |
| 713 Map* const map, JSReceiver* const holder) { | 699 JSObject* js_object = JSObject::cast(holder); |
| 714 switch (state_) { | 700 ElementsAccessor* accessor = js_object->GetElementsAccessor(); |
| 715 case NOT_FOUND: | 701 FixedArrayBase* backing_store = js_object->elements(); |
| 716 if (check_interceptor() && HasInterceptor(map) && | 702 number_ = accessor->GetEntryForIndex(js_object, backing_store, index_); |
| 717 !SkipInterceptor(JSObject::cast(holder))) { | 703 if (number_ == kMaxUInt32) { |
| 718 return INTERCEPTOR; | 704 return holder->IsJSTypedArray() ? INTEGER_INDEXED_EXOTIC : NOT_FOUND; |
| 719 } | 705 } |
| 720 // Fall through. | 706 property_details_ = accessor->GetDetails(js_object, number_); |
| 721 default: | 707 } else if (!map->is_dictionary_map()) { |
| 722 return NOT_FOUND; | 708 DescriptorArray* descriptors = map->instance_descriptors(); |
| 709 int number = descriptors->SearchWithCache(isolate_, *name_, map); |
| 710 if (number == DescriptorArray::kNotFound) return NotFound(holder); |
| 711 number_ = static_cast<uint32_t>(number); |
| 712 property_details_ = descriptors->GetDetails(number_); |
| 713 } else { |
| 714 NameDictionary* dict = holder->property_dictionary(); |
| 715 int number = dict->FindEntry(name_); |
| 716 if (number == NameDictionary::kNotFound) return NotFound(holder); |
| 717 number_ = static_cast<uint32_t>(number); |
| 718 property_details_ = dict->DetailsAt(number_); |
| 723 } | 719 } |
| 720 has_property_ = true; |
| 721 switch (property_details_.kind()) { |
| 722 case v8::internal::kData: |
| 723 return DATA; |
| 724 case v8::internal::kAccessor: |
| 725 return ACCESSOR; |
| 726 } |
| 727 |
| 724 UNREACHABLE(); | 728 UNREACHABLE(); |
| 725 return state_; | 729 return state_; |
| 726 } | 730 } |
| 727 | 731 |
| 728 } // namespace internal | 732 } // namespace internal |
| 729 } // namespace v8 | 733 } // namespace v8 |
| OLD | NEW |