diff options
| -rw-r--r-- | runtime/class_linker.cc | 520 |
1 files changed, 321 insertions, 199 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 0c3549a0bd..0878667872 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1104,6 +1104,19 @@ void ClassLinker::RunRootClinits(Thread* self) { } } +ALWAYS_INLINE +static uint32_t ComputeMethodHash(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(!method->IsRuntimeMethod()); + DCHECK(!method->IsProxyMethod()); + DCHECK(!method->IsObsolete()); + // Do not use `ArtMethod::GetNameView()` to avoid unnecessary runtime/proxy/obsolete method + // checks. It is safe to avoid the read barrier here, see `ArtMethod::GetDexFile()`. + const DexFile& dex_file = method->GetDeclaringClass<kWithoutReadBarrier>()->GetDexFile(); + const dex::MethodId& method_id = dex_file.GetMethodId(method->GetDexMethodIndex()); + std::string_view name = dex_file.GetMethodNameView(method_id); + return ComputeModifiedUtf8Hash(name); +} + static void InitializeObjectVirtualMethodHashes(ObjPtr<mirror::Class> java_lang_Object, PointerSize pointer_size, /*out*/ ArrayRef<uint32_t> virtual_method_hashes) @@ -1111,8 +1124,7 @@ static void InitializeObjectVirtualMethodHashes(ObjPtr<mirror::Class> java_lang_ ArraySlice<ArtMethod> virtual_methods = java_lang_Object->GetVirtualMethods(pointer_size); DCHECK_EQ(virtual_method_hashes.size(), virtual_methods.size()); for (size_t i = 0; i != virtual_method_hashes.size(); ++i) { - std::string_view name = virtual_methods[i].GetNameView(); - virtual_method_hashes[i] = ComputeModifiedUtf8Hash(name); + virtual_method_hashes[i] = ComputeMethodHash(&virtual_methods[i]); } } @@ -6169,79 +6181,6 @@ class MethodNameAndSignatureComparator final : public ValueObject { std::string_view name_view_; }; -class LinkVirtualHashTable { - public: - LinkVirtualHashTable(Handle<mirror::Class> klass, - size_t hash_size, - uint32_t* hash_table, - PointerSize image_pointer_size) - : klass_(klass), - hash_size_(hash_size), - hash_table_(hash_table), - image_pointer_size_(image_pointer_size) { - std::fill(hash_table_, hash_table_ + hash_size_, invalid_index_); - } - - void Add(uint32_t virtual_method_index) REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* local_method = klass_->GetVirtualMethodDuringLinking( - virtual_method_index, image_pointer_size_); - std::string_view name_view = - local_method->GetInterfaceMethodIfProxy(image_pointer_size_)->GetNameView(); - uint32_t hash = ComputeModifiedUtf8Hash(name_view); - uint32_t index = hash % hash_size_; - // Linear probe until we have an empty slot. - while (hash_table_[index] != invalid_index_) { - if (++index == hash_size_) { - index = 0; - } - } - hash_table_[index] = virtual_method_index; - } - - uint32_t FindAndRemove(MethodNameAndSignatureComparator* comparator, uint32_t hash) - REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK_EQ(hash, ComputeModifiedUtf8Hash(comparator->GetNameView())); - size_t index = hash % hash_size_; - while (true) { - const uint32_t value = hash_table_[index]; - // Since linear probe makes continuous blocks, hitting an invalid index means we are done - // the block and can safely assume not found. - if (value == invalid_index_) { - break; - } - if (value != removed_index_) { // This signifies not already overriden. - ArtMethod* virtual_method = - klass_->GetVirtualMethodDuringLinking(value, image_pointer_size_); - if (comparator->HasSameNameAndSignature( - virtual_method->GetInterfaceMethodIfProxy(image_pointer_size_))) { - hash_table_[index] = removed_index_; - return value; - } - } - if (++index == hash_size_) { - index = 0; - } - } - return GetNotFoundIndex(); - } - - static uint32_t GetNotFoundIndex() { - return invalid_index_; - } - - private: - static const uint32_t invalid_index_; - static const uint32_t removed_index_; - - Handle<mirror::Class> klass_; - const size_t hash_size_; - uint32_t* const hash_table_; - const PointerSize image_pointer_size_; -}; - -const uint32_t LinkVirtualHashTable::invalid_index_ = std::numeric_limits<uint32_t>::max(); -const uint32_t LinkVirtualHashTable::removed_index_ = std::numeric_limits<uint32_t>::max() - 1; - // Determine if the given iface has any subinterface in the given list that declares the method // specified by 'target'. // @@ -7216,6 +7155,14 @@ class ClassLinker::LinkMethodsHelper { REQUIRES_SHARED(Locks::mutator_lock_); private: + // Assign vtable indexes to declared virtual methods for a non-interface class other + // than `java.lang.Object`. Returns the number of vtable entries on success, 0 on failure. + template <bool kEmbeddedSuperVTable> + size_t AssignVtableIndexes(ObjPtr<mirror::Class> klass, + ObjPtr<mirror::Class> super_class, + size_t num_virtual_methods) + REQUIRES_SHARED(Locks::mutator_lock_); + bool LinkJavaLangObjectVirtualMethods(Thread* self, Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; @@ -7312,6 +7259,123 @@ class ClassLinker::LinkMethodsHelper { << overriding_default_conflict_methods_.size(); } + class MethodIndexEmptyFn { + public: + void MakeEmpty(uint32_t& item) const { + item = dex::kDexNoIndex; + } + bool IsEmpty(const uint32_t& item) const { + return item == dex::kDexNoIndex; + } + }; + + class VTableAccessorEmbedded { + public: + explicit VTableAccessorEmbedded(ObjPtr<mirror::Class> klass) + REQUIRES_SHARED(Locks::mutator_lock_) + : klass_(klass) { + DCHECK(klass->ShouldHaveEmbeddedVTable()); + } + + size_t GetVTableLength() const REQUIRES_SHARED(Locks::mutator_lock_) { + return dchecked_integral_cast<size_t>(klass_->GetEmbeddedVTableLength()); + } + + ArtMethod* GetVTableEntry(uint32_t index) const REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_LT(index, GetVTableLength()); + return klass_->GetEmbeddedVTableEntry(index, kPointerSize); + } + + private: + ObjPtr<mirror::Class> klass_; + }; + + class VTableAccessorNotEmbedded { + public: + explicit VTableAccessorNotEmbedded(ObjPtr<mirror::Class> klass) + REQUIRES_SHARED(Locks::mutator_lock_) + : vtable_(klass->GetVTable()) { + DCHECK(!klass->ShouldHaveEmbeddedVTable()); + DCHECK(vtable_ != nullptr); + } + + size_t GetVTableLength() const REQUIRES_SHARED(Locks::mutator_lock_) { + return dchecked_integral_cast<size_t>(vtable_->GetLength()); + } + + ArtMethod* GetVTableEntry(uint32_t index) const REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK_LT(index, GetVTableLength()); + return vtable_->GetElementPtrSize<ArtMethod*, kPointerSize>(index); + } + + private: + ObjPtr<mirror::PointerArray> vtable_; + }; + + template <bool kEmbedded> + using VTableAccessor = + std::conditional_t<kEmbedded, VTableAccessorEmbedded, VTableAccessorNotEmbedded>; + + template <bool kEmbedded> + class VTableSignatureHash { + public: + explicit VTableSignatureHash(VTableAccessor<kEmbedded> accessor) + REQUIRES_SHARED(Locks::mutator_lock_) + : accessor_(accessor) {} + + // NO_THREAD_SAFETY_ANALYSIS: This is called from unannotated `HashSet<>` functions. + size_t operator()(ArtMethod* method) const NO_THREAD_SAFETY_ANALYSIS { + return ComputeMethodHash(method); + } + + // NO_THREAD_SAFETY_ANALYSIS: This is called from unannotated `HashSet<>` functions. + size_t operator()(uint32_t index) const NO_THREAD_SAFETY_ANALYSIS { + return ComputeMethodHash(accessor_.GetVTableEntry(index)); + } + + private: + VTableAccessor<kEmbedded> accessor_; + }; + + template <bool kEmbedded> + class VTableSignatureEqual { + public: + explicit VTableSignatureEqual(VTableAccessor<kEmbedded> accessor) + REQUIRES_SHARED(Locks::mutator_lock_) + : accessor_(accessor) {} + + // NO_THREAD_SAFETY_ANALYSIS: This is called from unannotated `HashSet<>` functions. + bool operator()(uint32_t lhs_index, ArtMethod* rhs) const NO_THREAD_SAFETY_ANALYSIS { + ArtMethod* lhs = accessor_.GetVTableEntry(lhs_index); + const DexFile* lhs_dex_file = lhs->GetDexFile(); + const DexFile* rhs_dex_file = rhs->GetDexFile(); + const dex::MethodId& lhs_mid = lhs_dex_file->GetMethodId(lhs->GetDexMethodIndex()); + const dex::MethodId& rhs_mid = rhs_dex_file->GetMethodId(rhs->GetDexMethodIndex()); + if (lhs_dex_file == rhs_dex_file) { + return lhs_mid.name_idx_ == rhs_mid.name_idx_ && + lhs_mid.proto_idx_ == rhs_mid.proto_idx_; + } else { + return + lhs_dex_file->GetMethodNameView(lhs_mid) == rhs_dex_file->GetMethodNameView(rhs_mid) && + lhs_dex_file->GetMethodSignature(lhs_mid) == rhs_dex_file->GetMethodSignature(rhs_mid); + } + } + + // NO_THREAD_SAFETY_ANALYSIS: This is called from unannotated `HashSet<>` functions. + bool operator()(uint32_t lhs_index, uint32_t rhs_index) const NO_THREAD_SAFETY_ANALYSIS { + return (*this)(lhs_index, accessor_.GetVTableEntry(rhs_index)); + } + + private: + VTableAccessor<kEmbedded> accessor_; + }; + + template <bool kEmbedded> + using VTableSignatureSet = ScopedArenaHashSet<uint32_t, + MethodIndexEmptyFn, + VTableSignatureHash<kEmbedded>, + VTableSignatureEqual<kEmbedded>>; + static constexpr size_t kMethodAlignment = ArtMethod::Alignment(kPointerSize); static constexpr size_t kMethodSize = ArtMethod::Size(kPointerSize); @@ -7630,7 +7694,7 @@ ObjPtr<mirror::PointerArray> ClassLinker::LinkMethodsHelper<kPointerSize>::Updat // Update old vtable methods. We use the `default_translations_` map to figure out what each // vtable entry should be updated to, if they need to be at all. for (size_t i = 0; i < old_vtable_count; ++i) { - ArtMethod* translated_method = vtable->GetElementPtrSize<ArtMethod*>(i, kPointerSize); + ArtMethod* translated_method = vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(i); // Try and find what we need to change this method to. auto translation_it = default_translations_.find(i); if (translation_it != default_translations_.end()) { @@ -7691,7 +7755,7 @@ void ClassLinker::LinkMethodsHelper<kPointerSize>::UpdateIfTable(Handle<mirror:: for (size_t i = 0; i < ifcount; ++i) { for (size_t j = 0, count = iftable->GetMethodArrayCount(i); j < count; ++j) { ObjPtr<mirror::PointerArray> method_array = iftable->GetMethodArray(i); - ArtMethod* m = method_array->GetElementPtrSize<ArtMethod*>(j, kPointerSize); + ArtMethod* m = method_array->GetElementPtrSize<ArtMethod*, kPointerSize>(j); DCHECK(m != nullptr) << klass_->PrettyClass(); auto it = move_table_.find(m); if (it != move_table_.end()) { @@ -7715,6 +7779,126 @@ void ClassLinker::LinkMethodsHelper<kPointerSize>::UpdateIMT(ArtMethod** out_imt } template <PointerSize kPointerSize> +template <bool kEmbeddedSuperVTable> +size_t ClassLinker::LinkMethodsHelper<kPointerSize>::AssignVtableIndexes( + ObjPtr<mirror::Class> klass, ObjPtr<mirror::Class> super_class, size_t num_virtual_methods) { + DCHECK(!klass->IsInterface()); + DCHECK(klass->HasSuperClass()); + DCHECK(klass->GetSuperClass() == super_class); + DCHECK_EQ(kEmbeddedSuperVTable, super_class->ShouldHaveEmbeddedVTable()); + + // There should be no thread suspension unless we want to throw an exception. + std::optional<ScopedAssertNoThreadSuspension> sants(__FUNCTION__); + + // Prepare a hash table with virtual methods from the superclass. + // For the unlikely cases that there are multiple methods with the same signature + // but different vtable indexes, keep an array with indexes of the previous + // methods with the same signature (walked as singly-linked lists). + VTableAccessor<kEmbeddedSuperVTable> super_vtable_accessor(super_class); + const size_t super_vtable_length = super_vtable_accessor.GetVTableLength(); + static constexpr double kMinLoadFactor = 0.3; + static constexpr double kMaxLoadFactor = 0.5; + static constexpr size_t kMaxStackBuferSize = 250; + const size_t hash_table_size = super_vtable_length * 3; + uint32_t* hash_table_ptr = (hash_table_size <= kMaxStackBuferSize) + ? reinterpret_cast<uint32_t*>(alloca(hash_table_size * sizeof(*hash_table_ptr))) + : allocator_.AllocArray<uint32_t>(hash_table_size); + VTableSignatureSet<kEmbeddedSuperVTable> super_vtable_signatures( + kMinLoadFactor, + kMaxLoadFactor, + VTableSignatureHash<kEmbeddedSuperVTable>(super_vtable_accessor), + VTableSignatureEqual<kEmbeddedSuperVTable>(super_vtable_accessor), + hash_table_ptr, + hash_table_size, + allocator_.Adapter()); + ScopedArenaVector<uint32_t> same_signature_virtual_methods_lists_(allocator_.Adapter()); + // Insert the first `mirror::Object::kVTableLength` indexes with pre-calculated hashes. + DCHECK_GE(super_vtable_length, mirror::Object::kVTableLength); + for (uint32_t i = 0; i != mirror::Object::kVTableLength; ++i) { + size_t hash = class_linker_->object_virtual_method_hashes_[i]; + bool inserted = super_vtable_signatures.InsertWithHash(i, hash).second; + DCHECK(inserted); // No duplicate signatures in `java.lang.Object`. + } + // Insert the remaining indexes, check for duplicate signatures. + if (super_vtable_length > mirror::Object::kVTableLength) { + for (size_t i = mirror::Object::kVTableLength; i < super_vtable_length; ++i) { + // Use `super_vtable_accessor` for getting the method for hash calculation. + // Letting `HashSet<>::insert()` use the internal accessor copy in the hash + // function prevents the compiler from optimizing this properly because the + // compiler cannot prove that the accessor copy is immutable. + size_t hash = ComputeMethodHash(super_vtable_accessor.GetVTableEntry(i)); + auto [it, inserted] = super_vtable_signatures.InsertWithHash(i, hash); + if (UNLIKELY(!inserted)) { + if (same_signature_virtual_methods_lists_.empty()) { + same_signature_virtual_methods_lists_.resize(super_vtable_length, dex::kDexNoIndex); + } + DCHECK_LT(*it, i); + same_signature_virtual_methods_lists_[i] = *it; + *it = i; + } + } + } + + // For each declared virtual method, look for a superclass virtual method + // to override and assign a new vtable index if no method was overridden. + const bool is_proxy_class = klass->IsProxyClass(); + size_t vtable_length = super_vtable_length; + for (size_t i = 0; i < num_virtual_methods; ++i) { + ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(i, kPointerSize); + ArtMethod* signature_method = UNLIKELY(is_proxy_class) + ? virtual_method->GetInterfaceMethodForProxyUnchecked(kPointerSize) + : virtual_method; + size_t hash = ComputeMethodHash(signature_method); + auto it = super_vtable_signatures.FindWithHash(signature_method, hash); + if (it != super_vtable_signatures.end()) { + size_t super_index = *it; + DCHECK_LT(super_index, super_vtable_length); + ArtMethod* super_method = super_vtable_accessor.GetVTableEntry(super_index); + // Historical note: Before Android 4.1, an inaccessible package-private + // superclass method would have been incorrectly overridden. + bool overrides = klass->CanAccessMember(super_method->GetDeclaringClass(), + super_method->GetAccessFlags()); + if (UNLIKELY(!same_signature_virtual_methods_lists_.empty())) { + // We override only the first accessible virtual method from superclass. + // TODO: Override all methods that need to be overridden according to JLS. b/211854716 + size_t current_index = super_index; + while (same_signature_virtual_methods_lists_[current_index] != dex::kDexNoIndex) { + DCHECK_LT(same_signature_virtual_methods_lists_[current_index], current_index); + current_index = same_signature_virtual_methods_lists_[current_index]; + ArtMethod* current_method = super_vtable_accessor.GetVTableEntry(current_index); + if (klass->CanAccessMember(current_method->GetDeclaringClass(), + current_method->GetAccessFlags())) { + overrides = true; + super_index = current_index; + super_method = current_method; + } + } + } + if (overrides) { + if (super_method->IsFinal()) { + sants.reset(); + ThrowLinkageError(klass, "Method %s overrides final method in class %s", + virtual_method->PrettyMethod().c_str(), + super_method->GetDeclaringClassDescriptor()); + return 0u; + } + virtual_method->SetMethodIndex(super_index); + continue; + } + } + // The method does not override any method from superclass, so it needs a new vtable index. + virtual_method->SetMethodIndex(vtable_length); + ++vtable_length; + } + if (UNLIKELY(!IsUint<16>(vtable_length))) { + sants.reset(); + ThrowClassFormatError(klass, "Too many methods defined on class: %zd", vtable_length); + return 0u; + } + return vtable_length; +} + +template <PointerSize kPointerSize> FLATTEN bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkVirtualMethods( Thread* self, @@ -7758,106 +7942,70 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkVirtualMethods( return true; } else if (LIKELY(klass->HasSuperClass())) { const size_t super_vtable_length = klass->GetSuperClass()->GetVTableLength(); - const size_t max_count = num_virtual_methods + super_vtable_length; StackHandleScope<3> hs(self); Handle<mirror::Class> super_class(hs.NewHandle(klass->GetSuperClass())); - MutableHandle<mirror::PointerArray> vtable; - if (super_class->ShouldHaveEmbeddedVTable()) { - vtable = hs.NewHandle(class_linker_->AllocPointerArray(self, max_count)); - if (UNLIKELY(vtable == nullptr)) { - self->AssertPendingOOMException(); - return false; - } - for (size_t i = 0; i < super_vtable_length; i++) { - vtable->SetElementPtrSize( - i, super_class->GetEmbeddedVTableEntry(i, kPointerSize), kPointerSize); - } - // We might need to change vtable if we have new virtual methods or new interfaces (since that - // might give us new default methods). If no new interfaces then we can skip the rest since - // the class cannot override any of the super-class's methods. This is required for - // correctness since without it we might not update overridden default method vtable entries - // correctly. - if (num_virtual_methods == 0 && super_class->GetIfTableCount() == klass->GetIfTableCount()) { - klass->SetVTable(vtable.Get()); - return true; - } - } else { - DCHECK(super_class->IsAbstract() && !super_class->IsArrayClass()); - Handle<mirror::PointerArray> super_vtable = hs.NewHandle(super_class->GetVTable()); - CHECK(super_vtable != nullptr) << super_class->PrettyClass(); - // We might need to change vtable if we have new virtual methods or new interfaces (since that - // might give us new default methods). See comment above. - if (num_virtual_methods == 0 && super_class->GetIfTableCount() == klass->GetIfTableCount()) { - klass->SetVTable(super_vtable.Get()); - return true; - } - vtable = hs.NewHandle(ObjPtr<mirror::PointerArray>::DownCast( - mirror::Array::CopyOf(super_vtable, self, max_count))); - if (UNLIKELY(vtable == nullptr)) { - self->AssertPendingOOMException(); - return false; + + // If there are no new virtual methods and no new interfaces, we can simply reuse + // the vtable from superclass. We may need to make a copy if it's embedded. + if (num_virtual_methods == 0 && super_class->GetIfTableCount() == klass->GetIfTableCount()) { + if (super_class->ShouldHaveEmbeddedVTable()) { + ObjPtr<mirror::PointerArray> vtable = + class_linker_->AllocPointerArray(self, super_vtable_length); + if (UNLIKELY(vtable == nullptr)) { + self->AssertPendingOOMException(); + return false; + } + for (size_t i = 0; i < super_vtable_length; i++) { + vtable->SetElementPtrSize( + i, super_class->GetEmbeddedVTableEntry(i, kPointerSize), kPointerSize); + } + klass->SetVTable(vtable); + } else { + DCHECK(super_class->IsAbstract() && !super_class->IsArrayClass()); + ObjPtr<mirror::PointerArray> super_vtable = super_class->GetVTable(); + CHECK(super_vtable != nullptr) << super_class->PrettyClass(); + klass->SetVTable(super_vtable); } + return true; } - // How the algorithm works: - // 1. Populate hash table by adding num_virtual_methods from klass. The values in the hash - // table are: invalid_index for unused slots, index super_vtable_length + i for a virtual - // method which has not been matched to a vtable method, and j if the virtual method at the - // index overrode the super virtual method at index j. - // 2. Loop through super virtual methods, if they overwrite, update hash table to j - // (j < super_vtable_length) to avoid redundant checks. (TODO maybe use this info for reducing - // the need for the initial vtable which we later shrink back down). - // 3. Add non overridden methods to the end of the vtable. - static constexpr size_t kMaxStackHash = 250; - // + 1 so that even if we only have new default methods we will still be able to use this hash - // table (i.e. it will never have 0 size). - const size_t hash_table_size = num_virtual_methods * 3 + 1; - uint32_t* hash_table_ptr; - std::unique_ptr<uint32_t[]> hash_heap_storage; - if (hash_table_size <= kMaxStackHash) { - hash_table_ptr = reinterpret_cast<uint32_t*>( - alloca(hash_table_size * sizeof(*hash_table_ptr))); - } else { - hash_heap_storage.reset(new uint32_t[hash_table_size]); - hash_table_ptr = hash_heap_storage.get(); + + size_t final_vtable_size = super_class->ShouldHaveEmbeddedVTable() + ? AssignVtableIndexes</*kEmbeddedSuperVTable=*/ true>( + klass.Get(), super_class.Get(), num_virtual_methods) + : AssignVtableIndexes</*kEmbeddedSuperVTable=*/ false>( + klass.Get(), super_class.Get(), num_virtual_methods); + if (final_vtable_size == 0u) { + self->AssertPendingException(); + return false; } - LinkVirtualHashTable hash_table(klass, hash_table_size, hash_table_ptr, kPointerSize); - // Add virtual methods to the hash table. - for (size_t i = 0; i < num_virtual_methods; ++i) { - DCHECK(klass->GetVirtualMethodDuringLinking( - i, kPointerSize)->GetDeclaringClass() != nullptr); - hash_table.Add(i); - } - // Loop through each super vtable method and see if they are overridden by a method we added to - // the hash table. - for (size_t j = 0; j < super_vtable_length; ++j) { - // Search the hash table to see if we are overridden by any method. - ArtMethod* super_method = vtable->GetElementPtrSize<ArtMethod*>(j, kPointerSize); - if (!klass->CanAccessMember(super_method->GetDeclaringClass(), - super_method->GetAccessFlags())) { - // Continue on to the next method since this one is package private - // and cannot be overridden. Before Android 4.1, the package-private - // method super_method might have been incorrectly overridden. + DCHECK(IsUint<16>(final_vtable_size)); + + // Allocate the new vtable. + Handle<mirror::PointerArray> vtable = + hs.NewHandle(class_linker_->AllocPointerArray(self, final_vtable_size)); + if (UNLIKELY(vtable == nullptr)) { + self->AssertPendingOOMException(); + return false; + } + + // Store new virtual methods in the new vtable. + for (ArtMethod& virtual_method : klass->GetVirtualMethodsSliceUnchecked(kPointerSize)) { + int32_t vtable_index = virtual_method.GetMethodIndexDuringLinking(); + vtable->SetElementPtrSize(vtable_index, &virtual_method, kPointerSize); + } + + // For non-overridden vtable slots, copy a method from `super_class`. + for (size_t j = 0; j != super_vtable_length; ++j) { + if (vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(j) != nullptr) { + // Filled with new virtual method. continue; } - MethodNameAndSignatureComparator super_method_name_comparator( - super_method->GetInterfaceMethodIfProxy(kPointerSize)); - // We remove the method so that subsequent lookups will be faster by making the hash-map - // smaller as we go on. - uint32_t hash = (j < mirror::Object::kVTableLength) - ? class_linker_->object_virtual_method_hashes_[j] - : ComputeModifiedUtf8Hash(super_method_name_comparator.GetNameView()); - uint32_t hash_index = hash_table.FindAndRemove(&super_method_name_comparator, hash); - if (hash_index != hash_table.GetNotFoundIndex()) { - ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(hash_index, kPointerSize); - if (super_method->IsFinal()) { - ThrowLinkageError(klass.Get(), "Method %s overrides final method in class %s", - virtual_method->PrettyMethod().c_str(), - super_method->GetDeclaringClassDescriptor()); - return false; - } - vtable->SetElementPtrSize(j, virtual_method, kPointerSize); - virtual_method->SetMethodIndex(j); - } else if (super_method->IsOverridableByDefaultMethod()) { + ArtMethod* super_method = super_class->GetVTableEntry(j, kPointerSize); + vtable->SetElementPtrSize(j, super_method, kPointerSize); + // TODO: Postpone the search for overiding default methods until `LinkInterfaceMethods()`. + if (klass->CanAccessMember(super_method->GetDeclaringClass(), + super_method->GetAccessFlags()) && + super_method->IsOverridableByDefaultMethod()) { // We didn't directly override this method but we might through default methods... // Check for default method update. ArtMethod* default_method = nullptr; @@ -7910,33 +8058,7 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkVirtualMethods( } } } - size_t actual_count = super_vtable_length; - // Add the non-overridden methods at the end. - for (size_t i = 0; i < num_virtual_methods; ++i) { - ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i, kPointerSize); - size_t method_idx = local_method->GetMethodIndexDuringLinking(); - if (method_idx < super_vtable_length && - local_method == vtable->GetElementPtrSize<ArtMethod*>(method_idx, kPointerSize)) { - continue; - } - vtable->SetElementPtrSize(actual_count, local_method, kPointerSize); - local_method->SetMethodIndex(actual_count); - ++actual_count; - } - if (!IsUint<16>(actual_count)) { - ThrowClassFormatError(klass.Get(), "Too many methods defined on class: %zd", actual_count); - return false; - } - // Shrink vtable if possible - CHECK_LE(actual_count, max_count); - if (actual_count < max_count) { - vtable.Assign(ObjPtr<mirror::PointerArray>::DownCast( - mirror::Array::CopyOf(vtable, self, actual_count))); - if (UNLIKELY(vtable == nullptr)) { - self->AssertPendingOOMException(); - return false; - } - } + klass->SetVTable(vtable.Get()); } else { return LinkJavaLangObjectVirtualMethods(self, klass); @@ -8076,7 +8198,7 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkInterfaceMethods( for (int32_t k = input_array_length - 1; k >= 0; --k) { ArtMethod* vtable_method = using_virtuals ? &input_virtual_methods[k] : - input_vtable_array->GetElementPtrSize<ArtMethod*>(k, kPointerSize); + input_vtable_array->GetElementPtrSize<ArtMethod*, kPointerSize>(k); ArtMethod* vtable_method_for_name_comparison = vtable_method->GetInterfaceMethodIfProxy(kPointerSize); DCHECK(!vtable_method->IsStatic()) << vtable_method->PrettyMethod(); @@ -8133,7 +8255,7 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkInterfaceMethods( // TODO This is rather dirty but it is faster than searching through the entire vtable // every time. ArtMethod* supers_method = - method_array->GetElementPtrSize<ArtMethod*>(j, kPointerSize); + method_array->GetElementPtrSize<ArtMethod*, kPointerSize>(j); DCHECK(supers_method != nullptr); DCHECK(interface_name_comparator.HasSameNameAndSignature(supers_method)); if (LIKELY(!supers_method->IsOverridableByDefaultMethod())) { |