diff options
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; } +} |