Revert "Revert "Refactor ClassLinker::LinkInterfaceMethods().""
Drop the fix for using the correct linear allocator.
That fix breaks some debug checks that need non-trivial
changes and extra testing, so leave that for another CL.
This reverts commit dce413d333c88c86f28b9d0fe3a8052942ed9080.
Test: m test-art-host
Change-Id: I69935236a843a08d79b77c5fce74b4cc55f8ea99
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 8586b78..e0cd344 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -6091,6 +6091,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,
@@ -6630,6 +6665,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>*>(
+ Runtime::Current()->GetLinearAlloc()->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,
@@ -6644,25 +7153,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();
@@ -6679,32 +7172,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
@@ -6855,109 +7329,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) {
@@ -6973,266 +7354,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 6ef882a..77322ed 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -650,6 +650,8 @@
};
private:
+ class LinkInterfaceMethodsHelper;
+
struct ClassLoaderData {
jweak weak_root; // Weak root to enable class unloading.
ClassTable* class_table;
@@ -1087,6 +1089,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