summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/class_linker.cc520
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())) {