Refactor ClassLinker::LinkInterfaceMethods().
Add a helper class and split LinkInterfaceMethods() into
several functions to improve readability. Even with this
change, it is still almost 250 lines long.
Fix the method array reallocation to use the appropriate
linear allocator for the class's defining loader. Given that
this is a native reallocation, failure to reallocate would
result in an abort, so CHECK() that we get a non-null array
instead of expecting a null and asserting a pending OOME.
Test: m test-art-host
Change-Id: I8ff8257e4271ae8c975ea4b33fd8af36e1a1f77e
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 5b8d4e4..986e125 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -6090,6 +6090,41 @@
return new_conflict_method;
}
+bool ClassLinker::AllocateIfTableMethodArrays(Thread* self,
+ Handle<mirror::Class> klass,
+ Handle<mirror::IfTable> iftable) {
+ DCHECK(!klass->IsInterface());
+ const bool has_superclass = klass->HasSuperClass();
+ const bool extend_super_iftable = has_superclass;
+ const size_t ifcount = klass->GetIfTableCount();
+ const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U;
+ 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;
+ ObjPtr<mirror::PointerArray> method_array;
+ if (super_interface) {
+ ObjPtr<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);
+ }
+ }
+ return true;
+}
+
void ClassLinker::SetIMTRef(ArtMethod* unimplemented_method,
ArtMethod* imt_conflict_method,
ArtMethod* current_method,
@@ -6629,6 +6664,480 @@
}
}
+class ClassLinker::LinkInterfaceMethodsHelper {
+ public:
+ LinkInterfaceMethodsHelper(ClassLinker* class_linker,
+ Handle<mirror::Class> klass,
+ Thread* self,
+ Runtime* runtime)
+ : class_linker_(class_linker),
+ klass_(klass),
+ method_alignment_(ArtMethod::Alignment(class_linker->GetImagePointerSize())),
+ method_size_(ArtMethod::Size(class_linker->GetImagePointerSize())),
+ self_(self),
+ stack_(runtime->GetLinearAlloc()->GetArenaPool()),
+ allocator_(&stack_),
+ default_conflict_methods_(allocator_.Adapter()),
+ overriding_default_conflict_methods_(allocator_.Adapter()),
+ miranda_methods_(allocator_.Adapter()),
+ default_methods_(allocator_.Adapter()),
+ overriding_default_methods_(allocator_.Adapter()),
+ move_table_(allocator_.Adapter()) {
+ }
+
+ ArtMethod* FindMethod(ArtMethod* interface_method,
+ MethodNameAndSignatureComparator& interface_name_comparator,
+ ArtMethod* vtable_impl)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ ArtMethod* GetOrCreateMirandaMethod(ArtMethod* interface_method,
+ MethodNameAndSignatureComparator& interface_name_comparator)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ bool HasNewVirtuals() const {
+ return !(miranda_methods_.empty() &&
+ default_methods_.empty() &&
+ overriding_default_methods_.empty() &&
+ overriding_default_conflict_methods_.empty() &&
+ default_conflict_methods_.empty());
+ }
+
+ void ReallocMethods() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ ObjPtr<mirror::PointerArray> UpdateVtable(
+ const std::unordered_map<size_t, ClassLinker::MethodTranslation>& default_translations,
+ ObjPtr<mirror::PointerArray> old_vtable) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void UpdateIfTable(Handle<mirror::IfTable> iftable) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void UpdateIMT(ArtMethod** out_imt);
+
+ void CheckNoStaleMethodsInDexCache() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (kIsDebugBuild) {
+ PointerSize pointer_size = class_linker_->GetImagePointerSize();
+ // Check that there are no stale methods are in the dex cache array.
+ 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, 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(pointer_size).begin(),
+ m->GetDeclaringClass()->GetMethods(pointer_size).end(),
+ [m] (ArtMethod& meth) {
+ return &meth == m;
+ }) != m->GetDeclaringClass()->GetMethods(pointer_size).end())
+ << "Obsolete method " << m->PrettyMethod() << " is in dex cache!";
+ }
+ }
+ }
+
+ void ClobberOldMethods(LengthPrefixedArray<ArtMethod>* old_methods,
+ LengthPrefixedArray<ArtMethod>* methods) {
+ if (kIsDebugBuild) {
+ CHECK(methods != nullptr);
+ // Put some random garbage in old methods to help find stale pointers.
+ if (methods != old_methods && old_methods != nullptr) {
+ // 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);
+ const size_t old_size = LengthPrefixedArray<ArtMethod>::ComputeSize(old_methods->size(),
+ method_size_,
+ method_alignment_);
+ memset(old_methods, 0xFEu, old_size);
+ }
+ }
+ }
+
+ private:
+ size_t NumberOfNewVirtuals() const {
+ return miranda_methods_.size() +
+ default_methods_.size() +
+ overriding_default_conflict_methods_.size() +
+ overriding_default_methods_.size() +
+ default_conflict_methods_.size();
+ }
+
+ bool FillTables() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return !klass_->IsInterface();
+ }
+
+ void LogNewVirtuals() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(!klass_->IsInterface() || (default_methods_.empty() && miranda_methods_.empty()))
+ << "Interfaces should only have default-conflict methods appended to them.";
+ VLOG(class_linker) << mirror::Class::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();
+ }
+
+ ClassLinker* class_linker_;
+ Handle<mirror::Class> klass_;
+ size_t method_alignment_;
+ size_t method_size_;
+ Thread* const self_;
+
+ // 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_;
+ ScopedArenaAllocator allocator_;
+
+ ScopedArenaVector<ArtMethod*> default_conflict_methods_;
+ ScopedArenaVector<ArtMethod*> overriding_default_conflict_methods_;
+ ScopedArenaVector<ArtMethod*> miranda_methods_;
+ ScopedArenaVector<ArtMethod*> default_methods_;
+ ScopedArenaVector<ArtMethod*> overriding_default_methods_;
+
+ ScopedArenaUnorderedMap<ArtMethod*, ArtMethod*> move_table_;
+};
+
+ArtMethod* ClassLinker::LinkInterfaceMethodsHelper::FindMethod(
+ ArtMethod* interface_method,
+ MethodNameAndSignatureComparator& interface_name_comparator,
+ ArtMethod* vtable_impl) {
+ ArtMethod* current_method = nullptr;
+ switch (class_linker_->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,
+ class_linker_->GetImagePointerSize());
+ 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(FillTables())) {
+ // 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
+ }
+ return current_method;
+}
+
+ArtMethod* ClassLinker::LinkInterfaceMethodsHelper::GetOrCreateMirandaMethod(
+ ArtMethod* interface_method,
+ MethodNameAndSignatureComparator& interface_name_comparator) {
+ // 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()) << interface_method->PrettyMethod();
+ 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, class_linker_->GetImagePointerSize());
+ miranda_methods_.push_back(miranda_method);
+ }
+ return miranda_method;
+}
+
+void ClassLinker::LinkInterfaceMethodsHelper::ReallocMethods() {
+ LogNewVirtuals();
+
+ const size_t old_method_count = klass_->NumMethods();
+ const size_t new_method_count = old_method_count + NumberOfNewVirtuals();
+ DCHECK_NE(old_method_count, new_method_count);
+
+ // 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>*>(
+ class_linker_->GetAllocatorForClassLoader(klass_->GetClassLoader())->Realloc(
+ self_, old_methods, old_methods_ptr_size, new_size));
+ CHECK(methods != nullptr); // Native allocation failure aborts.
+
+ PointerSize pointer_size = class_linker_->GetImagePointerSize();
+ 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(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, 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, 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, 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, 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(class_linker_, &new_method);
+ move_table_.emplace(conf_method, &new_method);
+ ++out;
+ }
+ }
+ methods->SetSize(new_method_count);
+ class_linker_->UpdateClassMethods(klass_.Get(), methods);
+}
+
+ObjPtr<mirror::PointerArray> ClassLinker::LinkInterfaceMethodsHelper::UpdateVtable(
+ const std::unordered_map<size_t, ClassLinker::MethodTranslation>& default_translations,
+ ObjPtr<mirror::PointerArray> old_vtable) {
+ // 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 = old_vtable->GetLength();
+ const size_t new_vtable_count = old_vtable_count +
+ miranda_methods_.size() +
+ default_methods_.size() +
+ default_conflict_methods_.size();
+
+ ObjPtr<mirror::PointerArray> vtable =
+ down_cast<mirror::PointerArray*>(old_vtable->CopyOf(self_, new_vtable_count));
+ if (UNLIKELY(vtable == nullptr)) {
+ self_->AssertPendingOOMException();
+ return nullptr;
+ }
+
+ size_t vtable_pos = old_vtable_count;
+ PointerSize pointer_size = class_linker_->GetImagePointerSize();
+ // 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 " << ArtMethod::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, pointer_size);
+ ++vtable_pos;
+ }
+ }
+ DCHECK_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, 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(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(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) {
+ if (kIsDebugBuild) {
+ auto* methods = klass_->GetMethodsPtr();
+ CHECK_LE(reinterpret_cast<uintptr_t>(&*methods->begin(method_size_, method_alignment_)),
+ reinterpret_cast<uintptr_t>(new_method));
+ CHECK_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, 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.Ptr());
+ return vtable;
+}
+
+void ClassLinker::LinkInterfaceMethodsHelper::UpdateIfTable(Handle<mirror::IfTable> iftable) {
+ PointerSize pointer_size = class_linker_->GetImagePointerSize();
+ const size_t ifcount = klass_->GetIfTableCount();
+ // 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, pointer_size);
+ DCHECK(m != nullptr) << klass_->PrettyClass();
+ auto it = move_table_.find(m);
+ if (it != move_table_.end()) {
+ auto* new_m = it->second;
+ DCHECK(new_m != nullptr) << klass_->PrettyClass();
+ method_array->SetElementPtrSize(j, new_m, pointer_size);
+ }
+ }
+ }
+}
+
+void ClassLinker::LinkInterfaceMethodsHelper::UpdateIMT(ArtMethod** out_imt) {
+ // 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;
+ }
+ }
+}
+
// TODO This method needs to be split up into several smaller methods.
bool ClassLinker::LinkInterfaceMethods(
Thread* self,
@@ -6643,25 +7152,9 @@
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());
+ Handle<mirror::IfTable> iftable(hs.NewHandle(klass->GetIfTable()));
MutableHandle<mirror::PointerArray> vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
ArtMethod* const unimplemented_method = runtime->GetImtUnimplementedMethod();
@@ -6678,32 +7171,13 @@
// 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;
- ObjPtr<mirror::PointerArray> method_array;
- if (super_interface) {
- ObjPtr<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 (!AllocateIfTableMethodArrays(self, klass, iftable)) {
+ return false;
}
}
+ LinkInterfaceMethodsHelper helper(this, klass, self, runtime);
+
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
@@ -6854,109 +7328,16 @@
}
}
// 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
- }
+ ArtMethod* current_method = helper.FindMethod(interface_method,
+ interface_name_comparator,
+ vtable_impl);
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()) << interface_method->PrettyMethod();
- 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;
+ current_method = helper.GetOrCreateMirandaMethod(interface_method,
+ interface_name_comparator);
}
if (current_method != nullptr) {
@@ -6972,266 +7353,28 @@
} // 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) << mirror::Class::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(this, &new_method);
- move_table.emplace(conf_method, &new_method);
- ++out;
- }
- }
- methods->SetSize(new_method_count);
- UpdateClassMethods(klass.Get(), methods);
+ if (helper.HasNewVirtuals()) {
+ LengthPrefixedArray<ArtMethod>* old_methods = kIsDebugBuild ? klass->GetMethodsPtr() : nullptr;
+ helper.ReallocMethods(); // No return value to check. Native allocation failure aborts.
+ LengthPrefixedArray<ArtMethod>* methods = kIsDebugBuild ? klass->GetMethodsPtr() : nullptr;
+
// 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)));
+ vtable.Assign(helper.UpdateVtable(default_translations, vtable.Get()));
if (UNLIKELY(vtable.Get() == nullptr)) {
- self->AssertPendingOOMException();
+ // The helper has already called 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 " << ArtMethod::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) << klass->PrettyClass();
- auto it = move_table.find(m);
- if (it != move_table.end()) {
- auto* new_m = it->second;
- DCHECK(new_m != nullptr) << klass->PrettyClass();
- 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;
- }
- }
+ helper.UpdateIfTable(iftable);
+ helper.UpdateIMT(out_imt);
}
- // 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 " << m->PrettyMethod() << " 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);
- }
+ helper.CheckNoStaleMethodsInDexCache();
+ helper.ClobberOldMethods(old_methods, methods);
} else {
self->EndAssertNoThreadSuspension(old_cause);
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 9b25303..20fba13 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -652,6 +652,8 @@
};
private:
+ class LinkInterfaceMethodsHelper;
+
struct ClassLoaderData {
jweak weak_root; // Weak root to enable class unloading.
ClassTable* class_table;
@@ -1089,6 +1091,12 @@
REQUIRES(!Locks::dex_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Allocate method arrays for interfaces.
+ bool AllocateIfTableMethodArrays(Thread* self,
+ Handle<mirror::Class> klass,
+ Handle<mirror::IfTable> iftable)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Sets imt_ref appropriately for LinkInterfaceMethods.
// If there is no method in the imt location of imt_ref it will store the given method there.
// Otherwise it will set the conflict method which will figure out which method to use during