in runtime/class_linker.cc [6695:7298]
bool ClassLinker::LinkInterfaceMethods(
Thread* self,
Handle<mirror::Class> klass,
const std::unordered_map<size_t, ClassLinker::MethodTranslation>& default_translations,
bool* out_new_conflict,
ArtMethod** out_imt) {
StackHandleScope<3> hs(self);
Runtime* const runtime = Runtime::Current();
const bool is_interface = klass->IsInterface();
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_);
const size_t ifcount = klass->GetIfTableCount();
MutableHandle<mirror::IfTable> iftable(hs.NewHandle(klass->GetIfTable()));
// 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
// during cross compilation.
// Use the linear alloc pool since this one is in the low 4gb for the compiler.
ArenaStack stack(runtime->GetLinearAlloc()->GetArenaPool());
ScopedArenaAllocator allocator(&stack);
ScopedArenaVector<ArtMethod*> default_conflict_methods(allocator.Adapter());
ScopedArenaVector<ArtMethod*> overriding_default_conflict_methods(allocator.Adapter());
ScopedArenaVector<ArtMethod*> miranda_methods(allocator.Adapter());
ScopedArenaVector<ArtMethod*> default_methods(allocator.Adapter());
ScopedArenaVector<ArtMethod*> overriding_default_methods(allocator.Adapter());
MutableHandle<mirror::PointerArray> vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
ArtMethod* const unimplemented_method = runtime->GetImtUnimplementedMethod();
ArtMethod* const imt_conflict_method = runtime->GetImtConflictMethod();
// Copy the IMT from the super class if possible.
const bool extend_super_iftable = has_superclass;
if (has_superclass && fill_tables) {
FillImtFromSuperClass(klass,
unimplemented_method,
imt_conflict_method,
out_new_conflict,
out_imt);
}
// Allocate method arrays before since we don't want miss visiting miranda method roots due to
// thread suspension.
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);
}
}
}
auto* old_cause = self->StartAssertNoThreadSuspension(
"Copying ArtMethods for LinkInterfaceMethods");
// Going in reverse to ensure that we will hit abstract methods that override defaults before the
// defaults. This means we don't need to do any trickery when creating the Miranda methods, since
// they will already be null. This has the additional benefit that the declarer of a miranda
// method will actually declare an abstract method.
for (size_t i = ifcount; i != 0; ) {
--i;
DCHECK_GE(i, 0u);
DCHECK_LT(i, ifcount);
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;
// 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;
ScopedNullHandle<mirror::PointerArray> null_handle;
Handle<mirror::PointerArray> input_vtable_array(null_handle);
int32_t input_array_length = 0;
// TODO Cleanup Needed: In the presence of default methods this optimization is rather dirty
// and confusing. Default methods should always look through all the superclasses
// because they are the last choice of an implementation. We get around this by looking
// 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 || is_interface) {
// If we are overwriting a super class interface, try to only virtual methods instead of the
// whole vtable.
using_virtuals = true;
input_virtual_methods = klass->GetDeclaredMethodsSlice(image_pointer_size_);
input_array_length = input_virtual_methods.size();
} else {
// 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();
}
// For each method in interface
for (size_t j = 0; j < num_methods; ++j) {
auto* interface_method = iftable->GetInterface(i)->GetVirtualMethod(j, image_pointer_size_);
MethodNameAndSignatureComparator interface_name_comparator(
interface_method->GetInterfaceMethodIfProxy(image_pointer_size_));
uint32_t imt_index = GetIMTIndex(interface_method);
ArtMethod** imt_ptr = &out_imt[imt_index];
// For each method listed in the interface's method list, find the
// matching method in our class's method list. We want to favor the
// subclass over the superclass, which just requires walking
// back from the end of the vtable. (This only matters if the
// superclass defines a private method and this class redefines
// it -- otherwise it would use the same vtable slot. In .dex files
// those don't end up in the virtual method table, so it shouldn't
// matter which direction we go. We walk it backward anyway.)
//
// To find defaults we need to do the same but also go over interfaces.
bool found_impl = false;
ArtMethod* vtable_impl = nullptr;
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, image_pointer_size_);
ArtMethod* vtable_method_for_name_comparison =
vtable_method->GetInterfaceMethodIfProxy(image_pointer_size_);
if (interface_name_comparator.HasSameNameAndSignature(
vtable_method_for_name_comparison)) {
if (!vtable_method->IsAbstract() && !vtable_method->IsPublic()) {
// Must do EndAssertNoThreadSuspension before throw since the throw can cause
// allocations.
self->EndAssertNoThreadSuspension(old_cause);
ThrowIllegalAccessError(klass.Get(),
"Method '%s' implementing interface method '%s' is not public",
PrettyMethod(vtable_method).c_str(), PrettyMethod(interface_method).c_str());
return false;
} else if (UNLIKELY(vtable_method->IsOverridableByDefaultMethod())) {
// We might have a newer, better, default method for this, so we just skip it. If we
// are still using this we will select it again when scanning for default methods. To
// obviate the need to copy the method again we will make a note that we already found
// a default here.
// TODO This should be much cleaner.
vtable_impl = vtable_method;
break;
} else {
found_impl = true;
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,
vtable_method,
/*out*/out_new_conflict,
/*out*/imt_ptr);
}
break;
}
}
}
// Continue on to the next method if we are done.
if (LIKELY(found_impl)) {
continue;
} else if (LIKELY(super_interface)) {
// Don't look for a default implementation when the super-method is implemented directly
// by the class.
//
// See if we can use the superclasses method and skip searching everything else.
// Note: !found_impl && super_interface
CHECK(extend_super_iftable);
// If this is a super_interface method it is possible we shouldn't override it because a
// superclass could have implemented it directly. We get the method the superclass used
// to implement this to know if we can override it with a default method. Doing this is
// safe since we know that the super_iftable is filled in so we can simply pull it from
// there. We don't bother if this is not a super-classes interface since in that case we
// have scanned the entire vtable anyway and would have found it.
// 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, image_pointer_size_);
DCHECK(supers_method != nullptr);
DCHECK(interface_name_comparator.HasSameNameAndSignature(supers_method));
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;
} else {
// If the super-classes method is override-able by a default method we need to keep
// track of it since though it is override-able it is not guaranteed to be 'overridden'.
// If it turns out not to be overridden and we did not keep track of it we might add it
// to the vtable twice, causing corruption in this class and possibly any subclasses.
DCHECK(vtable_impl == nullptr || vtable_impl == supers_method)
<< "vtable_impl was " << PrettyMethod(vtable_impl) << " and not 'nullptr' or "
<< PrettyMethod(supers_method) << " as expected. IFTable appears to be corrupt!";
vtable_impl = supers_method;
}
}
// If we haven't found it yet we should search through the interfaces for default methods.
ArtMethod* current_method = nullptr;
switch (FindDefaultMethodImplementation(self,
interface_method,
klass,
/*out*/¤t_method)) {
case DefaultMethodSearchResult::kDefaultConflict: {
// Default method conflict.
DCHECK(current_method == nullptr);
ArtMethod* default_conflict_method = nullptr;
if (vtable_impl != nullptr && vtable_impl->IsDefaultConflicting()) {
// We can reuse the method from the superclass, don't bother adding it to virtuals.
default_conflict_method = vtable_impl;
} else {
// See if we already have a conflict method for this method.
ArtMethod* preexisting_conflict = FindSameNameAndSignature(
interface_name_comparator,
default_conflict_methods,
overriding_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));
new(default_conflict_method) ArtMethod(interface_method, image_pointer_size_);
if (vtable_impl == nullptr) {
// Save the conflict method. We need to add it to the vtable.
default_conflict_methods.push_back(default_conflict_method);
} else {
// Save the conflict method but it is already in the vtable.
overriding_default_conflict_methods.push_back(default_conflict_method);
}
}
}
current_method = default_conflict_method;
break;
} // case kDefaultConflict
case DefaultMethodSearchResult::kDefaultFound: {
DCHECK(current_method != nullptr);
// Found a default method.
if (vtable_impl != nullptr &&
current_method->GetDeclaringClass() == vtable_impl->GetDeclaringClass()) {
// 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 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,
overriding_default_methods);
if (old == nullptr) {
// We found a default method implementation and there were no conflicts.
if (vtable_impl == nullptr) {
// Save the default method. We need to add it to the vtable.
default_methods.push_back(current_method);
} else {
// Save the default method but it is already in the vtable.
overriding_default_methods.push_back(current_method);
}
} else {
CHECK(old == current_method) << "Multiple default implementations selected!";
}
}
break;
} // case kDefaultFound
case DefaultMethodSearchResult::kAbstractFound: {
DCHECK(current_method == nullptr);
// Abstract method masks all defaults.
if (vtable_impl != nullptr &&
vtable_impl->IsAbstract() &&
!vtable_impl->IsDefaultConflicting()) {
// We need to make this an abstract method but the version in the vtable already is so
// don't do anything.
current_method = vtable_impl;
}
break;
} // case kAbstractFound
}
if (LIKELY(fill_tables)) {
if (current_method == nullptr && !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);
}
current_method = miranda_method;
}
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,
current_method,
/*out*/out_new_conflict,
/*out*/imt_ptr);
}
}
} // For each method in interface end.
} // if (num_methods > 0)
} // For each interface.
const bool has_new_virtuals = !(miranda_methods.empty() &&
default_methods.empty() &&
overriding_default_methods.empty() &&
overriding_default_conflict_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()
<< " overriding_default_methods=" << overriding_default_methods.size()
<< " default_conflict_methods=" << default_conflict_methods.size()
<< " overriding_default_conflict_methods="
<< overriding_default_conflict_methods.size();
const size_t old_method_count = klass->NumMethods();
const size_t new_method_count = old_method_count +
miranda_methods.size() +
default_methods.size() +
overriding_default_conflict_methods.size() +
overriding_default_methods.size() +
default_conflict_methods.size();
// Attempt to realloc to save RAM if possible.
LengthPrefixedArray<ArtMethod>* old_methods = klass->GetMethodsPtr();
// The Realloced virtual methods aren't visible from the class roots, so there is no issue
// where GCs could attempt to mark stale pointers due to memcpy. And since we overwrite the
// realloced memory with out->CopyFrom, we are guaranteed to have objects in the to space since
// CopyFrom has internal read barriers.
//
// TODO We should maybe move some of this into mirror::Class or at least into another method.
const size_t old_size = LengthPrefixedArray<ArtMethod>::ComputeSize(old_method_count,
method_size,
method_alignment);
const size_t new_size = LengthPrefixedArray<ArtMethod>::ComputeSize(new_method_count,
method_size,
method_alignment);
const size_t old_methods_ptr_size = (old_methods != nullptr) ? old_size : 0;
auto* methods = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(
runtime->GetLinearAlloc()->Realloc(self, old_methods, old_methods_ptr_size, new_size));
if (UNLIKELY(methods == nullptr)) {
self->AssertPendingOOMException();
self->EndAssertNoThreadSuspension(old_cause);
return false;
}
ScopedArenaUnorderedMap<ArtMethod*, ArtMethod*> move_table(allocator.Adapter());
if (methods != old_methods) {
// Maps from heap allocated miranda method to linear alloc miranda method.
StrideIterator<ArtMethod> out = methods->begin(method_size, method_alignment);
// Copy over the old methods.
for (auto& m : klass->GetMethods(image_pointer_size_)) {
move_table.emplace(&m, &*out);
// The CopyFrom is only necessary to not miss read barriers since Realloc won't do read
// barriers when it copies.
out->CopyFrom(&m, image_pointer_size_);
++out;
}
}
StrideIterator<ArtMethod> out(methods->begin(method_size, method_alignment) + old_method_count);
// Copy over miranda methods before copying vtable since CopyOf may cause thread suspension and
// we want the roots of the miranda methods to get visited.
for (ArtMethod* mir_method : miranda_methods) {
ArtMethod& new_method = *out;
new_method.CopyFrom(mir_method, image_pointer_size_);
new_method.SetAccessFlags(new_method.GetAccessFlags() | kAccMiranda | kAccCopied);
DCHECK_NE(new_method.GetAccessFlags() & kAccAbstract, 0u)
<< "Miranda method should be abstract!";
move_table.emplace(mir_method, &new_method);
++out;
}
// We need to copy the default methods into our own method table since the runtime requires that
// every method on a class's vtable be in that respective class's virtual method table.
// NOTE This means that two classes might have the same implementation of a method from the same
// interface but will have different ArtMethod*s for them. This also means we cannot compare a
// default method found on a class with one found on the declaring interface directly and must
// look at the declaring class to determine if they are the same.
for (const ScopedArenaVector<ArtMethod*>& methods_vec : {default_methods,
overriding_default_methods}) {
for (ArtMethod* def_method : methods_vec) {
ArtMethod& new_method = *out;
new_method.CopyFrom(def_method, image_pointer_size_);
// Clear the kAccSkipAccessChecks flag if it is present. Since this class hasn't been
// verified yet it shouldn't have methods that are skipping access checks.
// TODO This is rather arbitrary. We should maybe support classes where only some of its
// methods are skip_access_checks.
constexpr uint32_t kSetFlags = kAccDefault | kAccCopied;
constexpr uint32_t kMaskFlags = ~kAccSkipAccessChecks;
new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags);
move_table.emplace(def_method, &new_method);
++out;
}
}
for (const ScopedArenaVector<ArtMethod*>& methods_vec : {default_conflict_methods,
overriding_default_conflict_methods}) {
for (ArtMethod* conf_method : methods_vec) {
ArtMethod& new_method = *out;
new_method.CopyFrom(conf_method, image_pointer_size_);
// This is a type of default method (there are default method impls, just a conflict) so
// mark this as a default, non-abstract method, since thats what it is. Also clear the
// kAccSkipAccessChecks bit since this class hasn't been verified yet it shouldn't have
// methods that are skipping access checks.
constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict | kAccCopied;
constexpr uint32_t kMaskFlags = ~(kAccAbstract | kAccSkipAccessChecks);
new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags);
DCHECK(new_method.IsDefaultConflicting());
// The actual method might or might not be marked abstract since we just copied it from a
// (possibly default) interface method. We need to set it entry point to be the bridge so
// that the compiler will not invoke the implementation of whatever method we copied from.
EnsureThrowsInvocationError(&new_method);
move_table.emplace(conf_method, &new_method);
++out;
}
}
methods->SetSize(new_method_count);
UpdateClassMethods(klass.Get(), methods);
// Done copying methods, they are all roots in the class now, so we can end the no thread
// suspension assert.
self->EndAssertNoThreadSuspension(old_cause);
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;
}
size_t vtable_pos = old_vtable_count;
// Update all the newly copied method's indexes so they denote their placement in the vtable.
for (const ScopedArenaVector<ArtMethod*>& methods_vec : {default_methods,
default_conflict_methods,
miranda_methods}) {
// These are the functions that are not already in the vtable!
for (ArtMethod* new_method : methods_vec) {
auto translated_method_it = move_table.find(new_method);
CHECK(translated_method_it != move_table.end())
<< "We must have a translation for methods added to the classes methods_ array! We "
<< "could not find the ArtMethod added for " << PrettyMethod(new_method);
ArtMethod* new_vtable_method = translated_method_it->second;
// 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_.
new_vtable_method->SetMethodIndex(0xFFFF & vtable_pos);
vtable->SetElementPtrSize(vtable_pos, new_vtable_method, image_pointer_size_);
++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_));
// We only need to look through overriding_default_conflict_methods since this is an
// overridden method we are fixing up here.
ArtMethod* new_conflict_method = FindSameNameAndSignature(
old_method_comparator, overriding_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;
}
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);
// Make sure the new_methods index is set.
if (new_method->GetMethodIndexDuringLinking() != i) {
DCHECK_LE(reinterpret_cast<uintptr_t>(&*methods->begin(method_size, method_alignment)),
reinterpret_cast<uintptr_t>(new_method));
DCHECK_LT(reinterpret_cast<uintptr_t>(new_method),
reinterpret_cast<uintptr_t>(&*methods->end(method_size, method_alignment)));
new_method->SetMethodIndex(0xFFFF & i);
}
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 < ImTable::kSize; ++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();
for (size_t i = 0, count = klass->GetDexCache()->NumResolvedMethods(); i < count; ++i) {
auto* m = mirror::DexCache::GetElementPtrSize(resolved_methods, i, image_pointer_size_);
CHECK(move_table.find(m) == move_table.end() ||
// The original versions of copied methods will still be present so allow those too.
// Note that if the first check passes this might fail to GetDeclaringClass().
std::find_if(m->GetDeclaringClass()->GetMethods(image_pointer_size_).begin(),
m->GetDeclaringClass()->GetMethods(image_pointer_size_).end(),
[m] (ArtMethod& meth) {
return &meth == m;
}) != m->GetDeclaringClass()->GetMethods(image_pointer_size_).end())
<< "Obsolete methods " << PrettyMethod(m) << " is in dex cache!";
}
}
// Put some random garbage in old methods to help find stale pointers.
if (methods != old_methods && old_methods != nullptr && kIsDebugBuild) {
// Need to make sure the GC is not running since it could be scanning the methods we are
// about to overwrite.
ScopedThreadStateChange tsc(self, kSuspended);
gc::ScopedGCCriticalSection gcs(self,
gc::kGcCauseClassLinker,
gc::kCollectorTypeClassLinker);
memset(old_methods, 0xFEu, old_size);
}
} else {
self->EndAssertNoThreadSuspension(old_cause);
}
if (kIsDebugBuild && !is_interface) {
SanityCheckVTable(self, klass, image_pointer_size_);
}
return true;
}