Rewrite virtual method linking.
Instead of using a custom hash set, use the `HashSet<>`.
Instead of inserting new methods and matching superclass
methods, start by inserting superclass methods and then
match new virtual methods. (The new approach can later be
extended to also insert new methods to allow faster search
for linking interface methods.)
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 181943478
Change-Id: Iec9ac10394414da4d3c2a58f279b36c32bf4b568
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 0c3549a..0878667 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1104,6 +1104,19 @@
}
}
+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 @@
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 @@
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 @@
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 @@
<< 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 @@
// 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 @@
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 @@
}
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 @@
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;
- }
- }
- // 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();
- }
- 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.
- 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());
+
+ // 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;
}
- vtable->SetElementPtrSize(j, virtual_method, kPointerSize);
- virtual_method->SetMethodIndex(j);
- } else if (super_method->IsOverridableByDefaultMethod()) {
+ 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;
+ }
+
+ 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;
+ }
+ 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;
+ }
+ 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 @@
}
}
}
- 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 @@
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 @@
// 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())) {