summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Vladimir Marko <vmarko@google.com> 2022-02-18 13:55:15 +0000
committer Vladimir Marko <vmarko@google.com> 2022-02-21 09:14:17 +0000
commit0441d203ff22b2574764835e313af57b30ddd3ce (patch)
tree81b47d71ab497e2c295782833910fc30a9097ac1
parent45c6d8b52c522fb1be39b169596cf81815aba5db (diff)
Share `IfTable`s and their method arrays when possible.
Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Bug: 181943478 Change-Id: Ifd8353fac740eabb0ceedd247f66bd07747b20b8
-rw-r--r--runtime/class_linker.cc253
-rw-r--r--runtime/class_linker.h6
-rw-r--r--runtime/mirror/array.cc5
-rw-r--r--runtime/mirror/iftable-inl.h2
4 files changed, 168 insertions, 98 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 813d8f0ed0..62be39ab4c 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -6170,26 +6170,6 @@ ArtMethod* ClassLinker::AddMethodToConflictTable(ObjPtr<mirror::Class> klass,
return new_conflict_method;
}
-bool ClassLinker::AllocateIfTableMethodArrays(Thread* self,
- Handle<mirror::Class> klass,
- Handle<mirror::IfTable> iftable) {
- DCHECK(!klass->IsInterface());
- DCHECK(klass->HasSuperClass());
- const size_t ifcount = iftable->Count();
- for (size_t i = 0; i < ifcount; ++i) {
- size_t num_methods = iftable->GetInterface(i)->NumDeclaredVirtualMethods();
- if (num_methods > 0) {
- ObjPtr<mirror::PointerArray> 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,
@@ -6572,25 +6552,13 @@ static ObjPtr<mirror::IfTable> SetupInterfaceLookupTable(
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(klass->HasSuperClass());
ObjPtr<mirror::IfTable> super_iftable = klass->GetSuperClass()->GetIfTable();
- const size_t super_ifcount = super_iftable->Count();
+ DCHECK(super_iftable != nullptr);
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()) {
+ // If there are no new interfaces, return the interface table from superclass.
+ // If any implementation methods are overridden, we shall copy the table and
+ // the method arrays that contain any differences (copy-on-write).
+ if (num_interfaces == 0) {
return super_iftable;
}
@@ -6610,6 +6578,7 @@ static ObjPtr<mirror::IfTable> SetupInterfaceLookupTable(
static constexpr size_t kMaxStackReferences = 16;
NewInterfaceReference initial_storage[kMaxStackReferences];
ScopedArenaVector<NewInterfaceReference> supplemental_storage(allocator->Adapter());
+ const size_t super_ifcount = super_iftable->Count();
ArrayRef<const NewInterfaceReference> new_interface_references =
FindNewIfTableInterfaces(
super_iftable,
@@ -6619,13 +6588,10 @@ static ObjPtr<mirror::IfTable> SetupInterfaceLookupTable(
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;
- }
+ // If all declared interfaces were already present in superclass interface table,
+ // return the interface table from superclass. See above.
+ if (UNLIKELY(new_interface_references.empty())) {
+ return super_iftable;
}
// Create the interface table.
@@ -6639,9 +6605,14 @@ static ObjPtr<mirror::IfTable> SetupInterfaceLookupTable(
if (super_ifcount != 0) {
// 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++) {
+ for (size_t i = 0; i != super_ifcount; i++) {
ObjPtr<mirror::Class> super_interface = super_iftable->GetInterface(i);
+ DCHECK(super_interface != nullptr);
iftable->SetInterface(i, super_interface);
+ ObjPtr<mirror::PointerArray> method_array = super_iftable->GetMethodArrayOrNull(i);
+ if (method_array != nullptr) {
+ iftable->SetMethodArray(i, method_array);
+ }
}
}
// Fill in the table with additional interfaces.
@@ -6935,6 +6906,16 @@ class ClassLinker::LinkMethodsHelper {
REQUIRES_SHARED(Locks::mutator_lock_);
private:
+ // Allocate a pointer array.
+ static ObjPtr<mirror::PointerArray> AllocPointerArray(Thread* self, size_t length)
+ 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_);
+
// Assign vtable indexes to declared virtual methods for a non-interface class other
// than `java.lang.Object`. Returns the number of vtable entries on success, 0 on failure.
// This function also assigns vtable indexes for interface methods in new interfaces
@@ -6955,9 +6936,9 @@ class ClassLinker::LinkMethodsHelper {
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
void ReallocMethods(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
- void FinalizeIfTable(ObjPtr<mirror::Class> klass,
- ObjPtr<mirror::IfTable> iftable,
- ObjPtr<mirror::PointerArray> vtable,
+ bool FinalizeIfTable(Handle<mirror::Class> klass,
+ MutableHandle<mirror::IfTable> iftable,
+ Handle<mirror::PointerArray> vtable,
bool is_klass_abstract,
bool is_super_abstract,
bool* out_new_conflict,
@@ -7694,18 +7675,21 @@ void ClassLinker::LinkMethodsHelper<kPointerSize>::ReallocMethods(ObjPtr<mirror:
}
template <PointerSize kPointerSize>
-void ClassLinker::LinkMethodsHelper<kPointerSize>::FinalizeIfTable(
- ObjPtr<mirror::Class> klass,
- ObjPtr<mirror::IfTable> iftable,
- ObjPtr<mirror::PointerArray> vtable,
+bool ClassLinker::LinkMethodsHelper<kPointerSize>::FinalizeIfTable(
+ Handle<mirror::Class> klass,
+ MutableHandle<mirror::IfTable> iftable,
+ Handle<mirror::PointerArray> vtable,
bool is_klass_abstract,
bool is_super_abstract,
bool* out_new_conflict,
ArtMethod** out_imt) {
- ObjPtr<mirror::IfTable> super_iftable = klass->GetSuperClass()->GetIfTable();
size_t ifcount = iftable->Count();
- size_t super_ifcount = super_iftable->Count();
+ // We do not need a read barrier here as the length is constant, both from-space and
+ // to-space `IfTable`s shall yield the same result. See also `Class::GetIfTableCount()`.
+ size_t super_ifcount =
+ klass->GetSuperClass<kDefaultVerifyFlags, kWithoutReadBarrier>()->GetIfTableCount();
+ ClassLinker* class_linker = nullptr;
ArtMethod* unimplemented_method = nullptr;
ArtMethod* imt_conflict_method = nullptr;
uintptr_t imt_methods_begin = 0u;
@@ -7713,6 +7697,7 @@ void ClassLinker::LinkMethodsHelper<kPointerSize>::FinalizeIfTable(
DCHECK_EQ(klass->ShouldHaveImt(), !is_klass_abstract);
DCHECK_EQ(klass->GetSuperClass()->ShouldHaveImt(), !is_super_abstract);
if (!is_klass_abstract) {
+ class_linker = class_linker_;
unimplemented_method = runtime_->GetImtUnimplementedMethod();
imt_conflict_method = runtime_->GetImtConflictMethod();
if (is_super_abstract) {
@@ -7734,32 +7719,76 @@ void ClassLinker::LinkMethodsHelper<kPointerSize>::FinalizeIfTable(
}
}
+ auto update_imt = [=](ObjPtr<mirror::Class> iface, size_t j, ArtMethod* implementation)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Place method in imt if entry is empty, place conflict otherwise.
+ ArtMethod** imt_ptr = &out_imt[iface->GetVirtualMethod(j, kPointerSize)->GetImtIndex()];
+ class_linker->SetIMTRef(unimplemented_method,
+ imt_conflict_method,
+ implementation,
+ /*out*/out_new_conflict,
+ /*out*/imt_ptr);
+ };
+
// For interfaces inherited from superclass, the new method arrays are empty,
// so use vtable indexes from implementation methods from the superclass method array.
for (size_t i = 0; i != super_ifcount; ++i) {
ObjPtr<mirror::PointerArray> method_array = iftable->GetMethodArrayOrNull(i);
+ DCHECK(method_array == klass->GetSuperClass()->GetIfTable()->GetMethodArrayOrNull(i));
if (method_array == nullptr) {
continue;
}
size_t num_methods = method_array->GetLength();
ObjPtr<mirror::Class> iface = iftable->GetInterface(i);
- ObjPtr<mirror::PointerArray> super_method_array = super_iftable->GetMethodArrayOrNull(i);
- for (size_t j = 0; j != num_methods; ++j) {
+ size_t j = 0;
+ // First loop has method array shared with the super class.
+ for (; j != num_methods; ++j) {
+ ArtMethod* super_implementation =
+ method_array->GetElementPtrSize<ArtMethod*, kPointerSize>(j);
+ size_t vtable_index = super_implementation->GetMethodIndex();
+ ArtMethod* implementation =
+ vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(vtable_index);
+ // Check if we need to update IMT with this method, see above.
+ if (reinterpret_cast<uintptr_t>(implementation) - imt_methods_begin < imt_methods_size) {
+ update_imt(iface, j, implementation);
+ }
+ if (implementation != super_implementation) {
+ // Copy-on-write and move to the next loop.
+ Thread* self = self_;
+ StackHandleScope<2u> hs(self);
+ Handle<mirror::PointerArray> old_method_array = hs.NewHandle(method_array);
+ HandleWrapperObjPtr<mirror::Class> h_iface = hs.NewHandleWrapper(&iface);
+ if (ifcount == super_ifcount && iftable.Get() == klass->GetSuperClass()->GetIfTable()) {
+ ObjPtr<mirror::IfTable> new_iftable = ObjPtr<mirror::IfTable>::DownCast(
+ mirror::ObjectArray<mirror::Object>::CopyOf(
+ iftable, self, ifcount * mirror::IfTable::kMax));
+ if (new_iftable == nullptr) {
+ return false;
+ }
+ iftable.Assign(new_iftable);
+ }
+ method_array = ObjPtr<mirror::PointerArray>::DownCast(
+ mirror::Array::CopyOf(old_method_array, self, num_methods));
+ if (method_array == nullptr) {
+ return false;
+ }
+ iftable->SetMethodArray(i, method_array);
+ method_array->SetElementPtrSize(j, implementation, kPointerSize);
+ ++j;
+ break;
+ }
+ }
+ // Second loop (if non-empty) has method array different from the superclass.
+ for (; j != num_methods; ++j) {
ArtMethod* super_implementation =
- super_method_array->GetElementPtrSize<ArtMethod*, kPointerSize>(j);
+ method_array->GetElementPtrSize<ArtMethod*, kPointerSize>(j);
size_t vtable_index = super_implementation->GetMethodIndex();
ArtMethod* implementation =
vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(vtable_index);
method_array->SetElementPtrSize(j, implementation, kPointerSize);
// Check if we need to update IMT with this method, see above.
if (reinterpret_cast<uintptr_t>(implementation) - imt_methods_begin < imt_methods_size) {
- // Place method in imt if entry is empty, place conflict otherwise.
- ArtMethod** imt_ptr = &out_imt[iface->GetVirtualMethod(j, kPointerSize)->GetImtIndex()];
- class_linker_->SetIMTRef(unimplemented_method,
- imt_conflict_method,
- implementation,
- /*out*/out_new_conflict,
- /*out*/imt_ptr);
+ update_imt(iface, j, implementation);
}
}
}
@@ -7779,16 +7808,12 @@ void ClassLinker::LinkMethodsHelper<kPointerSize>::FinalizeIfTable(
vtable->GetElementPtrSize<ArtMethod*, kPointerSize>(vtable_index);
method_array->SetElementPtrSize(j, implementation, kPointerSize);
if (!is_klass_abstract) {
- // Place method in IMT if entry is empty, place conflict otherwise.
- ArtMethod** imt_ptr = &out_imt[iface->GetVirtualMethod(j, kPointerSize)->GetImtIndex()];
- class_linker_->SetIMTRef(unimplemented_method,
- imt_conflict_method,
- implementation,
- /*out*/out_new_conflict,
- /*out*/imt_ptr);
+ update_imt(iface, j, implementation);
}
}
}
+
+ return true;
}
NO_INLINE
@@ -7806,6 +7831,56 @@ static void ThrowIllegalAccessErrorForImplementingMethod(ObjPtr<mirror::Class> k
}
template <PointerSize kPointerSize>
+ObjPtr<mirror::PointerArray> ClassLinker::LinkMethodsHelper<kPointerSize>::AllocPointerArray(
+ Thread* self, size_t length) {
+ using PointerArrayType = std::conditional_t<
+ kPointerSize == PointerSize::k64, mirror::LongArray, mirror::IntArray>;
+ ObjPtr<mirror::Array> array = PointerArrayType::Alloc(self, length);
+ return ObjPtr<mirror::PointerArray>::DownCast(array);
+}
+
+template <PointerSize kPointerSize>
+bool ClassLinker::LinkMethodsHelper<kPointerSize>::AllocateIfTableMethodArrays(
+ Thread* self,
+ Handle<mirror::Class> klass,
+ Handle<mirror::IfTable> iftable) {
+ DCHECK(!klass->IsInterface());
+ DCHECK(klass_->HasSuperClass());
+ const size_t ifcount = iftable->Count();
+ // We do not need a read barrier here as the length is constant, both from-space and
+ // to-space `IfTable`s shall yield the same result. See also `Class::GetIfTableCount()`.
+ size_t super_ifcount =
+ klass->GetSuperClass<kDefaultVerifyFlags, kWithoutReadBarrier>()->GetIfTableCount();
+ if (ifcount == super_ifcount) {
+ DCHECK(iftable.Get() == klass_->GetSuperClass()->GetIfTable());
+ return true;
+ }
+
+ if (kIsDebugBuild) {
+ // The method array references for superclass interfaces have been copied.
+ // We shall allocate new arrays if needed (copy-on-write) in `FinalizeIfTable()`.
+ ObjPtr<mirror::IfTable> super_iftable = klass_->GetSuperClass()->GetIfTable();
+ for (size_t i = 0; i != super_ifcount; ++i) {
+ CHECK(iftable->GetInterface(i) == super_iftable->GetInterface(i));
+ CHECK(iftable->GetMethodArrayOrNull(i) == super_iftable->GetMethodArrayOrNull(i));
+ }
+ }
+
+ for (size_t i = super_ifcount; i < ifcount; ++i) {
+ size_t num_methods = iftable->GetInterface(i)->NumDeclaredVirtualMethods();
+ if (num_methods > 0) {
+ ObjPtr<mirror::PointerArray> method_array = AllocPointerArray(self, num_methods);
+ if (UNLIKELY(method_array == nullptr)) {
+ self->AssertPendingOOMException();
+ return false;
+ }
+ iftable->SetMethodArray(i, method_array);
+ }
+ }
+ return true;
+}
+
+template <PointerSize kPointerSize>
size_t ClassLinker::LinkMethodsHelper<kPointerSize>::AssignVTableIndexes(
ObjPtr<mirror::Class> klass,
ObjPtr<mirror::Class> super_class,
@@ -8245,7 +8320,7 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkMethods(
// 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())
+ MutableHandle<mirror::IfTable> iftable = hs.NewHandle(UNLIKELY(klass->IsProxyClass())
? SetupInterfaceLookupTable(self, klass, &allocator_, ProxyInterfacesAccessor(interfaces))
: SetupInterfaceLookupTable(
self, klass, &allocator_, NonProxyInterfacesAccessor(class_linker_, klass)));
@@ -8254,10 +8329,8 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkMethods(
return false;
}
- const size_t super_vtable_length = klass->GetSuperClass()->GetVTableLength();
- Handle<mirror::Class> super_class = hs.NewHandle(klass->GetSuperClass());
-
// Copy the IMT from superclass if present and needed. Update with new methods later.
+ Handle<mirror::Class> super_class = hs.NewHandle(klass->GetSuperClass());
bool is_klass_abstract = klass->IsAbstract();
bool is_super_abstract = super_class->IsAbstract();
DCHECK_EQ(klass->ShouldHaveImt(), !is_klass_abstract);
@@ -8271,6 +8344,7 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkMethods(
// If there are no new virtual methods and no new interfaces, we can simply reuse
// the vtable from superclass. We may need to make a copy if it's embedded.
+ const size_t super_vtable_length = super_class->GetVTableLength();
if (num_virtual_methods == 0 && iftable.Get() == super_class->GetIfTable()) {
DCHECK_EQ(is_super_abstract, !super_class->ShouldHaveEmbeddedVTable());
if (is_super_abstract) {
@@ -8290,8 +8364,7 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkMethods(
out_imt);
}
} else {
- ObjPtr<mirror::PointerArray> vtable =
- class_linker_->AllocPointerArray(self, super_vtable_length);
+ ObjPtr<mirror::PointerArray> vtable = AllocPointerArray(self, super_vtable_length);
if (UNLIKELY(vtable == nullptr)) {
self->AssertPendingOOMException();
return false;
@@ -8312,7 +8385,8 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkMethods(
// 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)) {
+ if (!AllocateIfTableMethodArrays(self, klass, iftable)) {
+ self->AssertPendingOOMException();
return false;
}
@@ -8325,8 +8399,7 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkMethods(
DCHECK(IsUint<16>(final_vtable_size));
// Allocate the new vtable.
- Handle<mirror::PointerArray> vtable =
- hs.NewHandle(class_linker_->AllocPointerArray(self, final_vtable_size));
+ Handle<mirror::PointerArray> vtable = hs.NewHandle(AllocPointerArray(self, final_vtable_size));
if (UNLIKELY(vtable == nullptr)) {
self->AssertPendingOOMException();
return false;
@@ -8352,13 +8425,16 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkMethods(
}
// Update the `iftable` (and IMT) with finalized virtual methods.
- FinalizeIfTable(klass.Get(),
- iftable.Get(),
- vtable.Get(),
- is_klass_abstract,
- is_super_abstract,
- out_new_conflict,
- out_imt);
+ if (!FinalizeIfTable(klass,
+ iftable,
+ vtable,
+ is_klass_abstract,
+ is_super_abstract,
+ out_new_conflict,
+ out_imt)) {
+ self->AssertPendingOOMException();
+ return false;
+ }
klass->SetVTable(vtable.Get());
klass->SetIfTable(iftable.Get());
@@ -8379,8 +8455,7 @@ bool ClassLinker::LinkMethodsHelper<kPointerSize>::LinkJavaLangObjectMethods(
DCHECK_EQ(klass.Get(), GetClassRoot<mirror::Object>(class_linker_));
DCHECK_EQ(klass->NumVirtualMethods(), mirror::Object::kVTableLength);
static_assert(IsUint<16>(mirror::Object::kVTableLength));
- ObjPtr<mirror::PointerArray> vtable =
- class_linker_->AllocPointerArray(self, mirror::Object::kVTableLength);
+ ObjPtr<mirror::PointerArray> vtable = AllocPointerArray(self, mirror::Object::kVTableLength);
if (UNLIKELY(vtable == nullptr)) {
self->AssertPendingOOMException();
return false;
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index ae83009aa8..a503946634 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -1237,12 +1237,6 @@ class ClassLinker {
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
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index bd9f9cac18..7b61b86416 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -121,9 +121,10 @@ ObjPtr<Array> Array::CreateMultiArray(Thread* self,
template<typename T>
ObjPtr<PrimitiveArray<T>> PrimitiveArray<T>::Alloc(Thread* self, size_t length) {
- gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+ Runtime* runtime = Runtime::Current();
+ gc::AllocatorType allocator_type = runtime->GetHeap()->GetCurrentAllocator();
ObjPtr<Array> raw_array = Array::Alloc(self,
- GetClassRoot<PrimitiveArray<T>>(),
+ GetClassRoot<PrimitiveArray<T>>(runtime->GetClassLinker()),
length,
ComponentSizeShiftWidth(sizeof(T)),
allocator_type);
diff --git a/runtime/mirror/iftable-inl.h b/runtime/mirror/iftable-inl.h
index c96082f560..ce9d3f8515 100644
--- a/runtime/mirror/iftable-inl.h
+++ b/runtime/mirror/iftable-inl.h
@@ -65,8 +65,8 @@ inline size_t IfTable::GetMethodArrayCount(int32_t i) {
inline void IfTable::SetMethodArray(int32_t i, ObjPtr<PointerArray> arr) {
DCHECK(arr != nullptr);
+ DCHECK_EQ(static_cast<size_t>(arr->GetLength()), GetInterface(i)->NumDeclaredVirtualMethods());
auto idx = i * kMax + kMethodArray;
- DCHECK(Get(idx) == nullptr);
Set<false>(idx, arr);
}