diff options
| -rw-r--r-- | runtime/class_linker.cc | 457 | ||||
| -rw-r--r-- | runtime/class_linker.h | 12 | ||||
| -rw-r--r-- | runtime/gc/space/image_space.cc | 4 | ||||
| -rw-r--r-- | runtime/mirror/iftable.h | 5 |
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 { |