summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/class_linker.cc457
-rw-r--r--runtime/class_linker.h12
-rw-r--r--runtime/gc/space/image_space.cc4
-rw-r--r--runtime/mirror/iftable.h5
4 files changed, 284 insertions, 194 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d2ab81e7cf..56fd922b90 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -625,6 +625,16 @@ void ClassLinker::CheckSystemClass(Thread* self, Handle<mirror::Class> c1, const
}
}
+ObjPtr<mirror::IfTable> AllocIfTable(Thread* self,
+ size_t ifcount,
+ ObjPtr<mirror::Class> iftable_class)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(iftable_class->IsArrayClass());
+ DCHECK(iftable_class->GetComponentType()->IsObjectClass());
+ return ObjPtr<mirror::IfTable>::DownCast(ObjPtr<mirror::ObjectArray<mirror::Object>>(
+ mirror::IfTable::Alloc(self, iftable_class, ifcount * mirror::IfTable::kMax)));
+}
+
bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path,
std::string* error_msg) {
VLOG(startup) << "ClassLinker::Init";
@@ -732,10 +742,10 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b
SetClassRoot(ClassRoot::kJavaLangRefReference, java_lang_ref_Reference.Get());
// Fill in the empty iftable. Needs to be done after the kObjectArrayClass root is set.
- java_lang_Object->SetIfTable(AllocIfTable(self, 0));
+ java_lang_Object->SetIfTable(AllocIfTable(self, 0, object_array_class.Get()));
// Create array interface entries to populate once we can load system classes.
- object_array_class->SetIfTable(AllocIfTable(self, 2));
+ object_array_class->SetIfTable(AllocIfTable(self, 2, object_array_class.Get()));
DCHECK_EQ(GetArrayIfTable(), object_array_class->GetIfTable());
// Setup the primitive type classes.
@@ -6638,10 +6648,12 @@ void ClassLinker::FillIMTFromIfTable(ObjPtr<mirror::IfTable> if_table,
}
}
+namespace {
+
// Simple helper function that checks that no subtypes of 'val' are contained within the 'classes'
// set.
static bool NotSubinterfaceOfAny(
- const HashSet<mirror::Class*>& classes,
+ const ScopedArenaHashSet<mirror::Class*>& classes,
ObjPtr<mirror::Class> val)
REQUIRES(Roles::uninterruptible_)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -6654,48 +6666,113 @@ static bool NotSubinterfaceOfAny(
return true;
}
-// Fills in and flattens the interface inheritance hierarchy.
-//
-// By the end of this function all interfaces in the transitive closure of to_process are added to
-// the iftable and every interface precedes all of its sub-interfaces in this list.
-//
-// all I, J: Interface | I <: J implies J precedes I
-//
-// (note A <: B means that A is a subtype of B)
-//
-// This returns the total number of items in the iftable. The iftable might be resized down after
-// this call.
+// We record new interfaces by the index of the direct interface and the index in the
+// direct interface's `IfTable`, or `dex::kDexNoIndex` if it's the direct interface itself.
+struct NewInterfaceReference {
+ uint32_t direct_interface_index;
+ uint32_t direct_interface_iftable_index;
+};
+
+class ProxyInterfacesAccessor {
+ public:
+ explicit ProxyInterfacesAccessor(Handle<mirror::ObjectArray<mirror::Class>> interfaces)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ : interfaces_(interfaces) {}
+
+ size_t GetLength() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return interfaces_->GetLength();
+ }
+
+ ObjPtr<mirror::Class> GetInterface(size_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK_LT(index, GetLength());
+ return interfaces_->GetWithoutChecks(index);
+ }
+
+ private:
+ Handle<mirror::ObjectArray<mirror::Class>> interfaces_;
+};
+
+class NonProxyInterfacesAccessor {
+ public:
+ NonProxyInterfacesAccessor(ClassLinker* class_linker, Handle<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ : interfaces_(klass->GetInterfaceTypeList()),
+ class_linker_(class_linker),
+ klass_(klass) {
+ DCHECK(!klass->IsProxyClass());
+ }
+
+ size_t GetLength() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return (interfaces_ != nullptr) ? interfaces_->Size() : 0u;
+ }
+
+ ObjPtr<mirror::Class> GetInterface(size_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK_LT(index, GetLength());
+ dex::TypeIndex type_index = interfaces_->GetTypeItem(index).type_idx_;
+ return class_linker_->LookupResolvedType(type_index, klass_.Get());
+ }
+
+ private:
+ const dex::TypeList* interfaces_;
+ ClassLinker* class_linker_;
+ Handle<mirror::Class> klass_;
+};
+
+// Finds new interfaces to add to the interface table in addition to superclass interfaces.
//
-// We order this backwards so that we do not need to reorder superclass interfaces when new
-// interfaces are added in subclass's interface tables.
+// Interfaces in the interface table must satisfy the following constraint:
+// all I, J: Interface | I <: J implies J precedes I
+// (note A <: B means that A is a subtype of B). We order this backwards so that we do not need
+// to reorder superclass interfaces when new interfaces are added in subclass's interface tables.
//
-// Upon entry into this function iftable is a copy of the superclass's iftable with the first
-// super_ifcount entries filled in with the transitive closure of the interfaces of the superclass.
-// The other entries are uninitialized. We will fill in the remaining entries in this function. The
-// iftable must be large enough to hold all interfaces without changing its size.
-static size_t FillIfTable(ObjPtr<mirror::Class> klass,
- ObjPtr<mirror::ObjectArray<mirror::Class>> interfaces,
- ObjPtr<mirror::IfTable> iftable,
- size_t super_ifcount,
- size_t num_interfaces)
+// This function returns a list of references for all interfaces in the transitive
+// closure of the direct interfaces that are not in the superclass interfaces.
+// The entries in the list are ordered to satisfy the interface table ordering
+// constraint and therefore the interface table formed by appending them to the
+// superclass interface table shall also satisfy that constraint.
+template <typename InterfaceAccessor>
+ALWAYS_INLINE
+static ArrayRef<const NewInterfaceReference> FindNewIfTableInterfaces(
+ ObjPtr<mirror::IfTable> super_iftable,
+ size_t super_ifcount,
+ ScopedArenaAllocator* allocator,
+ InterfaceAccessor&& interfaces,
+ ArrayRef<NewInterfaceReference> initial_storage,
+ /*out*/ScopedArenaVector<NewInterfaceReference>* supplemental_storage)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedAssertNoThreadSuspension nts(__FUNCTION__);
+
// This is the set of all classes already in the iftable. Used to make checking
// if a class has already been added quicker.
constexpr size_t kBufferSize = 32; // 256 bytes on 64-bit architectures.
mirror::Class* buffer[kBufferSize];
- HashSet<mirror::Class*> classes_in_iftable(buffer, kBufferSize);
+ ScopedArenaHashSet<mirror::Class*> classes_in_iftable(buffer, kBufferSize, allocator->Adapter());
// The first super_ifcount elements are from the superclass. We note that they are already added.
for (size_t i = 0; i < super_ifcount; i++) {
- ObjPtr<mirror::Class> iface = iftable->GetInterface(i);
+ ObjPtr<mirror::Class> iface = super_iftable->GetInterface(i);
DCHECK(NotSubinterfaceOfAny(classes_in_iftable, iface)) << "Bad ordering.";
- classes_in_iftable.insert(iface.Ptr());
- }
- size_t filled_ifcount = super_ifcount;
- const bool have_interfaces = interfaces != nullptr;
- for (size_t i = 0; i != num_interfaces; ++i) {
- ObjPtr<mirror::Class> interface =
- have_interfaces ? interfaces->Get(i) : klass->GetDirectInterface(i);
+ classes_in_iftable.Put(iface.Ptr());
+ }
+
+ ArrayRef<NewInterfaceReference> current_storage = initial_storage;
+ DCHECK_NE(current_storage.size(), 0u);
+ size_t num_new_interfaces = 0u;
+ auto insert_reference = [&](uint32_t direct_interface_index,
+ uint32_t direct_interface_iface_index) {
+ if (UNLIKELY(num_new_interfaces == current_storage.size())) {
+ bool copy = current_storage.data() != supplemental_storage->data();
+ supplemental_storage->resize(2u * num_new_interfaces);
+ if (copy) {
+ std::copy_n(current_storage.data(), num_new_interfaces, supplemental_storage->data());
+ }
+ current_storage = ArrayRef<NewInterfaceReference>(*supplemental_storage);
+ }
+ current_storage[num_new_interfaces] = {direct_interface_index, direct_interface_iface_index};
+ ++num_new_interfaces;
+ };
+
+ for (size_t i = 0, num_interfaces = interfaces.GetLength(); i != num_interfaces; ++i) {
+ ObjPtr<mirror::Class> interface = interfaces.GetInterface(i);
// Let us call the first filled_ifcount elements of iftable the current-iface-list.
// At this point in the loop current-iface-list has the invariant that:
@@ -6710,136 +6787,140 @@ static size_t FillIfTable(ObjPtr<mirror::Class> klass,
int32_t ifcount = interface->GetIfTableCount();
for (int32_t j = 0; j < ifcount; j++) {
ObjPtr<mirror::Class> super_interface = interface->GetIfTable()->GetInterface(j);
- if (!ContainsElement(classes_in_iftable, super_interface)) {
+ if (classes_in_iftable.find(super_interface.Ptr()) == classes_in_iftable.end()) {
DCHECK(NotSubinterfaceOfAny(classes_in_iftable, super_interface)) << "Bad ordering.";
- classes_in_iftable.insert(super_interface.Ptr());
- iftable->SetInterface(filled_ifcount, super_interface);
- filled_ifcount++;
+ classes_in_iftable.Put(super_interface.Ptr());
+ insert_reference(i, j);
}
}
+ // Add this interface reference after all of its super-interfaces.
DCHECK(NotSubinterfaceOfAny(classes_in_iftable, interface)) << "Bad ordering";
- // Place this interface onto the current-iface-list after all of its super-interfaces.
- classes_in_iftable.insert(interface.Ptr());
- iftable->SetInterface(filled_ifcount, interface);
- filled_ifcount++;
+ classes_in_iftable.Put(interface.Ptr());
+ insert_reference(i, dex::kDexNoIndex);
} else if (kIsDebugBuild) {
// Check all super-interfaces are already in the list.
int32_t ifcount = interface->GetIfTableCount();
for (int32_t j = 0; j < ifcount; j++) {
ObjPtr<mirror::Class> super_interface = interface->GetIfTable()->GetInterface(j);
- DCHECK(ContainsElement(classes_in_iftable, super_interface))
+ DCHECK(classes_in_iftable.find(super_interface.Ptr()) != classes_in_iftable.end())
<< "Iftable does not contain " << mirror::Class::PrettyClass(super_interface)
<< ", a superinterface of " << interface->PrettyClass();
}
}
}
- if (kIsDebugBuild) {
- // Check that the iftable is ordered correctly.
- for (size_t i = 0; i < filled_ifcount; i++) {
- ObjPtr<mirror::Class> if_a = iftable->GetInterface(i);
- for (size_t j = i + 1; j < filled_ifcount; j++) {
- ObjPtr<mirror::Class> if_b = iftable->GetInterface(j);
- // !(if_a <: if_b)
- CHECK(!if_b->IsAssignableFrom(if_a))
- << "Bad interface order: " << mirror::Class::PrettyClass(if_a) << " (index " << i
- << ") extends "
- << if_b->PrettyClass() << " (index " << j << ") and so should be after it in the "
- << "interface list.";
- }
- }
- }
- return filled_ifcount;
+ return ArrayRef<const NewInterfaceReference>(current_storage.data(), num_new_interfaces);
}
-bool ClassLinker::SetupInterfaceLookupTable(Thread* self,
- Handle<mirror::Class> klass,
- Handle<mirror::ObjectArray<mirror::Class>> interfaces) {
- StackHandleScope<1> hs(self);
- const bool has_superclass = klass->HasSuperClass();
- const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U;
- const bool have_interfaces = interfaces != nullptr;
- const size_t num_interfaces =
- have_interfaces ? interfaces->GetLength() : klass->NumDirectInterfaces();
- if (num_interfaces == 0) {
- if (super_ifcount == 0) {
- if (LIKELY(has_superclass)) {
- klass->SetIfTable(klass->GetSuperClass()->GetIfTable());
- }
- // Class implements no interfaces.
- DCHECK_EQ(klass->GetIfTableCount(), 0);
- return true;
- }
- // Class implements same interfaces as parent, are any of these not marker interfaces?
- bool has_non_marker_interface = false;
- ObjPtr<mirror::IfTable> super_iftable = klass->GetSuperClass()->GetIfTable();
- for (size_t i = 0; i < super_ifcount; ++i) {
- if (super_iftable->GetMethodArrayCount(i) > 0) {
- has_non_marker_interface = true;
- break;
- }
- }
- // Class just inherits marker interfaces from parent so recycle parent's iftable.
- if (!has_non_marker_interface) {
- klass->SetIfTable(super_iftable);
+template <typename InterfaceAccessor>
+static ObjPtr<mirror::IfTable> SetupInterfaceLookupTable(
+ Thread* self,
+ Handle<mirror::Class> klass,
+ ScopedArenaAllocator* allocator,
+ InterfaceAccessor&& interfaces)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(klass->HasSuperClass());
+ ObjPtr<mirror::IfTable> super_iftable = klass->GetSuperClass()->GetIfTable();
+ const size_t super_ifcount = super_iftable->Count();
+ const size_t num_interfaces = interfaces.GetLength();
+
+ // If there are no new interfaces, we can recycle parent's interface table if the class
+ // inherits no interfaces from the superclass (this is always the case for interfaces as
+ // their superclass `java.lang.Object` does not implement any interface), or there are no
+ // new virtuals, or all interfaces inherited from the superclass are just marker interfaces.
+ auto is_marker_iface = [=](size_t index) REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
+ return super_iftable->GetMethodArrayCount(index) == 0;
+ };
+ auto can_reuse_super_iftable = [=]() REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE{
+ if (super_ifcount == 0u) {
return true;
}
+ DCHECK(!klass->IsInterface());
+ return klass->NumDeclaredVirtualMethods() == 0u ||
+ std::all_of(CountIter(0), CountIter(super_ifcount), is_marker_iface);
+ };
+ if (num_interfaces == 0 && can_reuse_super_iftable()) {
+ return super_iftable;
}
- size_t ifcount = super_ifcount + num_interfaces;
+
// Check that every class being implemented is an interface.
- for (size_t i = 0; i < num_interfaces; i++) {
- ObjPtr<mirror::Class> interface =
- have_interfaces ? interfaces->GetWithoutChecks(i) : klass->GetDirectInterface(i);
+ for (size_t i = 0; i != num_interfaces; ++i) {
+ ObjPtr<mirror::Class> interface = interfaces.GetInterface(i);
DCHECK(interface != nullptr);
if (UNLIKELY(!interface->IsInterface())) {
- std::string temp;
ThrowIncompatibleClassChangeError(klass.Get(),
"Class %s implements non-interface class %s",
klass->PrettyDescriptor().c_str(),
- PrettyDescriptor(interface->GetDescriptor(&temp)).c_str());
- return false;
+ interface->PrettyDescriptor().c_str());
+ return nullptr;
+ }
+ }
+
+ static constexpr size_t kMaxStackReferences = 16;
+ NewInterfaceReference initial_storage[kMaxStackReferences];
+ ScopedArenaVector<NewInterfaceReference> supplemental_storage(allocator->Adapter());
+ ArrayRef<const NewInterfaceReference> new_interface_references =
+ FindNewIfTableInterfaces(
+ super_iftable,
+ super_ifcount,
+ allocator,
+ interfaces,
+ ArrayRef<NewInterfaceReference>(initial_storage),
+ &supplemental_storage);
+
+ // If all declared interfaces were already present in superclass interface table, we can
+ // re-check if other conditions of reusing the superclass interface table are satisfied.
+ if (UNLIKELY(num_interfaces != 0u && new_interface_references.empty())) {
+ DCHECK(!klass->IsInterface());
+ if (can_reuse_super_iftable()) {
+ return super_iftable;
}
- ifcount += interface->GetIfTableCount();
}
- // Create the interface function table.
- MutableHandle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount)));
+
+ // Create the interface table.
+ size_t ifcount = super_ifcount + new_interface_references.size();
+ ObjPtr<mirror::IfTable> iftable = AllocIfTable(self, ifcount, super_iftable->GetClass());
if (UNLIKELY(iftable == nullptr)) {
self->AssertPendingOOMException();
- return false;
+ return nullptr;
}
// Fill in table with superclass's iftable.
if (super_ifcount != 0) {
- ObjPtr<mirror::IfTable> super_iftable = klass->GetSuperClass()->GetIfTable();
+ // Reload `super_iftable` as it may have been clobbered by the allocation.
+ super_iftable = klass->GetSuperClass()->GetIfTable();
for (size_t i = 0; i < super_ifcount; i++) {
ObjPtr<mirror::Class> super_interface = super_iftable->GetInterface(i);
iftable->SetInterface(i, super_interface);
}
}
+ // Fill in the table with additional interfaces.
+ size_t current_index = super_ifcount;
+ for (NewInterfaceReference ref : new_interface_references) {
+ ObjPtr<mirror::Class> direct_interface = interfaces.GetInterface(ref.direct_interface_index);
+ ObjPtr<mirror::Class> new_interface = (ref.direct_interface_iftable_index != dex::kDexNoIndex)
+ ? direct_interface->GetIfTable()->GetInterface(ref.direct_interface_iftable_index)
+ : direct_interface;
+ iftable->SetInterface(current_index, new_interface);
+ ++current_index;
+ }
+ DCHECK_EQ(current_index, ifcount);
- // Note that AllowThreadSuspension is to thread suspension as pthread_testcancel is to pthread
- // cancellation. That is it will suspend if one has a pending suspend request but otherwise
- // doesn't really do anything.
- self->AllowThreadSuspension();
-
- const size_t new_ifcount =
- FillIfTable(klass.Get(), interfaces.Get(), iftable.Get(), super_ifcount, num_interfaces);
-
- self->AllowThreadSuspension();
-
- // Shrink iftable in case duplicates were found
- if (new_ifcount < ifcount) {
- DCHECK_NE(num_interfaces, 0U);
- iftable.Assign(ObjPtr<mirror::IfTable>::DownCast(
- mirror::IfTable::CopyOf(iftable, self, new_ifcount * mirror::IfTable::kMax)));
- if (UNLIKELY(iftable == nullptr)) {
- self->AssertPendingOOMException();
- return false;
+ if (kIsDebugBuild) {
+ // Check that the iftable is ordered correctly.
+ for (size_t i = 0; i < ifcount; i++) {
+ ObjPtr<mirror::Class> if_a = iftable->GetInterface(i);
+ for (size_t j = i + 1; j < ifcount; j++) {
+ ObjPtr<mirror::Class> if_b = iftable->GetInterface(j);
+ // !(if_a <: if_b)
+ CHECK(!if_b->IsAssignableFrom(if_a))
+ << "Bad interface order: " << mirror::Class::PrettyClass(if_a) << " (index " << i
+ << ") extends "
+ << if_b->PrettyClass() << " (index " << j << ") and so should be after it in the "
+ << "interface list.";
+ }
}
- ifcount = new_ifcount;
- } else {
- DCHECK_EQ(new_ifcount, ifcount);
}
- klass->SetIfTable(iftable.Get());
- return true;
+
+ return iftable;
}
// Finds the method with a name/signature that matches cmp in the given lists of methods. The list
@@ -6861,8 +6942,6 @@ static ArtMethod* FindSameNameAndSignature(MethodNameAndSignatureComparator& cmp
return FindSameNameAndSignature(cmp, rest...);
}
-namespace {
-
// Check that all vtable entries are present in this class's virtuals or are the same as a
// superclasses vtable entry.
void CheckClassOwnsVTableEntries(Thread* self,
@@ -7126,6 +7205,7 @@ class ClassLinker::LinkMethodsHelper {
: class_linker_(class_linker),
klass_(klass),
self_(self),
+ runtime_(runtime),
stack_(runtime->GetLinearAlloc()->GetArenaPool()),
allocator_(&stack_),
default_translations_(default_translations_initial_buffer_,
@@ -7139,21 +7219,18 @@ class ClassLinker::LinkMethodsHelper {
move_table_(allocator_.Adapter()) {
}
- // Links the virtual methods for the given class and records any default methods
- // that will need to be updated later.
+ // Links the virtual and interface methods for the given class.
//
// Arguments:
// * self - The current thread.
// * klass - class, whose vtable will be filled in.
- bool LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- // Sets the imt entries and fixes up the vtable for the given class by linking
- // all the interface methods.
- bool LinkInterfaceMethods(
+ // * interfaces - implemented interfaces for a proxy class, otherwise null.
+ // * out_new_conflict - whether there is a new conflict compared to the superclass.
+ // * out_imt - interface method table to fill.
+ bool LinkMethods(
Thread* self,
Handle<mirror::Class> klass,
- Runtime* runtime,
+ Handle<mirror::ObjectArray<mirror::Class>> interfaces,
bool* out_new_conflict,
ArtMethod** out_imt)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -7167,9 +7244,18 @@ class ClassLinker::LinkMethodsHelper {
size_t num_virtual_methods)
REQUIRES_SHARED(Locks::mutator_lock_);
- bool LinkJavaLangObjectVirtualMethods(Thread* self, Handle<mirror::Class> klass)
+ bool LinkJavaLangObjectMethods(Thread* self, Handle<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+ // Sets the imt entries and fixes up the vtable for the given class by linking
+ // all the interface methods.
+ bool LinkInterfaceMethods(
+ Thread* self,
+ Handle<mirror::Class> klass,
+ bool* out_new_conflict,
+ ArtMethod** out_imt)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
ArtMethod* FindOrCreateImplementationMethod(
ArtMethod* interface_method,
MethodNameAndSignatureComparator& interface_name_comparator,
@@ -7386,6 +7472,7 @@ class ClassLinker::LinkMethodsHelper {
ClassLinker* class_linker_;
Handle<mirror::Class> klass_;
Thread* const self_;
+ Runtime* const runtime_;
// These are allocated on the heap to begin, we then transfer to linear alloc when we re-create
// the virtual methods array.
@@ -7905,9 +7992,12 @@ size_t ClassLinker::LinkMethodsHelper<kPointerSize>::AssignVtableIndexes(
template <PointerSize kPointerSize>
FLATTEN
-bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkVirtualMethods(
+bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkMethods(
Thread* self,
- Handle<mirror::Class> klass) {
+ Handle<mirror::Class> klass,
+ Handle<mirror::ObjectArray<mirror::Class>> interfaces,
+ bool* out_new_conflict,
+ ArtMethod** out_imt) {
const size_t num_virtual_methods = klass->NumVirtualMethods();
if (klass->IsInterface()) {
// No vtable.
@@ -7944,10 +8034,38 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkVirtualMethods(
if (has_defaults) {
klass->SetHasDefaultMethods();
}
- return true;
+ ObjPtr<mirror::IfTable> iftable = SetupInterfaceLookupTable(
+ self, klass, &allocator_, NonProxyInterfacesAccessor(class_linker_, klass));
+ if (UNLIKELY(iftable == nullptr)) {
+ self->AssertPendingException();
+ return false;
+ }
+ // TODO: Delay setting the interface table until we're sure we shall not throw an exception.
+ klass->SetIfTable(iftable);
+ return LinkInterfaceMethods(self, klass, out_new_conflict, out_imt);
} else if (LIKELY(klass->HasSuperClass())) {
- const size_t super_vtable_length = klass->GetSuperClass()->GetVTableLength();
+ // Copy IMT from superclass. It shall be updated later if needed.
+ class_linker_->FillImtFromSuperClass(klass,
+ runtime_->GetImtUnimplementedMethod(),
+ runtime_->GetImtConflictMethod(),
+ out_new_conflict,
+ out_imt);
+
+ // We set up the interface lookup table now because we need it to determine if we need
+ // to update any vtable entries with new default method implementations.
StackHandleScope<3> hs(self);
+ Handle<mirror::IfTable> iftable = hs.NewHandle(UNLIKELY(klass->IsProxyClass())
+ ? SetupInterfaceLookupTable(self, klass, &allocator_, ProxyInterfacesAccessor(interfaces))
+ : SetupInterfaceLookupTable(
+ self, klass, &allocator_, NonProxyInterfacesAccessor(class_linker_, klass)));
+ if (UNLIKELY(iftable == nullptr)) {
+ self->AssertPendingException();
+ return false;
+ }
+ // TODO: Delay setting the interface table until we're sure we shall not throw an exception.
+ klass->SetIfTable(iftable.Get());
+
+ const size_t super_vtable_length = klass->GetSuperClass()->GetVTableLength();
Handle<mirror::Class> super_class(hs.NewHandle(klass->GetSuperClass()));
// If there are no new virtual methods and no new interfaces, we can simply reuse
@@ -7971,6 +8089,8 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkVirtualMethods(
CHECK(super_vtable != nullptr) << super_class->PrettyClass();
klass->SetVTable(super_vtable);
}
+ // The interface table from superclass has also been reused by `SetupInterfaceLookupTable()`.
+ DCHECK(iftable.Get() == super_class->GetIfTable()) << klass->PrettyDescriptor();
return true;
}
@@ -7999,6 +8119,15 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkVirtualMethods(
vtable->SetElementPtrSize(vtable_index, &virtual_method, kPointerSize);
}
+ // Allocate method arrays, so that we can link interface methods without thread suspension,
+ // otherwise GC could miss visiting newly allocated copied methods.
+ // TODO: Do not allocate copied methods during linking, store only records about what
+ // we need to allocate and allocate it at the end. Start with superclass iftable and
+ // perform copy-on-write when needed to facilitate maximum memory sharing.
+ if (!class_linker_->AllocateIfTableMethodArrays(self, klass, iftable)) {
+ return false;
+ }
+
// For non-overridden vtable slots, copy a method from `super_class`.
for (size_t j = 0; j != super_vtable_length; ++j) {
if (vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(j) != nullptr) {
@@ -8065,14 +8194,14 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkVirtualMethods(
}
klass->SetVTable(vtable.Get());
+ return LinkInterfaceMethods(self, klass, out_new_conflict, out_imt);
} else {
- return LinkJavaLangObjectVirtualMethods(self, klass);
+ return LinkJavaLangObjectMethods(self, klass);
}
- return true;
}
template <PointerSize kPointerSize>
-bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkJavaLangObjectVirtualMethods(
+bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkJavaLangObjectMethods(
Thread* self,
Handle<mirror::Class> klass) {
DCHECK_EQ(klass.Get(), GetClassRoot<mirror::Object>(class_linker_));
@@ -8094,16 +8223,17 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkJavaLangObjectVirtualMeth
klass.Get(),
kPointerSize,
ArrayRef<uint32_t>(class_linker_->object_virtual_method_hashes_));
+ // The interface table is already allocated but there are no interface methods to link.
+ DCHECK(klass->GetIfTable() != nullptr);
+ DCHECK_EQ(klass->GetIfTableCount(), 0);
return true;
}
// TODO This method needs to be split up into several smaller methods.
template <PointerSize kPointerSize>
-FLATTEN
bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkInterfaceMethods(
Thread* self,
Handle<mirror::Class> klass,
- Runtime* runtime,
bool* out_new_conflict,
ArtMethod** out_imt) {
StackHandleScope<3> hs(self);
@@ -8117,23 +8247,8 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkInterfaceMethods(
Handle<mirror::IfTable> iftable(hs.NewHandle(klass->GetIfTable()));
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.
- if (has_superclass && fill_tables) {
- class_linker_->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) {
- if (!class_linker_->AllocateIfTableMethodArrays(self, klass, iftable)) {
- return false;
- }
- }
+ ArtMethod* const unimplemented_method = runtime_->GetImtUnimplementedMethod();
+ ArtMethod* const imt_conflict_method = runtime_->GetImtConflictMethod();
auto* old_cause = self->StartAssertNoThreadSuspension(
"Copying ArtMethods for LinkInterfaceMethods");
@@ -8345,23 +8460,16 @@ bool ClassLinker::LinkMethods(Thread* self,
bool* out_new_conflict,
ArtMethod** out_imt) {
self->AllowThreadSuspension();
- // We set up the interface lookup table first because we need it to determine if we need
- // to update any vtable entries with new default method implementations.
- if (!SetupInterfaceLookupTable(self, klass, interfaces)) {
- return false;
- }
// Link virtual methods then interface methods.
Runtime* const runtime = Runtime::Current();
if (LIKELY(GetImagePointerSize() == kRuntimePointerSize)) {
LinkMethodsHelper<kRuntimePointerSize> helper(this, klass, self, runtime);
- return helper.LinkVirtualMethods(self, klass) &&
- helper.LinkInterfaceMethods(self, klass, runtime, out_new_conflict, out_imt);
+ return helper.LinkMethods(self, klass, interfaces, out_new_conflict, out_imt);
} else {
constexpr PointerSize kOtherPointerSize =
(kRuntimePointerSize == PointerSize::k64) ? PointerSize::k32 : PointerSize::k64;
LinkMethodsHelper<kOtherPointerSize> helper(this, klass, self, runtime);
- return helper.LinkVirtualMethods(self, klass) &&
- helper.LinkInterfaceMethods(self, klass, runtime, out_new_conflict, out_imt);
+ return helper.LinkMethods(self, klass, interfaces, out_new_conflict, out_imt);
}
}
@@ -10063,13 +10171,6 @@ ObjPtr<mirror::Class> ClassLinker::GetHoldingClassOfCopiedMethod(ArtMethod* meth
return visitor.holder_;
}
-ObjPtr<mirror::IfTable> ClassLinker::AllocIfTable(Thread* self, size_t ifcount) {
- return ObjPtr<mirror::IfTable>::DownCast(ObjPtr<mirror::ObjectArray<mirror::Object>>(
- mirror::IfTable::Alloc(self,
- GetClassRoot<mirror::ObjectArray<mirror::Object>>(this),
- ifcount * mirror::IfTable::kMax)));
-}
-
bool ClassLinker::DenyAccessBasedOnPublicSdk(ArtMethod* art_method ATTRIBUTE_UNUSED) const
REQUIRES_SHARED(Locks::mutator_lock_) {
// Should not be called on ClassLinker, only on AotClassLinker that overrides this.
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index b0c02e538e..22a8c7f190 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -552,10 +552,6 @@ class ClassLinker {
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
- ObjPtr<mirror::IfTable> AllocIfTable(Thread* self, size_t ifcount)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Roles::uninterruptible_);
-
ObjPtr<mirror::ObjectArray<mirror::StackTraceElement>> AllocStackTraceElementArray(Thread* self,
size_t length)
REQUIRES_SHARED(Locks::mutator_lock_)
@@ -1172,14 +1168,6 @@ class ClassLinker {
const dex::MethodHandleItem& method_handle,
ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_);
- // Sets up the interface lookup table (IFTable) in the correct order to allow searching for
- // default methods.
- bool SetupInterfaceLookupTable(Thread* self,
- Handle<mirror::Class> klass,
- Handle<mirror::ObjectArray<mirror::Class>> interfaces)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
-
enum class DefaultMethodSearchResult {
kDefaultFound,
kAbstractFound,
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index caae3c6585..c7247cca74 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -2778,8 +2778,8 @@ class ImageSpace::BootImageLoader {
main_patch_object_visitor.VisitPointerArray(vtable);
}
ObjPtr<mirror::IfTable> iftable = klass->GetIfTable<kVerifyNone, kWithoutReadBarrier>();
- if (iftable != nullptr) {
- int32_t ifcount = klass->GetIfTableCount<kVerifyNone>();
+ if (kExtension ? simple_relocate_visitor.InDest(iftable.Ptr()) : iftable != nullptr) {
+ int32_t ifcount = iftable->Count<kVerifyNone>();
for (int32_t i = 0; i != ifcount; ++i) {
ObjPtr<mirror::PointerArray> unpatched_ifarray =
iftable->GetMethodArrayOrNull<kVerifyNone, kWithoutReadBarrier>(i);
diff --git a/runtime/mirror/iftable.h b/runtime/mirror/iftable.h
index 7391e0700d..30fed566aa 100644
--- a/runtime/mirror/iftable.h
+++ b/runtime/mirror/iftable.h
@@ -46,8 +46,9 @@ class MANAGED IfTable final : public ObjectArray<Object> {
void SetMethodArray(int32_t i, ObjPtr<PointerArray> arr) REQUIRES_SHARED(Locks::mutator_lock_);
- size_t Count() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetLength() / kMax;
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ ALWAYS_INLINE size_t Count() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetLength<kVerifyFlags>() / kMax;
}
enum {