summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/image_writer.cc2
-rw-r--r--oatdump/oatdump.cc10
-rw-r--r--runtime/arch/arm/quick_entrypoints_arm.S30
-rw-r--r--runtime/arch/arm64/quick_entrypoints_arm64.S31
-rw-r--r--runtime/arch/x86/quick_entrypoints_x86.S35
-rw-r--r--runtime/arch/x86_64/quick_entrypoints_x86_64.S27
-rw-r--r--runtime/art_method-inl.h12
-rw-r--r--runtime/art_method.h83
-rw-r--r--runtime/asm_support.h8
-rw-r--r--runtime/class_linker.cc18
-rw-r--r--runtime/class_linker.h2
-rw-r--r--runtime/entrypoints/entrypoint_utils-inl.h2
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc115
-rw-r--r--runtime/runtime.cc13
-rw-r--r--runtime/runtime.h3
-rw-r--r--test/469-condition-materialization/expected.txt (renamed from test/469-condition-materialization-regression/expected.txt)0
-rw-r--r--test/469-condition-materialization/info.txt (renamed from test/469-condition-materialization-regression/info.txt)0
-rw-r--r--test/469-condition-materialization/src/Main.java (renamed from test/469-condition-materialization-regression/src/Main.java)0
-rw-r--r--test/589-super-imt/expected.txt0
-rw-r--r--test/589-super-imt/info.txt2
-rw-r--r--test/589-super-imt/src/Main.java447
21 files changed, 780 insertions, 60 deletions
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 07bd0e35db..c747ffa65b 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -1986,7 +1986,7 @@ const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method,
const ImageInfo& image_info,
bool* quick_is_interpreted) {
DCHECK(!method->IsResolutionMethod()) << PrettyMethod(method);
- DCHECK(!method->IsImtConflictMethod()) << PrettyMethod(method);
+ DCHECK_NE(method, Runtime::Current()->GetImtConflictMethod()) << PrettyMethod(method);
DCHECK(!method->IsImtUnimplementedMethod()) << PrettyMethod(method);
DCHECK(method->IsInvokable()) << PrettyMethod(method);
DCHECK(!IsInBootImage(method)) << PrettyMethod(method);
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 9ac18e830e..48a9d912fe 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1997,9 +1997,13 @@ class ImageDumper {
image_header_.GetPointerSize())) {
indent_os << StringPrintf("OAT CODE: %p\n", quick_oat_code_begin);
}
- } else if (method->IsAbstract() || method->IsCalleeSaveMethod() ||
- method->IsResolutionMethod() || method->IsImtConflictMethod() ||
- method->IsImtUnimplementedMethod() || method->IsClassInitializer()) {
+ } else if (method->IsAbstract() ||
+ method->IsCalleeSaveMethod() ||
+ method->IsResolutionMethod() ||
+ (method == Runtime::Current()->GetImtConflictMethod()) ||
+ method->IsImtUnimplementedMethod() ||
+ method->IsClassInitializer()) {
+ // Don't print information for these.
} else {
const DexFile::CodeItem* code_item = method->GetCodeItem();
size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2;
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 82ec0b7c09..5a901f1e46 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1225,11 +1225,35 @@ ENTRY art_quick_proxy_invoke_handler
END art_quick_proxy_invoke_handler
/*
- * Called to resolve an imt conflict. r12 is a hidden argument that holds the target method's
- * dex method index.
+ * Called to resolve an imt conflict.
+ * r0 is the conflict ArtMethod.
+ * r12 is a hidden argument that holds the target interface method's dex method index.
+ *
+ * Note that this stub writes to r0, r4, and r12.
*/
ENTRY art_quick_imt_conflict_trampoline
- mov r0, r12
+ ldr r4, [sp, #0] // Load referrer
+ ldr r4, [r4, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_32] // Load dex cache methods array
+ ldr r12, [r4, r12, lsl #POINTER_SIZE_SHIFT] // Load interface method
+ ldr r0, [r0, #ART_METHOD_JNI_OFFSET_32] // Load ImtConflictTable
+ ldr r4, [r0] // Load first entry in ImtConflictTable.
+.Limt_table_iterate:
+ cmp r4, r12
+ // Branch if found. Benchmarks have shown doing a branch here is better.
+ beq .Limt_table_found
+ // If the entry is null, the interface method is not in the ImtConflictTable.
+ cbz r4, .Lconflict_trampoline
+ // Iterate over the entries of the ImtConflictTable.
+ ldr r4, [r0, #(2 * __SIZEOF_POINTER__)]!
+ b .Limt_table_iterate
+.Limt_table_found:
+ // We successuflly hit an entry in the table. Load the target method
+ // and jump to it.
+ ldr r0, [r0, #__SIZEOF_POINTER__]
+ ldr pc, [r0, #ART_METHOD_QUICK_CODE_OFFSET_32]
+.Lconflict_trampoline:
+ // Call the runtime stub to populate the ImtConflictTable and jump to the
+ // resolved method.
INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
END art_quick_imt_conflict_trampoline
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 65d5b463c7..8b497fe579 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1757,12 +1757,37 @@ ENTRY art_quick_proxy_invoke_handler
END art_quick_proxy_invoke_handler
/*
- * Called to resolve an imt conflict. xIP1 is a hidden argument that holds the target method's
- * dex method index.
+ * Called to resolve an imt conflict.
+ * x0 is the conflict ArtMethod.
+ * xIP1 is a hidden argument that holds the target interface method's dex method index.
+ *
+ * Note that this stub writes to xIP0, xIP1, and x0.
*/
.extern artInvokeInterfaceTrampoline
ENTRY art_quick_imt_conflict_trampoline
- mov x0, xIP1
+ ldr xIP0, [sp, #0] // Load referrer
+ ldr xIP0, [xIP0, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_64] // Load dex cache methods array
+ ldr xIP0, [xIP0, xIP1, lsl #POINTER_SIZE_SHIFT] // Load interface method
+ ldr xIP1, [x0, #ART_METHOD_JNI_OFFSET_64] // Load ImtConflictTable
+ ldr x0, [xIP1] // Load first entry in ImtConflictTable.
+.Limt_table_iterate:
+ cmp x0, xIP0
+ // Branch if found. Benchmarks have shown doing a branch here is better.
+ beq .Limt_table_found
+ // If the entry is null, the interface method is not in the ImtConflictTable.
+ cbz x0, .Lconflict_trampoline
+ // Iterate over the entries of the ImtConflictTable.
+ ldr x0, [xIP1, #(2 * __SIZEOF_POINTER__)]!
+ b .Limt_table_iterate
+.Limt_table_found:
+ // We successuflly hit an entry in the table. Load the target method
+ // and jump to it.
+ ldr x0, [xIP1, #__SIZEOF_POINTER__]
+ ldr xIP0, [x0, #ART_METHOD_QUICK_CODE_OFFSET_64]
+ br xIP0
+.Lconflict_trampoline:
+ // Call the runtime stub to populate the ImtConflictTable and jump to the
+ // resolved method.
INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
END art_quick_imt_conflict_trampoline
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 125570d5bb..82ac5749b2 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1402,11 +1402,42 @@ DEFINE_FUNCTION art_quick_proxy_invoke_handler
END_FUNCTION art_quick_proxy_invoke_handler
/*
- * Called to resolve an imt conflict. xmm7 is a hidden argument that holds the target method's
- * dex method index.
+ * Called to resolve an imt conflict.
+ * eax is the conflict ArtMethod.
+ * xmm7 is a hidden argument that holds the target interface method's dex method index.
+ *
+ * Note that this stub writes to eax.
+ * Because of lack of free registers, it also saves and restores edi.
*/
DEFINE_FUNCTION art_quick_imt_conflict_trampoline
+ PUSH EDI
+ movl 8(%esp), %edi // Load referrer
+ movl ART_METHOD_DEX_CACHE_METHODS_OFFSET_32(%edi), %edi // Load dex cache methods array
+ pushl ART_METHOD_JNI_OFFSET_32(%eax) // Push ImtConflictTable.
+ CFI_ADJUST_CFA_OFFSET(4)
movd %xmm7, %eax // get target method index stored in xmm7
+ movl 0(%edi, %eax, __SIZEOF_POINTER__), %edi // Load interface method
+ popl %eax // Pop ImtConflictTable.
+ CFI_ADJUST_CFA_OFFSET(-4)
+.Limt_table_iterate:
+ cmpl %edi, 0(%eax)
+ jne .Limt_table_next_entry
+ // We successuflly hit an entry in the table. Load the target method
+ // and jump to it.
+ POP EDI
+ movl __SIZEOF_POINTER__(%eax), %eax
+ jmp *ART_METHOD_QUICK_CODE_OFFSET_32(%eax)
+.Limt_table_next_entry:
+ // If the entry is null, the interface method is not in the ImtConflictTable.
+ cmpl LITERAL(0), 0(%eax)
+ jz .Lconflict_trampoline
+ // Iterate over the entries of the ImtConflictTable.
+ addl LITERAL(2 * __SIZEOF_POINTER__), %eax
+ jmp .Limt_table_iterate
+.Lconflict_trampoline:
+ // Call the runtime stub to populate the ImtConflictTable and jump to the
+ // resolved method.
+ POP EDI
INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
END_FUNCTION art_quick_imt_conflict_trampoline
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index dee8d3c6f3..90049cc748 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1314,14 +1314,37 @@ END_FUNCTION art_quick_proxy_invoke_handler
/*
* Called to resolve an imt conflict.
- * rax is a hidden argument that holds the target method's dex method index.
+ * rdi is the conflict ArtMethod.
+ * rax is a hidden argument that holds the target interface method's dex method index.
+ *
+ * Note that this stub writes to r10 and rdi.
*/
DEFINE_FUNCTION art_quick_imt_conflict_trampoline
#if defined(__APPLE__)
int3
int3
#else
- movq %rax, %rdi
+ movq __SIZEOF_POINTER__(%rsp), %r10 // Load referrer
+ movq ART_METHOD_DEX_CACHE_METHODS_OFFSET_64(%r10), %r10 // Load dex cache methods array
+ movq 0(%r10, %rax, __SIZEOF_POINTER__), %r10 // Load interface method
+ movq ART_METHOD_JNI_OFFSET_64(%rdi), %rdi // Load ImtConflictTable
+.Limt_table_iterate:
+ cmpq %r10, 0(%rdi)
+ jne .Limt_table_next_entry
+ // We successuflly hit an entry in the table. Load the target method
+ // and jump to it.
+ movq __SIZEOF_POINTER__(%rdi), %rdi
+ jmp *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi)
+.Limt_table_next_entry:
+ // If the entry is null, the interface method is not in the ImtConflictTable.
+ cmpq LITERAL(0), 0(%rdi)
+ jz .Lconflict_trampoline
+ // Iterate over the entries of the ImtConflictTable.
+ addq LITERAL(2 * __SIZEOF_POINTER__), %rdi
+ jmp .Limt_table_iterate
+.Lconflict_trampoline:
+ // Call the runtime stub to populate the ImtConflictTable and jump to the
+ // resolved method.
INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
#endif // __APPLE__
END_FUNCTION art_quick_imt_conflict_trampoline
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 8541210791..6449efad78 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -264,13 +264,6 @@ inline bool ArtMethod::IsResolutionMethod() {
return result;
}
-inline bool ArtMethod::IsImtConflictMethod() {
- bool result = this == Runtime::Current()->GetImtConflictMethod();
- // Check that if we do think it is phony it looks like the imt conflict method.
- DCHECK(!result || IsRuntimeMethod());
- return result;
-}
-
inline bool ArtMethod::IsImtUnimplementedMethod() {
bool result = this == Runtime::Current()->GetImtUnimplementedMethod();
// Check that if we do think it is phony it looks like the imt unimplemented method.
@@ -463,7 +456,10 @@ void ArtMethod::VisitRoots(RootVisitorType& visitor, size_t pointer_size) {
interface_method->VisitRoots(visitor, pointer_size);
}
visitor.VisitRoot(declaring_class_.AddressWithoutBarrier());
- if (!IsNative()) {
+ // Runtime methods and native methods use the same field as the profiling info for
+ // storing their own data (jni entrypoint for native methods, and ImtConflictTable for
+ // some runtime methods).
+ if (!IsNative() && !IsRuntimeMethod()) {
ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size);
if (profiling_info != nullptr) {
profiling_info->VisitRoots(visitor);
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 5ca362ce2c..3dbcd58f05 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -44,6 +44,76 @@ class Class;
class PointerArray;
} // namespace mirror
+// Table to resolve IMT conflicts at runtime. The table is attached to
+// the jni entrypoint of IMT conflict ArtMethods.
+// The table contains a list of pairs of { interface_method, implementation_method }
+// with the last entry being null to make an assembly implementation of a lookup
+// faster.
+class ImtConflictTable {
+ public:
+ // Build a new table copying `other` and adding the new entry formed of
+ // the pair { `interface_method`, `implementation_method` }
+ ImtConflictTable(ImtConflictTable* other,
+ ArtMethod* interface_method,
+ ArtMethod* implementation_method) {
+ size_t index = 0;
+ while (other->entries_[index].interface_method != nullptr) {
+ entries_[index] = other->entries_[index];
+ index++;
+ }
+ entries_[index].interface_method = interface_method;
+ entries_[index].implementation_method = implementation_method;
+ // Add the null marker.
+ entries_[index + 1].interface_method = nullptr;
+ entries_[index + 1].implementation_method = nullptr;
+ }
+
+ // Lookup the implementation ArtMethod associated to `interface_method`. Return null
+ // if not found.
+ ArtMethod* Lookup(ArtMethod* interface_method) const {
+ uint32_t table_index = 0;
+ ArtMethod* current_interface_method;
+ while ((current_interface_method = entries_[table_index].interface_method) != nullptr) {
+ if (current_interface_method == interface_method) {
+ return entries_[table_index].implementation_method;
+ }
+ table_index++;
+ }
+ return nullptr;
+ }
+
+ // Compute the size in bytes taken by this table.
+ size_t ComputeSize() const {
+ uint32_t table_index = 0;
+ size_t total_size = 0;
+ while ((entries_[table_index].interface_method) != nullptr) {
+ total_size += sizeof(Entry);
+ table_index++;
+ }
+ // Add the end marker.
+ return total_size + sizeof(Entry);
+ }
+
+ // Compute the size in bytes needed for copying the given `table` and add
+ // one more entry.
+ static size_t ComputeSizeWithOneMoreEntry(ImtConflictTable* table) {
+ return table->ComputeSize() + sizeof(Entry);
+ }
+
+ struct Entry {
+ ArtMethod* interface_method;
+ ArtMethod* implementation_method;
+ };
+
+ private:
+ // Array of entries that the assembly stubs will iterate over. Note that this is
+ // not fixed size, and we allocate data prior to calling the constructor
+ // of ImtConflictTable.
+ Entry entries_[0];
+
+ DISALLOW_COPY_AND_ASSIGN(ImtConflictTable);
+};
+
class ArtMethod FINAL {
public:
ArtMethod() : access_flags_(0), dex_code_item_offset_(0), dex_method_index_(0),
@@ -338,6 +408,15 @@ class ArtMethod FINAL {
return reinterpret_cast<ProfilingInfo*>(GetEntryPointFromJniPtrSize(pointer_size));
}
+ ImtConflictTable* GetImtConflictTable(size_t pointer_size) {
+ DCHECK(IsRuntimeMethod());
+ return reinterpret_cast<ImtConflictTable*>(GetEntryPointFromJniPtrSize(pointer_size));
+ }
+
+ ALWAYS_INLINE void SetImtConflictTable(ImtConflictTable* table) {
+ SetEntryPointFromJniPtrSize(table, sizeof(void*));
+ }
+
ALWAYS_INLINE void SetProfilingInfo(ProfilingInfo* info) {
SetEntryPointFromJniPtrSize(info, sizeof(void*));
}
@@ -376,8 +455,6 @@ class ArtMethod FINAL {
bool IsResolutionMethod() SHARED_REQUIRES(Locks::mutator_lock_);
- bool IsImtConflictMethod() SHARED_REQUIRES(Locks::mutator_lock_);
-
bool IsImtUnimplementedMethod() SHARED_REQUIRES(Locks::mutator_lock_);
MethodReference ToMethodReference() SHARED_REQUIRES(Locks::mutator_lock_) {
@@ -537,7 +614,7 @@ class ArtMethod FINAL {
GcRoot<mirror::Class>* dex_cache_resolved_types_;
// Pointer to JNI function registered to this method, or a function to resolve the JNI function,
- // or the profiling data for non-native methods.
+ // or the profiling data for non-native methods, or an ImtConflictTable.
void* entry_point_from_jni_;
// Method dispatch from quick compiled code invokes this pointer which may cause bridging into
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 1f24f45fd0..942f9de0b5 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -299,6 +299,14 @@ ADD_TEST_EQ(ART_METHOD_DEX_CACHE_TYPES_OFFSET_32,
ADD_TEST_EQ(ART_METHOD_DEX_CACHE_TYPES_OFFSET_64,
art::ArtMethod::DexCacheResolvedTypesOffset(8).Int32Value())
+#define ART_METHOD_JNI_OFFSET_32 28
+ADD_TEST_EQ(ART_METHOD_JNI_OFFSET_32,
+ art::ArtMethod::EntryPointFromJniOffset(4).Int32Value())
+
+#define ART_METHOD_JNI_OFFSET_64 40
+ADD_TEST_EQ(ART_METHOD_JNI_OFFSET_64,
+ art::ArtMethod::EntryPointFromJniOffset(8).Int32Value())
+
#define ART_METHOD_QUICK_CODE_OFFSET_32 32
ADD_TEST_EQ(ART_METHOD_QUICK_CODE_OFFSET_32,
art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value())
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index c1115da552..99e38d9fcf 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -498,10 +498,11 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b
object_array_string->SetComponentType(java_lang_String.Get());
SetClassRoot(kJavaLangStringArrayClass, object_array_string.Get());
+ LinearAlloc* linear_alloc = runtime->GetLinearAlloc();
// Create runtime resolution and imt conflict methods.
runtime->SetResolutionMethod(runtime->CreateResolutionMethod());
- runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod());
- runtime->SetImtUnimplementedMethod(runtime->CreateImtConflictMethod());
+ runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod(linear_alloc));
+ runtime->SetImtUnimplementedMethod(runtime->CreateImtConflictMethod(linear_alloc));
// Setup boot_class_path_ and register class_path now that we can use AllocObjectArray to create
// DexCache instances. Needs to be after String, Field, Method arrays since AllocDexCache uses
@@ -5904,9 +5905,11 @@ static void SetIMTRef(ArtMethod* unimplemented_method,
// Place method in imt if entry is empty, place conflict otherwise.
if (*imt_ref == unimplemented_method) {
*imt_ref = current_method;
- } else if (*imt_ref != imt_conflict_method) {
+ } else if (!(*imt_ref)->IsRuntimeMethod()) {
// If we are not a conflict and we have the same signature and name as the imt
// entry, it must be that we overwrote a superclass vtable entry.
+ // Note that we have checked IsRuntimeMethod, as there may be multiple different
+ // conflict methods.
MethodNameAndSignatureComparator imt_comparator(
(*imt_ref)->GetInterfaceMethodIfProxy(image_pointer_size));
if (imt_comparator.HasSameNameAndSignature(
@@ -5915,6 +5918,11 @@ static void SetIMTRef(ArtMethod* unimplemented_method,
} else {
*imt_ref = imt_conflict_method;
}
+ } else {
+ // Place the default conflict method. Note that there may be an existing conflict
+ // method in the IMT, but it could be one tailored to the super class, with a
+ // specific ImtConflictTable.
+ *imt_ref = imt_conflict_method;
}
}
@@ -7654,12 +7662,12 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self, std::vector<const DexFi
return soa.Env()->NewGlobalRef(local_ref.get());
}
-ArtMethod* ClassLinker::CreateRuntimeMethod() {
+ArtMethod* ClassLinker::CreateRuntimeMethod(LinearAlloc* linear_alloc) {
const size_t method_alignment = ArtMethod::Alignment(image_pointer_size_);
const size_t method_size = ArtMethod::Size(image_pointer_size_);
LengthPrefixedArray<ArtMethod>* method_array = AllocArtMethodArray(
Thread::Current(),
- Runtime::Current()->GetLinearAlloc(),
+ linear_alloc,
1);
ArtMethod* method = &method_array->At(0, method_size, method_alignment);
CHECK(method != nullptr);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index c368a3adb3..b4fbe1c43f 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -565,7 +565,7 @@ class ClassLinker {
REQUIRES(Locks::classlinker_classes_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
- ArtMethod* CreateRuntimeMethod();
+ ArtMethod* CreateRuntimeMethod(LinearAlloc* linear_alloc);
// Clear the ArrayClass cache. This is necessary when cleaning up for the image, as the cache
// entries are roots, but potentially not image classes.
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 3e6b4537f9..5344cdda07 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -548,7 +548,7 @@ inline ArtMethod* FindMethodFromCode(uint32_t method_idx, mirror::Object** this_
uint32_t imt_index = resolved_method->GetDexMethodIndex() % mirror::Class::kImtSize;
ArtMethod* imt_method = (*this_object)->GetClass()->GetEmbeddedImTableEntry(
imt_index, class_linker->GetImagePointerSize());
- if (!imt_method->IsImtConflictMethod() && !imt_method->IsImtUnimplementedMethod()) {
+ if (!imt_method->IsRuntimeMethod()) {
if (kIsDebugBuild) {
mirror::Class* klass = (*this_object)->GetClass();
ArtMethod* method = klass->FindVirtualMethodForInterface(
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 7005aa5548..35f2102b25 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -23,6 +23,7 @@
#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc/accounting/card_table-inl.h"
#include "interpreter/interpreter.h"
+#include "linear_alloc.h"
#include "method_reference.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache-inl.h"
@@ -2118,48 +2119,73 @@ extern "C" TwoWordReturn artInvokeVirtualTrampolineWithAccessCheck(
return artInvokeCommon<kVirtual, true>(method_idx, this_object, self, sp);
}
-// Determine target of interface dispatch. This object is known non-null.
-extern "C" TwoWordReturn artInvokeInterfaceTrampoline(uint32_t dex_method_idx,
+// Determine target of interface dispatch. This object is known non-null. First argument
+// is there for consistency but should not be used, as some architectures overwrite it
+// in the assembly trampoline.
+extern "C" TwoWordReturn artInvokeInterfaceTrampoline(uint32_t deadbeef ATTRIBUTE_UNUSED,
mirror::Object* this_object,
- Thread* self, ArtMethod** sp)
+ Thread* self,
+ ArtMethod** sp)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> cls(hs.NewHandle(this_object->GetClass()));
+
// The optimizing compiler currently does not inline methods that have an interface
// invocation. We use the outer method directly to avoid fetching a stack map, which is
// more expensive.
ArtMethod* caller_method = QuickArgumentVisitor::GetOuterMethod(sp);
DCHECK_EQ(caller_method, QuickArgumentVisitor::GetCallingMethod(sp));
+
+ // Fetch the dex_method_idx of the target interface method from the caller.
+ uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
+
+ const DexFile::CodeItem* code_item = caller_method->GetCodeItem();
+ CHECK_LT(dex_pc, code_item->insns_size_in_code_units_);
+ const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]);
+ Instruction::Code instr_code = instr->Opcode();
+ CHECK(instr_code == Instruction::INVOKE_INTERFACE ||
+ instr_code == Instruction::INVOKE_INTERFACE_RANGE)
+ << "Unexpected call into interface trampoline: " << instr->DumpString(nullptr);
+ uint32_t dex_method_idx;
+ if (instr_code == Instruction::INVOKE_INTERFACE) {
+ dex_method_idx = instr->VRegB_35c();
+ } else {
+ CHECK_EQ(instr_code, Instruction::INVOKE_INTERFACE_RANGE);
+ dex_method_idx = instr->VRegB_3rc();
+ }
+
ArtMethod* interface_method = caller_method->GetDexCacheResolvedMethod(
dex_method_idx, sizeof(void*));
DCHECK(interface_method != nullptr) << dex_method_idx << " " << PrettyMethod(caller_method);
- ArtMethod* method;
+ ArtMethod* method = nullptr;
+
if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex)) {
- method = this_object->GetClass()->FindVirtualMethodForInterface(
- interface_method, sizeof(void*));
+ // If the dex cache already resolved the interface method, look whether we have
+ // a match in the ImtConflictTable.
+ uint32_t imt_index = interface_method->GetDexMethodIndex();
+ ArtMethod* conflict_method = cls->GetEmbeddedImTableEntry(
+ imt_index % mirror::Class::kImtSize, sizeof(void*));
+ DCHECK(conflict_method->IsRuntimeMethod()) << PrettyMethod(conflict_method);
+ ImtConflictTable* current_table = conflict_method->GetImtConflictTable(sizeof(void*));
+ method = current_table->Lookup(interface_method);
+ if (method != nullptr) {
+ return GetTwoWordSuccessValue(
+ reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCode()),
+ reinterpret_cast<uintptr_t>(method));
+ }
+
+ // No match, use the IfTable.
+ method = cls->FindVirtualMethodForInterface(interface_method, sizeof(void*));
if (UNLIKELY(method == nullptr)) {
ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(
interface_method, this_object, caller_method);
return GetTwoWordFailureValue(); // Failure.
}
} else {
+ // The dex cache did not resolve the method, look it up in the dex file
+ // of the caller,
DCHECK_EQ(interface_method, Runtime::Current()->GetResolutionMethod());
- if (kIsDebugBuild) {
- uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
- const DexFile::CodeItem* code = caller_method->GetCodeItem();
- CHECK_LT(dex_pc, code->insns_size_in_code_units_);
- const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
- Instruction::Code instr_code = instr->Opcode();
- CHECK(instr_code == Instruction::INVOKE_INTERFACE ||
- instr_code == Instruction::INVOKE_INTERFACE_RANGE)
- << "Unexpected call into interface trampoline: " << instr->DumpString(nullptr);
- if (instr_code == Instruction::INVOKE_INTERFACE) {
- CHECK_EQ(dex_method_idx, instr->VRegB_35c());
- } else {
- CHECK_EQ(instr_code, Instruction::INVOKE_INTERFACE_RANGE);
- CHECK_EQ(dex_method_idx, instr->VRegB_3rc());
- }
- }
-
const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache()
->GetDexFile();
uint32_t shorty_len;
@@ -2179,7 +2205,50 @@ extern "C" TwoWordReturn artInvokeInterfaceTrampoline(uint32_t dex_method_idx,
CHECK(self->IsExceptionPending());
return GetTwoWordFailureValue(); // Failure.
}
+ interface_method = caller_method->GetDexCacheResolvedMethod(dex_method_idx, sizeof(void*));
+ DCHECK(!interface_method->IsRuntimeMethod());
+ }
+
+ // We arrive here if we have found an implementation, and it is not in the ImtConflictTable.
+ // We create a new table with the new pair { interface_method, method }.
+ uint32_t imt_index = interface_method->GetDexMethodIndex();
+ ArtMethod* conflict_method = cls->GetEmbeddedImTableEntry(
+ imt_index % mirror::Class::kImtSize, sizeof(void*));
+ ImtConflictTable* current_table = conflict_method->GetImtConflictTable(sizeof(void*));
+ Runtime* runtime = Runtime::Current();
+ LinearAlloc* linear_alloc = (cls->GetClassLoader() == nullptr)
+ ? runtime->GetLinearAlloc()
+ : cls->GetClassLoader()->GetAllocator();
+ bool is_new_entry = (conflict_method == runtime->GetImtConflictMethod());
+
+ // Create a new entry if the existing one is the shared conflict method.
+ ArtMethod* new_conflict_method = is_new_entry
+ ? runtime->CreateImtConflictMethod(linear_alloc)
+ : conflict_method;
+
+ // Allocate a new table. Note that we will leak this table at the next conflict,
+ // but that's a tradeoff compared to making the table fixed size.
+ void* data = linear_alloc->Alloc(
+ self, ImtConflictTable::ComputeSizeWithOneMoreEntry(current_table));
+ CHECK(data != nullptr) << "Out of memory";
+ ImtConflictTable* new_table = new (data) ImtConflictTable(
+ current_table, interface_method, method);
+
+ // Do a fence to ensure threads see the data in the table before it is assigned
+ // to the conlict method.
+ // Note that there is a race in the presence of multiple threads and we may leak
+ // memory from the LinearAlloc, but that's a tradeoff compared to using
+ // atomic operations.
+ QuasiAtomic::ThreadFenceRelease();
+ new_conflict_method->SetImtConflictTable(new_table);
+ if (is_new_entry) {
+ // Update the IMT if we create a new conflict method. No fence needed here, as the
+ // data is consistent.
+ cls->SetEmbeddedImTableEntry(imt_index % mirror::Class::kImtSize,
+ new_conflict_method,
+ sizeof(void*));
}
+
const void* code = method->GetEntryPointFromQuickCompiledCode();
// When we return, the caller will branch to this address, so it had better not be 0!
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 1d64d6d04f..941217f24d 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1596,8 +1596,10 @@ void Runtime::VisitImageRoots(RootVisitor* visitor) {
}
}
-ArtMethod* Runtime::CreateImtConflictMethod() {
- auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod();
+static ImtConflictTable::Entry empty_entry = { nullptr, nullptr };
+
+ArtMethod* Runtime::CreateImtConflictMethod(LinearAlloc* linear_alloc) {
+ auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod(linear_alloc);
// When compiling, the code pointer will get set later when the image is loaded.
if (IsAotCompiler()) {
size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
@@ -1605,6 +1607,7 @@ ArtMethod* Runtime::CreateImtConflictMethod() {
} else {
method->SetEntryPointFromQuickCompiledCode(GetQuickImtConflictStub());
}
+ method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
return method;
}
@@ -1612,10 +1615,11 @@ void Runtime::SetImtConflictMethod(ArtMethod* method) {
CHECK(method != nullptr);
CHECK(method->IsRuntimeMethod());
imt_conflict_method_ = method;
+ method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
}
ArtMethod* Runtime::CreateResolutionMethod() {
- auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod();
+ auto* method = GetClassLinker()->CreateRuntimeMethod(GetLinearAlloc());
// When compiling, the code pointer will get set later when the image is loaded.
if (IsAotCompiler()) {
size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
@@ -1627,7 +1631,7 @@ ArtMethod* Runtime::CreateResolutionMethod() {
}
ArtMethod* Runtime::CreateCalleeSaveMethod() {
- auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod();
+ auto* method = GetClassLinker()->CreateRuntimeMethod(GetLinearAlloc());
size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
method->SetEntryPointFromQuickCompiledCodePtrSize(nullptr, pointer_size);
DCHECK_NE(instruction_set_, kNone);
@@ -1929,6 +1933,7 @@ void Runtime::SetImtUnimplementedMethod(ArtMethod* method) {
CHECK(method != nullptr);
CHECK(method->IsRuntimeMethod());
imt_unimplemented_method_ = method;
+ method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
}
bool Runtime::IsVerificationEnabled() const {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index ac6e689c26..6a6fdb79db 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -386,7 +386,8 @@ class Runtime {
void SetImtConflictMethod(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
void SetImtUnimplementedMethod(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
- ArtMethod* CreateImtConflictMethod() SHARED_REQUIRES(Locks::mutator_lock_);
+ ArtMethod* CreateImtConflictMethod(LinearAlloc* linear_alloc)
+ SHARED_REQUIRES(Locks::mutator_lock_);
// Returns a special method that describes all callee saves being spilled to the stack.
enum CalleeSaveType {
diff --git a/test/469-condition-materialization-regression/expected.txt b/test/469-condition-materialization/expected.txt
index e69de29bb2..e69de29bb2 100644
--- a/test/469-condition-materialization-regression/expected.txt
+++ b/test/469-condition-materialization/expected.txt
diff --git a/test/469-condition-materialization-regression/info.txt b/test/469-condition-materialization/info.txt
index 59290f0b50..59290f0b50 100644
--- a/test/469-condition-materialization-regression/info.txt
+++ b/test/469-condition-materialization/info.txt
diff --git a/test/469-condition-materialization-regression/src/Main.java b/test/469-condition-materialization/src/Main.java
index 0be386a38f..0be386a38f 100644
--- a/test/469-condition-materialization-regression/src/Main.java
+++ b/test/469-condition-materialization/src/Main.java
diff --git a/test/589-super-imt/expected.txt b/test/589-super-imt/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/589-super-imt/expected.txt
diff --git a/test/589-super-imt/info.txt b/test/589-super-imt/info.txt
new file mode 100644
index 0000000000..c815dc9537
--- /dev/null
+++ b/test/589-super-imt/info.txt
@@ -0,0 +1,2 @@
+Test what the IMT is properly set for a subclass, and that the
+subclass won't use the ImtConflictTable table of its super class.
diff --git a/test/589-super-imt/src/Main.java b/test/589-super-imt/src/Main.java
new file mode 100644
index 0000000000..e381ca7c93
--- /dev/null
+++ b/test/589-super-imt/src/Main.java
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface Itf {
+ public Class<?> method1();
+ public Class<?> method2();
+ public Class<?> method3();
+ public Class<?> method4();
+ public Class<?> method5();
+ public Class<?> method6();
+ public Class<?> method7();
+ public Class<?> method8();
+ public Class<?> method9();
+ public Class<?> method10();
+ public Class<?> method11();
+ public Class<?> method12();
+ public Class<?> method13();
+ public Class<?> method14();
+ public Class<?> method15();
+ public Class<?> method16();
+ public Class<?> method17();
+ public Class<?> method18();
+ public Class<?> method19();
+ public Class<?> method20();
+ public Class<?> method21();
+ public Class<?> method22();
+ public Class<?> method23();
+ public Class<?> method24();
+ public Class<?> method25();
+ public Class<?> method26();
+ public Class<?> method27();
+ public Class<?> method28();
+ public Class<?> method29();
+ public Class<?> method30();
+ public Class<?> method31();
+ public Class<?> method32();
+ public Class<?> method33();
+ public Class<?> method34();
+ public Class<?> method35();
+ public Class<?> method36();
+ public Class<?> method37();
+ public Class<?> method38();
+ public Class<?> method39();
+ public Class<?> method40();
+ public Class<?> method41();
+ public Class<?> method42();
+ public Class<?> method43();
+ public Class<?> method44();
+ public Class<?> method45();
+ public Class<?> method46();
+ public Class<?> method47();
+ public Class<?> method48();
+ public Class<?> method49();
+ public Class<?> method50();
+ public Class<?> method51();
+ public Class<?> method52();
+ public Class<?> method53();
+ public Class<?> method54();
+ public Class<?> method55();
+ public Class<?> method56();
+ public Class<?> method57();
+ public Class<?> method58();
+ public Class<?> method59();
+ public Class<?> method60();
+ public Class<?> method61();
+ public Class<?> method62();
+ public Class<?> method63();
+ public Class<?> method64();
+ public Class<?> method65();
+ public Class<?> method66();
+ public Class<?> method67();
+ public Class<?> method68();
+ public Class<?> method69();
+ public Class<?> method70();
+ public Class<?> method71();
+ public Class<?> method72();
+ public Class<?> method73();
+ public Class<?> method74();
+ public Class<?> method75();
+ public Class<?> method76();
+ public Class<?> method77();
+ public Class<?> method78();
+ public Class<?> method79();
+}
+
+public class Main implements Itf {
+ public static Itf main;
+ public static void main(String[] args) {
+ main = new Main();
+ callMains();
+ main = new SubMain();
+ callSubMains();
+ }
+
+ public static void callMains() {
+ // We loop to artificially create branches. The compiler will
+ // not compile this method otherwise.
+ for (int i = 0; i < 2; ++i) {
+ expectEquals(main.method1(), Main.class);
+ expectEquals(main.method2(), Main.class);
+ expectEquals(main.method3(), Main.class);
+ expectEquals(main.method4(), Main.class);
+ expectEquals(main.method5(), Main.class);
+ expectEquals(main.method6(), Main.class);
+ expectEquals(main.method7(), Main.class);
+ expectEquals(main.method8(), Main.class);
+ expectEquals(main.method9(), Main.class);
+ expectEquals(main.method10(), Main.class);
+ expectEquals(main.method11(), Main.class);
+ expectEquals(main.method12(), Main.class);
+ expectEquals(main.method13(), Main.class);
+ expectEquals(main.method14(), Main.class);
+ expectEquals(main.method15(), Main.class);
+ expectEquals(main.method16(), Main.class);
+ expectEquals(main.method17(), Main.class);
+ expectEquals(main.method18(), Main.class);
+ expectEquals(main.method19(), Main.class);
+ expectEquals(main.method20(), Main.class);
+ expectEquals(main.method21(), Main.class);
+ expectEquals(main.method22(), Main.class);
+ expectEquals(main.method23(), Main.class);
+ expectEquals(main.method24(), Main.class);
+ expectEquals(main.method25(), Main.class);
+ expectEquals(main.method26(), Main.class);
+ expectEquals(main.method27(), Main.class);
+ expectEquals(main.method28(), Main.class);
+ expectEquals(main.method29(), Main.class);
+ expectEquals(main.method30(), Main.class);
+ expectEquals(main.method31(), Main.class);
+ expectEquals(main.method32(), Main.class);
+ expectEquals(main.method33(), Main.class);
+ expectEquals(main.method34(), Main.class);
+ expectEquals(main.method35(), Main.class);
+ expectEquals(main.method36(), Main.class);
+ expectEquals(main.method37(), Main.class);
+ expectEquals(main.method38(), Main.class);
+ expectEquals(main.method39(), Main.class);
+ expectEquals(main.method40(), Main.class);
+ expectEquals(main.method41(), Main.class);
+ expectEquals(main.method42(), Main.class);
+ expectEquals(main.method43(), Main.class);
+ expectEquals(main.method44(), Main.class);
+ expectEquals(main.method45(), Main.class);
+ expectEquals(main.method46(), Main.class);
+ expectEquals(main.method47(), Main.class);
+ expectEquals(main.method48(), Main.class);
+ expectEquals(main.method49(), Main.class);
+ expectEquals(main.method50(), Main.class);
+ expectEquals(main.method51(), Main.class);
+ expectEquals(main.method52(), Main.class);
+ expectEquals(main.method53(), Main.class);
+ expectEquals(main.method54(), Main.class);
+ expectEquals(main.method55(), Main.class);
+ expectEquals(main.method56(), Main.class);
+ expectEquals(main.method57(), Main.class);
+ expectEquals(main.method58(), Main.class);
+ expectEquals(main.method59(), Main.class);
+ expectEquals(main.method60(), Main.class);
+ expectEquals(main.method61(), Main.class);
+ expectEquals(main.method62(), Main.class);
+ expectEquals(main.method63(), Main.class);
+ expectEquals(main.method64(), Main.class);
+ expectEquals(main.method65(), Main.class);
+ expectEquals(main.method66(), Main.class);
+ expectEquals(main.method67(), Main.class);
+ expectEquals(main.method68(), Main.class);
+ expectEquals(main.method69(), Main.class);
+ expectEquals(main.method70(), Main.class);
+ expectEquals(main.method71(), Main.class);
+ expectEquals(main.method72(), Main.class);
+ expectEquals(main.method73(), Main.class);
+ expectEquals(main.method74(), Main.class);
+ expectEquals(main.method75(), Main.class);
+ expectEquals(main.method76(), Main.class);
+ expectEquals(main.method77(), Main.class);
+ expectEquals(main.method78(), Main.class);
+ expectEquals(main.method79(), Main.class);
+ }
+ }
+
+ public static void callSubMains() {
+ // We loop to artificially create branches. The compiler will
+ // not compile this method otherwise.
+ for (int i = 0; i < 2; ++i) {
+ expectEquals(main.method1(), SubMain.class);
+ expectEquals(main.method2(), SubMain.class);
+ expectEquals(main.method3(), SubMain.class);
+ expectEquals(main.method4(), SubMain.class);
+ expectEquals(main.method5(), SubMain.class);
+ expectEquals(main.method6(), SubMain.class);
+ expectEquals(main.method7(), SubMain.class);
+ expectEquals(main.method8(), SubMain.class);
+ expectEquals(main.method9(), SubMain.class);
+ expectEquals(main.method10(), SubMain.class);
+ expectEquals(main.method11(), SubMain.class);
+ expectEquals(main.method12(), SubMain.class);
+ expectEquals(main.method13(), SubMain.class);
+ expectEquals(main.method14(), SubMain.class);
+ expectEquals(main.method15(), SubMain.class);
+ expectEquals(main.method16(), SubMain.class);
+ expectEquals(main.method17(), SubMain.class);
+ expectEquals(main.method18(), SubMain.class);
+ expectEquals(main.method19(), SubMain.class);
+ expectEquals(main.method20(), SubMain.class);
+ expectEquals(main.method21(), SubMain.class);
+ expectEquals(main.method22(), SubMain.class);
+ expectEquals(main.method23(), SubMain.class);
+ expectEquals(main.method24(), SubMain.class);
+ expectEquals(main.method25(), SubMain.class);
+ expectEquals(main.method26(), SubMain.class);
+ expectEquals(main.method27(), SubMain.class);
+ expectEquals(main.method28(), SubMain.class);
+ expectEquals(main.method29(), SubMain.class);
+ expectEquals(main.method30(), SubMain.class);
+ expectEquals(main.method31(), SubMain.class);
+ expectEquals(main.method32(), SubMain.class);
+ expectEquals(main.method33(), SubMain.class);
+ expectEquals(main.method34(), SubMain.class);
+ expectEquals(main.method35(), SubMain.class);
+ expectEquals(main.method36(), SubMain.class);
+ expectEquals(main.method37(), SubMain.class);
+ expectEquals(main.method38(), SubMain.class);
+ expectEquals(main.method39(), SubMain.class);
+ expectEquals(main.method40(), SubMain.class);
+ expectEquals(main.method41(), SubMain.class);
+ expectEquals(main.method42(), SubMain.class);
+ expectEquals(main.method43(), SubMain.class);
+ expectEquals(main.method44(), SubMain.class);
+ expectEquals(main.method45(), SubMain.class);
+ expectEquals(main.method46(), SubMain.class);
+ expectEquals(main.method47(), SubMain.class);
+ expectEquals(main.method48(), SubMain.class);
+ expectEquals(main.method49(), SubMain.class);
+ expectEquals(main.method50(), SubMain.class);
+ expectEquals(main.method51(), SubMain.class);
+ expectEquals(main.method52(), SubMain.class);
+ expectEquals(main.method53(), SubMain.class);
+ expectEquals(main.method54(), SubMain.class);
+ expectEquals(main.method55(), SubMain.class);
+ expectEquals(main.method56(), SubMain.class);
+ expectEquals(main.method57(), SubMain.class);
+ expectEquals(main.method58(), SubMain.class);
+ expectEquals(main.method59(), SubMain.class);
+ expectEquals(main.method60(), SubMain.class);
+ expectEquals(main.method61(), SubMain.class);
+ expectEquals(main.method62(), SubMain.class);
+ expectEquals(main.method63(), SubMain.class);
+ expectEquals(main.method64(), SubMain.class);
+ expectEquals(main.method65(), SubMain.class);
+ expectEquals(main.method66(), SubMain.class);
+ expectEquals(main.method67(), SubMain.class);
+ expectEquals(main.method68(), SubMain.class);
+ expectEquals(main.method69(), SubMain.class);
+ expectEquals(main.method70(), SubMain.class);
+ expectEquals(main.method71(), SubMain.class);
+ expectEquals(main.method72(), SubMain.class);
+ expectEquals(main.method73(), SubMain.class);
+ expectEquals(main.method74(), SubMain.class);
+ expectEquals(main.method75(), SubMain.class);
+ expectEquals(main.method76(), SubMain.class);
+ expectEquals(main.method77(), SubMain.class);
+ expectEquals(main.method78(), SubMain.class);
+ expectEquals(main.method79(), SubMain.class);
+ }
+ }
+
+ public static void expectEquals(Object actual, Object expected) {
+ if (!actual.equals(expected)) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public Class<?> method1() { return Main.class; }
+ public Class<?> method2() { return Main.class; }
+ public Class<?> method3() { return Main.class; }
+ public Class<?> method4() { return Main.class; }
+ public Class<?> method5() { return Main.class; }
+ public Class<?> method6() { return Main.class; }
+ public Class<?> method7() { return Main.class; }
+ public Class<?> method8() { return Main.class; }
+ public Class<?> method9() { return Main.class; }
+ public Class<?> method10() { return Main.class; }
+ public Class<?> method11() { return Main.class; }
+ public Class<?> method12() { return Main.class; }
+ public Class<?> method13() { return Main.class; }
+ public Class<?> method14() { return Main.class; }
+ public Class<?> method15() { return Main.class; }
+ public Class<?> method16() { return Main.class; }
+ public Class<?> method17() { return Main.class; }
+ public Class<?> method18() { return Main.class; }
+ public Class<?> method19() { return Main.class; }
+ public Class<?> method20() { return Main.class; }
+ public Class<?> method21() { return Main.class; }
+ public Class<?> method22() { return Main.class; }
+ public Class<?> method23() { return Main.class; }
+ public Class<?> method24() { return Main.class; }
+ public Class<?> method25() { return Main.class; }
+ public Class<?> method26() { return Main.class; }
+ public Class<?> method27() { return Main.class; }
+ public Class<?> method28() { return Main.class; }
+ public Class<?> method29() { return Main.class; }
+ public Class<?> method30() { return Main.class; }
+ public Class<?> method31() { return Main.class; }
+ public Class<?> method32() { return Main.class; }
+ public Class<?> method33() { return Main.class; }
+ public Class<?> method34() { return Main.class; }
+ public Class<?> method35() { return Main.class; }
+ public Class<?> method36() { return Main.class; }
+ public Class<?> method37() { return Main.class; }
+ public Class<?> method38() { return Main.class; }
+ public Class<?> method39() { return Main.class; }
+ public Class<?> method40() { return Main.class; }
+ public Class<?> method41() { return Main.class; }
+ public Class<?> method42() { return Main.class; }
+ public Class<?> method43() { return Main.class; }
+ public Class<?> method44() { return Main.class; }
+ public Class<?> method45() { return Main.class; }
+ public Class<?> method46() { return Main.class; }
+ public Class<?> method47() { return Main.class; }
+ public Class<?> method48() { return Main.class; }
+ public Class<?> method49() { return Main.class; }
+ public Class<?> method50() { return Main.class; }
+ public Class<?> method51() { return Main.class; }
+ public Class<?> method52() { return Main.class; }
+ public Class<?> method53() { return Main.class; }
+ public Class<?> method54() { return Main.class; }
+ public Class<?> method55() { return Main.class; }
+ public Class<?> method56() { return Main.class; }
+ public Class<?> method57() { return Main.class; }
+ public Class<?> method58() { return Main.class; }
+ public Class<?> method59() { return Main.class; }
+ public Class<?> method60() { return Main.class; }
+ public Class<?> method61() { return Main.class; }
+ public Class<?> method62() { return Main.class; }
+ public Class<?> method63() { return Main.class; }
+ public Class<?> method64() { return Main.class; }
+ public Class<?> method65() { return Main.class; }
+ public Class<?> method66() { return Main.class; }
+ public Class<?> method67() { return Main.class; }
+ public Class<?> method68() { return Main.class; }
+ public Class<?> method69() { return Main.class; }
+ public Class<?> method70() { return Main.class; }
+ public Class<?> method71() { return Main.class; }
+ public Class<?> method72() { return Main.class; }
+ public Class<?> method73() { return Main.class; }
+ public Class<?> method74() { return Main.class; }
+ public Class<?> method75() { return Main.class; }
+ public Class<?> method76() { return Main.class; }
+ public Class<?> method77() { return Main.class; }
+ public Class<?> method78() { return Main.class; }
+ public Class<?> method79() { return Main.class; }
+}
+
+class SubMain extends Main {
+ public Class<?> method1() { return SubMain.class; }
+ public Class<?> method2() { return SubMain.class; }
+ public Class<?> method3() { return SubMain.class; }
+ public Class<?> method4() { return SubMain.class; }
+ public Class<?> method5() { return SubMain.class; }
+ public Class<?> method6() { return SubMain.class; }
+ public Class<?> method7() { return SubMain.class; }
+ public Class<?> method8() { return SubMain.class; }
+ public Class<?> method9() { return SubMain.class; }
+ public Class<?> method10() { return SubMain.class; }
+ public Class<?> method11() { return SubMain.class; }
+ public Class<?> method12() { return SubMain.class; }
+ public Class<?> method13() { return SubMain.class; }
+ public Class<?> method14() { return SubMain.class; }
+ public Class<?> method15() { return SubMain.class; }
+ public Class<?> method16() { return SubMain.class; }
+ public Class<?> method17() { return SubMain.class; }
+ public Class<?> method18() { return SubMain.class; }
+ public Class<?> method19() { return SubMain.class; }
+ public Class<?> method20() { return SubMain.class; }
+ public Class<?> method21() { return SubMain.class; }
+ public Class<?> method22() { return SubMain.class; }
+ public Class<?> method23() { return SubMain.class; }
+ public Class<?> method24() { return SubMain.class; }
+ public Class<?> method25() { return SubMain.class; }
+ public Class<?> method26() { return SubMain.class; }
+ public Class<?> method27() { return SubMain.class; }
+ public Class<?> method28() { return SubMain.class; }
+ public Class<?> method29() { return SubMain.class; }
+ public Class<?> method30() { return SubMain.class; }
+ public Class<?> method31() { return SubMain.class; }
+ public Class<?> method32() { return SubMain.class; }
+ public Class<?> method33() { return SubMain.class; }
+ public Class<?> method34() { return SubMain.class; }
+ public Class<?> method35() { return SubMain.class; }
+ public Class<?> method36() { return SubMain.class; }
+ public Class<?> method37() { return SubMain.class; }
+ public Class<?> method38() { return SubMain.class; }
+ public Class<?> method39() { return SubMain.class; }
+ public Class<?> method40() { return SubMain.class; }
+ public Class<?> method41() { return SubMain.class; }
+ public Class<?> method42() { return SubMain.class; }
+ public Class<?> method43() { return SubMain.class; }
+ public Class<?> method44() { return SubMain.class; }
+ public Class<?> method45() { return SubMain.class; }
+ public Class<?> method46() { return SubMain.class; }
+ public Class<?> method47() { return SubMain.class; }
+ public Class<?> method48() { return SubMain.class; }
+ public Class<?> method49() { return SubMain.class; }
+ public Class<?> method50() { return SubMain.class; }
+ public Class<?> method51() { return SubMain.class; }
+ public Class<?> method52() { return SubMain.class; }
+ public Class<?> method53() { return SubMain.class; }
+ public Class<?> method54() { return SubMain.class; }
+ public Class<?> method55() { return SubMain.class; }
+ public Class<?> method56() { return SubMain.class; }
+ public Class<?> method57() { return SubMain.class; }
+ public Class<?> method58() { return SubMain.class; }
+ public Class<?> method59() { return SubMain.class; }
+ public Class<?> method60() { return SubMain.class; }
+ public Class<?> method61() { return SubMain.class; }
+ public Class<?> method62() { return SubMain.class; }
+ public Class<?> method63() { return SubMain.class; }
+ public Class<?> method64() { return SubMain.class; }
+ public Class<?> method65() { return SubMain.class; }
+ public Class<?> method66() { return SubMain.class; }
+ public Class<?> method67() { return SubMain.class; }
+ public Class<?> method68() { return SubMain.class; }
+ public Class<?> method69() { return SubMain.class; }
+ public Class<?> method70() { return SubMain.class; }
+ public Class<?> method71() { return SubMain.class; }
+ public Class<?> method72() { return SubMain.class; }
+ public Class<?> method73() { return SubMain.class; }
+ public Class<?> method74() { return SubMain.class; }
+ public Class<?> method75() { return SubMain.class; }
+ public Class<?> method76() { return SubMain.class; }
+ public Class<?> method77() { return SubMain.class; }
+ public Class<?> method78() { return SubMain.class; }
+ public Class<?> method79() { return SubMain.class; }
+}