Support directly invoking interface default methods
With the Java 8 Language one is allowed to directly call default
interface methods of interfaces one (directly) implements through the
use of the super keyword. We support this behavior through the
invoke-super opcode with the target being an interface.
We add 3 tests for this behavior.
Currently only supports slow-path interpreter.
Invoke-super is currently extremely slow.
Bug: 24618811
Change-Id: I7e06e17326f7dbae0116bd7dfefca151f0092bd2
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 06f40e4..076bd74 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -46,6 +46,7 @@
#include "dex_file-inl.h"
#include "entrypoints/entrypoint_utils.h"
#include "entrypoints/runtime_asm_entrypoints.h"
+#include "experimental_flags.h"
#include "gc_root-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap.h"
@@ -4703,7 +4704,6 @@
return false;
}
bool has_defaults = false;
- // TODO May need to replace this with real VTable for invoke_super
// Assign each method an IMT index and set the default flag.
for (size_t i = 0; i < num_virtual_methods; ++i) {
ArtMethod* m = klass->GetVirtualMethodDuringLinking(i, image_pointer_size_);
@@ -5341,6 +5341,53 @@
}
}
+static void FillImtFromSuperClass(Handle<mirror::Class> klass,
+ Handle<mirror::IfTable> iftable,
+ ArtMethod* unimplemented_method,
+ ArtMethod* imt_conflict_method,
+ ArtMethod** out_imt,
+ size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(klass->HasSuperClass());
+ mirror::Class* super_class = klass->GetSuperClass();
+ if (super_class->ShouldHaveEmbeddedImtAndVTable()) {
+ for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
+ out_imt[i] = super_class->GetEmbeddedImTableEntry(i, pointer_size);
+ }
+ } else {
+ // No imt in the super class, need to reconstruct from the iftable.
+ mirror::IfTable* if_table = super_class->GetIfTable();
+ const size_t length = super_class->GetIfTableCount();
+ for (size_t i = 0; i < length; ++i) {
+ mirror::Class* interface = iftable->GetInterface(i);
+ const size_t num_virtuals = interface->NumDeclaredVirtualMethods();
+ const size_t method_array_count = if_table->GetMethodArrayCount(i);
+ DCHECK_EQ(num_virtuals, method_array_count);
+ if (method_array_count == 0) {
+ continue;
+ }
+ auto* method_array = if_table->GetMethodArray(i);
+ for (size_t j = 0; j < num_virtuals; ++j) {
+ auto method = method_array->GetElementPtrSize<ArtMethod*>(j, pointer_size);
+ DCHECK(method != nullptr) << PrettyClass(super_class);
+ // Miranda methods cannot be used to implement an interface method and defaults should be
+ // skipped in case we override it.
+ if (method->IsDefault() || method->IsMiranda()) {
+ continue;
+ }
+ ArtMethod* interface_method = interface->GetVirtualMethod(j, pointer_size);
+ uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
+ auto** imt_ref = &out_imt[imt_index];
+ if (*imt_ref == unimplemented_method) {
+ *imt_ref = method;
+ } else if (*imt_ref != imt_conflict_method) {
+ *imt_ref = imt_conflict_method;
+ }
+ }
+ }
+ }
+}
+
+// TODO This method needs to be split up into several smaller methods.
bool ClassLinker::LinkInterfaceMethods(
Thread* self,
Handle<mirror::Class> klass,
@@ -5348,7 +5395,19 @@
ArtMethod** out_imt) {
StackHandleScope<3> hs(self);
Runtime* const runtime = Runtime::Current();
+
+ const bool is_interface = klass->IsInterface();
+ // TODO It might in the future prove useful to make interfaces have full iftables, allowing a
+ // faster invoke-super implementation in the interpreter/across dex-files.
+ // We will just skip doing any of this on non-debug builds for speed.
+ if (is_interface &&
+ !kIsDebugBuild &&
+ !runtime->AreExperimentalFlagsEnabled(ExperimentalFlags::kDefaultMethods)) {
+ return true;
+ }
+
const bool has_superclass = klass->HasSuperClass();
+ const bool fill_tables = !is_interface;
const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U;
const size_t method_alignment = ArtMethod::Alignment(image_pointer_size_);
const size_t method_size = ArtMethod::Size(image_pointer_size_);
@@ -5356,10 +5415,6 @@
MutableHandle<mirror::IfTable> iftable(hs.NewHandle(klass->GetIfTable()));
- // If we're an interface, we don't need the vtable pointers, so we're done.
- if (klass->IsInterface()) {
- return true;
- }
// These are allocated on the heap to begin, we then transfer to linear alloc when we re-create
// the virtual methods array.
// Need to use low 4GB arenas for compiler or else the pointers wont fit in 32 bit method array
@@ -5376,71 +5431,42 @@
ArtMethod* const unimplemented_method = runtime->GetImtUnimplementedMethod();
ArtMethod* const imt_conflict_method = runtime->GetImtConflictMethod();
// Copy the IMT from the super class if possible.
- bool extend_super_iftable = false;
- if (has_superclass) {
- mirror::Class* super_class = klass->GetSuperClass();
- extend_super_iftable = true;
- if (super_class->ShouldHaveEmbeddedImtAndVTable()) {
- for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
- out_imt[i] = super_class->GetEmbeddedImTableEntry(i, image_pointer_size_);
- }
- } else {
- // No imt in the super class, need to reconstruct from the iftable.
- mirror::IfTable* if_table = super_class->GetIfTable();
- const size_t length = super_class->GetIfTableCount();
- for (size_t i = 0; i < length; ++i) {
- mirror::Class* interface = iftable->GetInterface(i);
- const size_t num_virtuals = interface->NumVirtualMethods();
- const size_t method_array_count = if_table->GetMethodArrayCount(i);
- DCHECK_EQ(num_virtuals, method_array_count);
- if (method_array_count == 0) {
- continue;
- }
- auto* method_array = if_table->GetMethodArray(i);
- for (size_t j = 0; j < num_virtuals; ++j) {
- auto method = method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_);
- DCHECK(method != nullptr) << PrettyClass(super_class);
- // Miranda methods cannot be used to implement an interface method and defaults should be
- // skipped in case we override it.
- if (method->IsDefault() || method->IsMiranda()) {
- continue;
- }
- ArtMethod* interface_method = interface->GetVirtualMethod(j, image_pointer_size_);
- uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
- auto** imt_ref = &out_imt[imt_index];
- if (*imt_ref == unimplemented_method) {
- *imt_ref = method;
- } else if (*imt_ref != imt_conflict_method) {
- *imt_ref = imt_conflict_method;
- }
- }
- }
- }
+ const bool extend_super_iftable = has_superclass;
+ if (has_superclass && fill_tables) {
+ FillImtFromSuperClass(klass,
+ iftable,
+ unimplemented_method,
+ imt_conflict_method,
+ out_imt,
+ image_pointer_size_);
}
+
// Allocate method arrays before since we don't want miss visiting miranda method roots due to
// thread suspension.
- for (size_t i = 0; i < ifcount; ++i) {
- size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods();
- if (num_methods > 0) {
- const bool is_super = i < super_ifcount;
- // This is an interface implemented by a super-class. Therefore we can just copy the method
- // array from the superclass.
- const bool super_interface = is_super && extend_super_iftable;
- mirror::PointerArray* method_array;
- if (super_interface) {
- mirror::IfTable* if_table = klass->GetSuperClass()->GetIfTable();
- DCHECK(if_table != nullptr);
- DCHECK(if_table->GetMethodArray(i) != nullptr);
- // If we are working on a super interface, try extending the existing method array.
- method_array = down_cast<mirror::PointerArray*>(if_table->GetMethodArray(i)->Clone(self));
- } else {
- method_array = AllocPointerArray(self, num_methods);
+ if (fill_tables) {
+ for (size_t i = 0; i < ifcount; ++i) {
+ size_t num_methods = iftable->GetInterface(i)->NumDeclaredVirtualMethods();
+ if (num_methods > 0) {
+ const bool is_super = i < super_ifcount;
+ // This is an interface implemented by a super-class. Therefore we can just copy the method
+ // array from the superclass.
+ const bool super_interface = is_super && extend_super_iftable;
+ mirror::PointerArray* method_array;
+ if (super_interface) {
+ mirror::IfTable* if_table = klass->GetSuperClass()->GetIfTable();
+ DCHECK(if_table != nullptr);
+ DCHECK(if_table->GetMethodArray(i) != nullptr);
+ // If we are working on a super interface, try extending the existing method array.
+ method_array = down_cast<mirror::PointerArray*>(if_table->GetMethodArray(i)->Clone(self));
+ } else {
+ method_array = AllocPointerArray(self, num_methods);
+ }
+ if (UNLIKELY(method_array == nullptr)) {
+ self->AssertPendingOOMException();
+ return false;
+ }
+ iftable->SetMethodArray(i, method_array);
}
- if (UNLIKELY(method_array == nullptr)) {
- self->AssertPendingOOMException();
- return false;
- }
- iftable->SetMethodArray(i, method_array);
}
}
@@ -5456,12 +5482,16 @@
DCHECK_GE(i, 0u);
DCHECK_LT(i, ifcount);
- size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods();
+ size_t num_methods = iftable->GetInterface(i)->NumDeclaredVirtualMethods();
if (num_methods > 0) {
StackHandleScope<2> hs2(self);
const bool is_super = i < super_ifcount;
const bool super_interface = is_super && extend_super_iftable;
- auto method_array(hs2.NewHandle(iftable->GetMethodArray(i)));
+ // We don't actually create or fill these tables for interfaces, we just copy some methods for
+ // conflict methods. Just set this as nullptr in those cases.
+ Handle<mirror::PointerArray> method_array(fill_tables
+ ? hs2.NewHandle(iftable->GetMethodArray(i))
+ : hs2.NewHandle<mirror::PointerArray>(nullptr));
ArraySlice<ArtMethod> input_virtual_methods;
Handle<mirror::PointerArray> input_vtable_array = NullHandle<mirror::PointerArray>();
@@ -5473,7 +5503,7 @@
// at the super-classes iftable methods (copied into method_array previously) when we are
// looking for the implementation of a super-interface method but that is rather dirty.
bool using_virtuals;
- if (super_interface) {
+ if (super_interface || is_interface) {
// If we are overwriting a super class interface, try to only virtual methods instead of the
// whole vtable.
using_virtuals = true;
@@ -5483,6 +5513,7 @@
// For a new interface, however, we need the whole vtable in case a new
// interface method is implemented in the whole superclass.
using_virtuals = false;
+ DCHECK(vtable.Get() != nullptr);
input_vtable_array = vtable;
input_array_length = input_vtable_array->GetLength();
}
@@ -5532,13 +5563,15 @@
break;
} else {
found_impl = true;
- method_array->SetElementPtrSize(j, vtable_method, image_pointer_size_);
- // Place method in imt if entry is empty, place conflict otherwise.
- SetIMTRef(unimplemented_method,
- imt_conflict_method,
- image_pointer_size_,
- vtable_method,
- /*out*/imt_ptr);
+ if (LIKELY(fill_tables)) {
+ method_array->SetElementPtrSize(j, vtable_method, image_pointer_size_);
+ // Place method in imt if entry is empty, place conflict otherwise.
+ SetIMTRef(unimplemented_method,
+ imt_conflict_method,
+ image_pointer_size_,
+ vtable_method,
+ /*out*/imt_ptr);
+ }
break;
}
}
@@ -5565,7 +5598,7 @@
method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_);
DCHECK(supers_method != nullptr);
DCHECK(interface_name_comparator.HasSameNameAndSignature(supers_method));
- if (!supers_method->IsOverridableByDefaultMethod()) {
+ if (LIKELY(!supers_method->IsOverridableByDefaultMethod())) {
// The method is not overridable by a default method (i.e. it is directly implemented
// in some class). Therefore move onto the next interface method.
continue;
@@ -5587,11 +5620,13 @@
} else {
// See if we already have a conflict method for this method.
ArtMethod* preexisting_conflict = FindSameNameAndSignature(interface_name_comparator,
- default_conflict_methods);
+ default_conflict_methods);
if (LIKELY(preexisting_conflict != nullptr)) {
// We already have another conflict we can reuse.
default_conflict_method = preexisting_conflict;
} else {
+ // Note that we do this even if we are an interface since we need to create this and
+ // cannot reuse another classes.
// Create a new conflict method for this to use.
default_conflict_method =
reinterpret_cast<ArtMethod*>(allocator.Alloc(method_size));
@@ -5601,7 +5636,7 @@
}
current_method = default_conflict_method;
break;
- }
+ } // case kDefaultConflict
case DefaultMethodSearchResult::kDefaultFound: {
DCHECK(current_method != nullptr);
// Found a default method.
@@ -5610,8 +5645,12 @@
// We found a default method but it was the same one we already have from our
// superclass. Don't bother adding it to our vtable again.
current_method = vtable_impl;
- } else {
+ } else if (LIKELY(fill_tables)) {
+ // Interfaces don't need to copy default methods since they don't have vtables.
// Only record this default method if it is new to save space.
+ // TODO It might be worthwhile to copy default methods on interfaces anyway since it
+ // would make lookup for interface super much faster. (We would only need to scan
+ // the iftable to find if there is a NSME or AME.)
ArtMethod* old = FindSameNameAndSignature(interface_name_comparator, default_methods);
if (old == nullptr) {
// We found a default method implementation and there were no conflicts.
@@ -5622,7 +5661,7 @@
}
}
break;
- }
+ } // case kDefaultFound
case DefaultMethodSearchResult::kAbstractFound: {
DCHECK(current_method == nullptr);
// Abstract method masks all defaults.
@@ -5634,38 +5673,46 @@
current_method = vtable_impl;
}
break;
+ } // case kAbstractFound
+ }
+ if (LIKELY(fill_tables)) {
+ if (current_method != nullptr) {
+ // We found a default method implementation. Record it in the iftable and IMT.
+ method_array->SetElementPtrSize(j, current_method, image_pointer_size_);
+ SetIMTRef(unimplemented_method,
+ imt_conflict_method,
+ image_pointer_size_,
+ current_method,
+ /*out*/imt_ptr);
+ } else if (!super_interface) {
+ // We could not find an implementation for this method and since it is a brand new
+ // interface we searched the entire vtable (and all default methods) for an
+ // implementation but couldn't find one. We therefore need to make a miranda method.
+ //
+ // Find out if there is already a miranda method we can use.
+ ArtMethod* miranda_method = FindSameNameAndSignature(interface_name_comparator,
+ miranda_methods);
+ if (miranda_method == nullptr) {
+ DCHECK(interface_method->IsAbstract()) << PrettyMethod(interface_method);
+ miranda_method = reinterpret_cast<ArtMethod*>(allocator.Alloc(method_size));
+ CHECK(miranda_method != nullptr);
+ // Point the interface table at a phantom slot.
+ new(miranda_method) ArtMethod(interface_method, image_pointer_size_);
+ miranda_methods.push_back(miranda_method);
+ }
+ method_array->SetElementPtrSize(j, miranda_method, image_pointer_size_);
}
}
- if (current_method != nullptr) {
- // We found a default method implementation. Record it in the iftable and IMT.
- method_array->SetElementPtrSize(j, current_method, image_pointer_size_);
- SetIMTRef(unimplemented_method,
- imt_conflict_method,
- image_pointer_size_,
- current_method,
- /*out*/imt_ptr);
- } else if (!super_interface) {
- // We could not find an implementation for this method and since it is a brand new
- // interface we searched the entire vtable (and all default methods) for an implementation
- // but couldn't find one. We therefore need to make a miranda method.
- //
- // Find out if there is already a miranda method we can use.
- ArtMethod* miranda_method = FindSameNameAndSignature(interface_name_comparator,
- miranda_methods);
- if (miranda_method == nullptr) {
- DCHECK(interface_method->IsAbstract()) << PrettyMethod(interface_method);
- miranda_method = reinterpret_cast<ArtMethod*>(allocator.Alloc(method_size));
- CHECK(miranda_method != nullptr);
- // Point the interface table at a phantom slot.
- new(miranda_method) ArtMethod(interface_method, image_pointer_size_);
- miranda_methods.push_back(miranda_method);
- }
- method_array->SetElementPtrSize(j, miranda_method, image_pointer_size_);
- }
- }
- }
- }
- if (!miranda_methods.empty() || !default_methods.empty() || !default_conflict_methods.empty()) {
+ } // For each method in interface end.
+ } // if (num_methods > 0)
+ } // For each interface.
+ const bool has_new_virtuals = !(miranda_methods.empty() &&
+ default_methods.empty() &&
+ default_conflict_methods.empty());
+ // TODO don't extend virtuals of interface unless necessary (when is it?).
+ if (has_new_virtuals) {
+ DCHECK(!is_interface || (default_methods.empty() && miranda_methods.empty()))
+ << "Interfaces should only have default-conflict methods appended to them.";
VLOG(class_linker) << PrettyClass(klass.Get()) << ": miranda_methods=" << miranda_methods.size()
<< " default_methods=" << default_methods.size()
<< " default_conflict_methods=" << default_conflict_methods.size();
@@ -5762,101 +5809,102 @@
// suspension assert.
self->EndAssertNoThreadSuspension(old_cause);
- const size_t old_vtable_count = vtable->GetLength();
- const size_t new_vtable_count = old_vtable_count +
- miranda_methods.size() +
- default_methods.size() +
- default_conflict_methods.size();
- miranda_methods.clear();
- vtable.Assign(down_cast<mirror::PointerArray*>(vtable->CopyOf(self, new_vtable_count)));
- if (UNLIKELY(vtable.Get() == nullptr)) {
- self->AssertPendingOOMException();
- return false;
- }
- out = methods->begin(method_size, method_alignment) + old_method_count;
- size_t vtable_pos = old_vtable_count;
- for (size_t i = old_method_count; i < new_method_count; ++i) {
- // Leave the declaring class alone as type indices are relative to it
- out->SetMethodIndex(0xFFFF & vtable_pos);
- vtable->SetElementPtrSize(vtable_pos, &*out, image_pointer_size_);
- ++out;
- ++vtable_pos;
- }
- CHECK_EQ(vtable_pos, new_vtable_count);
- // 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, image_pointer_size_);
- // Try and find what we need to change this method to.
- auto translation_it = default_translations.find(i);
- bool found_translation = false;
- if (translation_it != default_translations.end()) {
- if (translation_it->second.IsInConflict()) {
- // Find which conflict method we are to use for this method.
- MethodNameAndSignatureComparator old_method_comparator(
- translated_method->GetInterfaceMethodIfProxy(image_pointer_size_));
- ArtMethod* new_conflict_method = FindSameNameAndSignature(old_method_comparator,
- default_conflict_methods);
- CHECK(new_conflict_method != nullptr) << "Expected a conflict method!";
- translated_method = new_conflict_method;
- } else if (translation_it->second.IsAbstract()) {
- // Find which miranda method we are to use for this method.
- MethodNameAndSignatureComparator old_method_comparator(
- translated_method->GetInterfaceMethodIfProxy(image_pointer_size_));
- ArtMethod* miranda_method = FindSameNameAndSignature(old_method_comparator,
- miranda_methods);
- DCHECK(miranda_method != nullptr);
- translated_method = miranda_method;
- } else {
- // Normal default method (changed from an older default or abstract interface method).
- DCHECK(translation_it->second.IsTranslation());
- translated_method = translation_it->second.GetTranslation();
+ if (fill_tables) {
+ // Update the vtable to the new method structures. We can skip this for interfaces since they
+ // do not have vtables.
+ const size_t old_vtable_count = vtable->GetLength();
+ const size_t new_vtable_count = old_vtable_count +
+ miranda_methods.size() +
+ default_methods.size() +
+ default_conflict_methods.size();
+ vtable.Assign(down_cast<mirror::PointerArray*>(vtable->CopyOf(self, new_vtable_count)));
+ if (UNLIKELY(vtable.Get() == nullptr)) {
+ self->AssertPendingOOMException();
+ return false;
+ }
+ out = methods->begin(method_size, method_alignment) + old_method_count;
+ size_t vtable_pos = old_vtable_count;
+ // Update all the newly copied method's indexes so they denote their placement in the vtable.
+ for (size_t i = old_method_count; i < new_method_count; ++i) {
+ // Leave the declaring class alone the method's dex_code_item_offset_ and dex_method_index_
+ // fields are references into the dex file the method was defined in. Since the ArtMethod
+ // does not store that information it uses declaring_class_->dex_cache_.
+ out->SetMethodIndex(0xFFFF & vtable_pos);
+ vtable->SetElementPtrSize(vtable_pos, &*out, image_pointer_size_);
+ ++out;
+ ++vtable_pos;
+ }
+ CHECK_EQ(vtable_pos, new_vtable_count);
+ // 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, image_pointer_size_);
+ // Try and find what we need to change this method to.
+ auto translation_it = default_translations.find(i);
+ bool found_translation = false;
+ if (translation_it != default_translations.end()) {
+ if (translation_it->second.IsInConflict()) {
+ // Find which conflict method we are to use for this method.
+ MethodNameAndSignatureComparator old_method_comparator(
+ translated_method->GetInterfaceMethodIfProxy(image_pointer_size_));
+ ArtMethod* new_conflict_method = FindSameNameAndSignature(old_method_comparator,
+ default_conflict_methods);
+ CHECK(new_conflict_method != nullptr) << "Expected a conflict method!";
+ translated_method = new_conflict_method;
+ } else if (translation_it->second.IsAbstract()) {
+ // Find which miranda method we are to use for this method.
+ MethodNameAndSignatureComparator old_method_comparator(
+ translated_method->GetInterfaceMethodIfProxy(image_pointer_size_));
+ ArtMethod* miranda_method = FindSameNameAndSignature(old_method_comparator,
+ miranda_methods);
+ DCHECK(miranda_method != nullptr);
+ translated_method = miranda_method;
+ } else {
+ // Normal default method (changed from an older default or abstract interface method).
+ DCHECK(translation_it->second.IsTranslation());
+ translated_method = translation_it->second.GetTranslation();
+ }
+ found_translation = true;
}
- found_translation = true;
- }
- DCHECK(translated_method != nullptr);
- auto it = move_table.find(translated_method);
- if (it != move_table.end()) {
- auto* new_method = it->second;
- DCHECK(new_method != nullptr);
- vtable->SetElementPtrSize(i, new_method, image_pointer_size_);
- } else {
- // If it was not going to be updated we wouldn't have put it into the default_translations
- // map.
- CHECK(!found_translation) << "We were asked to update this vtable entry. Must not fail.";
- }
- }
-
- if (kIsDebugBuild) {
- for (size_t i = 0; i < new_vtable_count; ++i) {
- CHECK(move_table.find(vtable->GetElementPtrSize<ArtMethod*>(i, image_pointer_size_)) ==
- move_table.end());
- }
- }
-
- klass->SetVTable(vtable.Get());
- // Go fix up all the stale (old miranda or default method) pointers.
- // First do it on the iftable.
- for (size_t i = 0; i < ifcount; ++i) {
- for (size_t j = 0, count = iftable->GetMethodArrayCount(i); j < count; ++j) {
- auto* method_array = iftable->GetMethodArray(i);
- auto* m = method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_);
- DCHECK(m != nullptr) << PrettyClass(klass.Get());
- auto it = move_table.find(m);
+ DCHECK(translated_method != nullptr);
+ auto it = move_table.find(translated_method);
if (it != move_table.end()) {
- auto* new_m = it->second;
- DCHECK(new_m != nullptr) << PrettyClass(klass.Get());
- method_array->SetElementPtrSize(j, new_m, image_pointer_size_);
+ auto* new_method = it->second;
+ DCHECK(new_method != nullptr);
+ vtable->SetElementPtrSize(i, new_method, image_pointer_size_);
+ } else {
+ // If it was not going to be updated we wouldn't have put it into the default_translations
+ // map.
+ CHECK(!found_translation) << "We were asked to update this vtable entry. Must not fail.";
+ }
+ }
+ klass->SetVTable(vtable.Get());
+
+ // Go fix up all the stale iftable pointers.
+ for (size_t i = 0; i < ifcount; ++i) {
+ for (size_t j = 0, count = iftable->GetMethodArrayCount(i); j < count; ++j) {
+ auto* method_array = iftable->GetMethodArray(i);
+ auto* m = method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_);
+ DCHECK(m != nullptr) << PrettyClass(klass.Get());
+ auto it = move_table.find(m);
+ if (it != move_table.end()) {
+ auto* new_m = it->second;
+ DCHECK(new_m != nullptr) << PrettyClass(klass.Get());
+ method_array->SetElementPtrSize(j, new_m, image_pointer_size_);
+ }
+ }
+ }
+
+ // Fix up IMT next
+ for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
+ auto it = move_table.find(out_imt[i]);
+ if (it != move_table.end()) {
+ out_imt[i] = it->second;
}
}
}
- // Fix up IMT next
- for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
- auto it = move_table.find(out_imt[i]);
- if (it != move_table.end()) {
- out_imt[i] = it->second;
- }
- }
+
// Check that there are no stale methods are in the dex cache array.
if (kIsDebugBuild) {
auto* resolved_methods = klass->GetDexCache()->GetResolvedMethods();
@@ -5880,7 +5928,7 @@
} else {
self->EndAssertNoThreadSuspension(old_cause);
}
- if (kIsDebugBuild) {
+ if (kIsDebugBuild && !is_interface) {
SanityCheckVTable(klass, image_pointer_size_);
}
return true;
@@ -6270,7 +6318,13 @@
DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface());
}
break;
- case kSuper: // Fall-through.
+ case kSuper:
+ if (klass->IsInterface()) {
+ resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_);
+ } else {
+ resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx, image_pointer_size_);
+ }
+ break;
case kVirtual:
resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx, image_pointer_size_);
break;
@@ -6292,7 +6346,13 @@
resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_);
DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface());
break;
- case kSuper: // Fall-through.
+ case kSuper:
+ if (klass->IsInterface()) {
+ resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_);
+ } else {
+ resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_);
+ }
+ break;
case kVirtual:
resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_);
break;