summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Treehugger Robot <treehugger-gerrit@google.com> 2017-07-20 14:09:32 +0000
committer Gerrit Code Review <noreply-gerritcodereview@google.com> 2017-07-20 14:09:32 +0000
commit331f4c4e287791611733120c1a1c2afd55ecdd65 (patch)
tree1fcf7810c6c8e2df8b6191bb14a69084f3c7cf11
parent13c8343a3394414c90f2fcd1e8efff70e7d2387e (diff)
parentba118827465d12177f3996e50133960087b1c916 (diff)
Merge "ART: Change method lookup to be more consistent to JLS and the RI."
-rw-r--r--compiler/common_compiler_test.cc8
-rw-r--r--compiler/dex/dex_to_dex_compiler.cc15
-rw-r--r--compiler/driver/compiler_driver-inl.h2
-rw-r--r--compiler/driver/compiler_driver.cc14
-rw-r--r--compiler/exception_test.cc6
-rw-r--r--compiler/image_test.cc8
-rw-r--r--compiler/jni/jni_compiler_test.cc4
-rw-r--r--compiler/oat_writer.cc6
-rw-r--r--compiler/optimizing/instruction_builder.cc64
-rw-r--r--compiler/optimizing/instruction_simplifier.cc18
-rw-r--r--compiler/optimizing/reference_type_propagation.cc2
-rw-r--r--compiler/verifier_deps_test.cc387
-rw-r--r--runtime/art_method.cc7
-rw-r--r--runtime/class_linker-inl.h151
-rw-r--r--runtime/class_linker.cc328
-rw-r--r--runtime/class_linker.h34
-rw-r--r--runtime/class_linker_test.cc38
-rw-r--r--runtime/dex_file-inl.h25
-rw-r--r--runtime/entrypoints/entrypoint_utils-inl.h84
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc15
-rw-r--r--runtime/instrumentation_test.cc26
-rw-r--r--runtime/interpreter/interpreter_common.cc6
-rw-r--r--runtime/interpreter/unstarted_runtime.cc8
-rw-r--r--runtime/interpreter/unstarted_runtime_test.cc25
-rw-r--r--runtime/jni_internal.cc11
-rw-r--r--runtime/jni_internal_test.cc4
-rw-r--r--runtime/mirror/class-inl.h6
-rw-r--r--runtime/mirror/class.cc387
-rw-r--r--runtime/mirror/class.h91
-rw-r--r--runtime/mirror/dex_cache_test.cc8
-rw-r--r--runtime/native/dalvik_system_VMRuntime.cc31
-rw-r--r--runtime/openjdkjvmti/ti_redefine.cc13
-rw-r--r--runtime/openjdkjvmti/ti_search.cc8
-rw-r--r--runtime/proxy_test.cc13
-rw-r--r--runtime/reference_table_test.cc4
-rw-r--r--runtime/reflection_test.cc13
-rw-r--r--runtime/runtime.cc3
-rw-r--r--runtime/thread.cc2
-rw-r--r--runtime/utils_test.cc9
-rw-r--r--runtime/vdex_file.h4
-rw-r--r--runtime/verifier/method_verifier.cc75
-rw-r--r--runtime/verifier/verifier_deps.cc96
-rw-r--r--runtime/verifier/verifier_deps.h13
-rw-r--r--test/162-method-resolution/expected.txt43
-rw-r--r--test/162-method-resolution/info.txt4
-rw-r--r--test/162-method-resolution/jasmin-multidex/Test1User.j26
-rw-r--r--test/162-method-resolution/jasmin-multidex/Test3User.j26
-rw-r--r--test/162-method-resolution/jasmin/Test1Derived.j43
-rw-r--r--test/162-method-resolution/jasmin/Test1User2.j26
-rw-r--r--test/162-method-resolution/jasmin/Test2Derived.j25
-rw-r--r--test/162-method-resolution/jasmin/Test2User.j26
-rw-r--r--test/162-method-resolution/jasmin/Test2User2.j23
-rw-r--r--test/162-method-resolution/jasmin/Test3Derived.j25
-rw-r--r--test/162-method-resolution/jasmin/Test4User.j29
-rw-r--r--test/162-method-resolution/jasmin/Test5User.j40
-rw-r--r--test/162-method-resolution/jasmin/Test5User2.j26
-rw-r--r--test/162-method-resolution/jasmin/Test6User.j29
-rw-r--r--test/162-method-resolution/jasmin/Test6User2.j29
-rw-r--r--test/162-method-resolution/jasmin/Test8Derived.j33
-rw-r--r--test/162-method-resolution/jasmin/Test8User.j26
-rw-r--r--test/162-method-resolution/jasmin/Test8User2.j23
-rw-r--r--test/162-method-resolution/jasmin/Test9Derived.j33
-rw-r--r--test/162-method-resolution/jasmin/Test9User.j23
-rw-r--r--test/162-method-resolution/jasmin/Test9User2.j26
-rw-r--r--test/162-method-resolution/multidex.jpp117
-rw-r--r--test/162-method-resolution/src/Main.java401
-rw-r--r--test/162-method-resolution/src/Test1Base.java (renamed from runtime/verifier/method_resolution_kind.h)24
-rw-r--r--test/162-method-resolution/src/Test2Base.java21
-rw-r--r--test/162-method-resolution/src/Test2Interface.java21
-rw-r--r--test/162-method-resolution/src/Test3Base.java21
-rw-r--r--test/162-method-resolution/src/Test3Interface.java21
-rw-r--r--test/162-method-resolution/src/Test4Derived.java18
-rw-r--r--test/162-method-resolution/src/Test4Interface.java19
-rw-r--r--test/162-method-resolution/src/Test5Base.java18
-rw-r--r--test/162-method-resolution/src/Test5Derived.java21
-rw-r--r--test/162-method-resolution/src/Test5Interface.java19
-rw-r--r--test/162-method-resolution/src/Test6Derived.java18
-rw-r--r--test/162-method-resolution/src/Test6Interface.java19
-rw-r--r--test/162-method-resolution/src/Test7Base.java21
-rw-r--r--test/162-method-resolution/src/Test7Derived.java18
-rw-r--r--test/162-method-resolution/src/Test7Interface.java21
-rw-r--r--test/162-method-resolution/src/Test7User.java21
-rw-r--r--test/162-method-resolution/src/Test7User2.java22
-rw-r--r--test/162-method-resolution/src/Test8Base.java21
-rw-r--r--test/162-method-resolution/src/Test9Base.java21
-rw-r--r--test/VerifierDeps/Main.smali12
86 files changed, 2455 insertions, 1116 deletions
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 3683695a1b..08145e202e 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -265,8 +265,8 @@ void CommonCompilerTest::CompileDirectMethod(Handle<mirror::ClassLoader> class_l
mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader);
CHECK(klass != nullptr) << "Class not found " << class_name;
auto pointer_size = class_linker_->GetImagePointerSize();
- ArtMethod* method = klass->FindDirectMethod(method_name, signature, pointer_size);
- CHECK(method != nullptr) << "Direct method not found: "
+ ArtMethod* method = klass->FindClassMethod(method_name, signature, pointer_size);
+ CHECK(method != nullptr && method->IsDirect()) << "Direct method not found: "
<< class_name << "." << method_name << signature;
CompileMethod(method);
}
@@ -279,8 +279,8 @@ void CommonCompilerTest::CompileVirtualMethod(Handle<mirror::ClassLoader> class_
mirror::Class* klass = class_linker_->FindClass(self, class_descriptor.c_str(), class_loader);
CHECK(klass != nullptr) << "Class not found " << class_name;
auto pointer_size = class_linker_->GetImagePointerSize();
- ArtMethod* method = klass->FindVirtualMethod(method_name, signature, pointer_size);
- CHECK(method != nullptr) << "Virtual method not found: "
+ ArtMethod* method = klass->FindClassMethod(method_name, signature, pointer_size);
+ CHECK(method != nullptr && !method->IsDirect()) << "Virtual method not found: "
<< class_name << "." << method_name << signature;
CompileMethod(method);
}
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index fba1136d19..9d57b965ab 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -291,13 +291,14 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc,
ScopedObjectAccess soa(Thread::Current());
ClassLinker* class_linker = unit_.GetClassLinker();
- ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::kForceICCECheck>(
- GetDexFile(),
- method_idx,
- unit_.GetDexCache(),
- unit_.GetClassLoader(),
- /* referrer */ nullptr,
- kVirtual);
+ ArtMethod* resolved_method =
+ class_linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
+ GetDexFile(),
+ method_idx,
+ unit_.GetDexCache(),
+ unit_.GetClassLoader(),
+ /* referrer */ nullptr,
+ kVirtual);
if (UNLIKELY(resolved_method == nullptr)) {
// Clean up any exception left by type resolution.
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index db95bd6e03..b04392918d 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -111,7 +111,7 @@ inline ArtMethod* CompilerDriver::ResolveMethod(
InvokeType invoke_type) {
DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
ArtMethod* resolved_method =
- mUnit->GetClassLinker()->ResolveMethod<ClassLinker::kForceICCECheck>(
+ mUnit->GetClassLinker()->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
*dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type);
if (UNLIKELY(resolved_method == nullptr)) {
DCHECK(soa.Self()->IsExceptionPending());
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index f3718c467b..cf04e41d5c 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -373,14 +373,12 @@ static void SetupIntrinsic(Thread* self,
REQUIRES_SHARED(Locks::mutator_lock_) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
PointerSize image_size = class_linker->GetImagePointerSize();
- mirror::Class* cls = class_linker->FindSystemClass(self, class_name);
+ ObjPtr<mirror::Class> cls = class_linker->FindSystemClass(self, class_name);
if (cls == nullptr) {
LOG(FATAL) << "Could not find class of intrinsic " << class_name;
}
- ArtMethod* method = (invoke_type == kStatic || invoke_type == kDirect)
- ? cls->FindDeclaredDirectMethod(method_name, signature, image_size)
- : cls->FindDeclaredVirtualMethod(method_name, signature, image_size);
- if (method == nullptr) {
+ ArtMethod* method = cls->FindClassMethod(method_name, signature, image_size);
+ if (method == nullptr || method->GetDeclaringClass() != cls) {
LOG(FATAL) << "Could not find method of intrinsic "
<< class_name << " " << method_name << " " << signature;
}
@@ -543,7 +541,7 @@ static void CompileMethod(Thread* self,
// TODO: Lookup annotation from DexFile directly without resolving method.
ArtMethod* method =
- Runtime::Current()->GetClassLinker()->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
+ Runtime::Current()->GetClassLinker()->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
dex_file,
method_idx,
dex_cache,
@@ -1755,7 +1753,7 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor {
}
if (resolve_fields_and_methods) {
while (it.HasNextDirectMethod()) {
- ArtMethod* method = class_linker->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
+ ArtMethod* method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
dex_file, it.GetMemberIndex(), dex_cache, class_loader, nullptr,
it.GetMethodInvokeType(class_def));
if (method == nullptr) {
@@ -1764,7 +1762,7 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor {
it.Next();
}
while (it.HasNextVirtualMethod()) {
- ArtMethod* method = class_linker->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
+ ArtMethod* method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
dex_file, it.GetMemberIndex(), dex_cache, class_loader, nullptr,
it.GetMethodInvokeType(class_def));
if (method == nullptr) {
diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc
index b4777df0df..0b3ca69846 100644
--- a/compiler/exception_test.cc
+++ b/compiler/exception_test.cc
@@ -102,12 +102,14 @@ class ExceptionTest : public CommonRuntimeTest {
CHECK_ALIGNED(stack_maps_offset, 2);
}
- method_f_ = my_klass_->FindVirtualMethod("f", "()I", kRuntimePointerSize);
+ method_f_ = my_klass_->FindClassMethod("f", "()I", kRuntimePointerSize);
ASSERT_TRUE(method_f_ != nullptr);
+ ASSERT_FALSE(method_f_->IsDirect());
method_f_->SetEntryPointFromQuickCompiledCode(code_ptr);
- method_g_ = my_klass_->FindVirtualMethod("g", "(I)V", kRuntimePointerSize);
+ method_g_ = my_klass_->FindClassMethod("g", "(I)V", kRuntimePointerSize);
ASSERT_TRUE(method_g_ != nullptr);
+ ASSERT_FALSE(method_g_->IsDirect());
method_g_->SetEntryPointFromQuickCompiledCode(code_ptr);
}
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 9d7aff769b..252fdd67e1 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -113,9 +113,9 @@ TEST_F(ImageTest, TestDefaultMethods) {
mirror::Class* iface_klass = class_linker_->LookupClass(
self, "LIface;", ObjPtr<mirror::ClassLoader>());
ASSERT_NE(nullptr, iface_klass);
- ArtMethod* origin = iface_klass->FindDeclaredVirtualMethod(
- "defaultMethod", "()V", pointer_size);
+ ArtMethod* origin = iface_klass->FindInterfaceMethod("defaultMethod", "()V", pointer_size);
ASSERT_NE(nullptr, origin);
+ ASSERT_TRUE(origin->GetDeclaringClass() == iface_klass);
const void* code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size);
// The origin method should have a pointer to quick code
ASSERT_NE(nullptr, code);
@@ -134,9 +134,11 @@ TEST_F(ImageTest, TestDefaultMethods) {
mirror::Class* iterable_klass = class_linker_->LookupClass(
self, "Ljava/lang/Iterable;", ObjPtr<mirror::ClassLoader>());
ASSERT_NE(nullptr, iterable_klass);
- origin = iterable_klass->FindDeclaredVirtualMethod(
+ origin = iterable_klass->FindClassMethod(
"forEach", "(Ljava/util/function/Consumer;)V", pointer_size);
ASSERT_NE(nullptr, origin);
+ ASSERT_FALSE(origin->IsDirect());
+ ASSERT_TRUE(origin->GetDeclaringClass() == iterable_klass);
code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size);
// the origin method should have a pointer to quick code
ASSERT_NE(nullptr, code);
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index 6c7baf02b3..3460efe474 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -247,9 +247,9 @@ class JniCompilerTest : public CommonCompilerTest {
// Compile the native method before starting the runtime
mirror::Class* c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader);
const auto pointer_size = class_linker_->GetImagePointerSize();
- ArtMethod* method = direct ? c->FindDirectMethod(method_name, method_sig, pointer_size) :
- c->FindVirtualMethod(method_name, method_sig, pointer_size);
+ ArtMethod* method = c->FindClassMethod(method_name, method_sig, pointer_size);
ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig;
+ ASSERT_EQ(direct, method->IsDirect()) << method_name << " " << method_sig;
if (check_generic_jni_) {
method->SetEntryPointFromQuickCompiledCode(class_linker_->GetRuntimeQuickGenericJniStub());
} else {
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 6120ed08ed..f8bb417f16 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1143,11 +1143,13 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor {
// in the copied method should be the same as in the origin
// method.
mirror::Class* declaring_class = method.GetDeclaringClass();
- ArtMethod* origin = declaring_class->FindDeclaredVirtualMethod(
+ ArtMethod* origin = declaring_class->FindClassMethod(
declaring_class->GetDexCache(),
method.GetDexMethodIndex(),
pointer_size_);
CHECK(origin != nullptr);
+ CHECK(!origin->IsDirect());
+ CHECK(origin->GetDeclaringClass() == declaring_class);
if (IsInOatFile(&declaring_class->GetDexFile())) {
const void* code_ptr =
origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_);
@@ -1189,7 +1191,7 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor {
if (writer_->HasBootImage()) {
const InvokeType invoke_type = it.GetMethodInvokeType(
dex_file_->GetClassDef(class_def_index_));
- method = class_linker_->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
+ method = class_linker_->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
*dex_file_,
it.GetMemberIndex(),
dex_cache,
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 839f328a4f..8054140924 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -664,10 +664,7 @@ void HInstructionBuilder::BuildReturn(const Instruction& instruction,
// TODO: remove redundant constructor fences (b/36656456).
if (RequiresConstructorBarrier(dex_compilation_unit_, compiler_driver_)) {
// Compiling instance constructor.
- if (kIsDebugBuild) {
- std::string method_name = graph_->GetMethodName();
- CHECK_EQ(std::string("<init>"), method_name);
- }
+ DCHECK_STREQ("<init>", graph_->GetMethodName());
HInstruction* fence_target = current_this_parameter_;
DCHECK(fence_target != nullptr);
@@ -710,29 +707,18 @@ static InvokeType GetInvokeTypeFromOpCode(Instruction::Code opcode) {
ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType invoke_type) {
ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<2> hs(soa.Self());
ClassLinker* class_linker = dex_compilation_unit_->GetClassLinker();
Handle<mirror::ClassLoader> class_loader = dex_compilation_unit_->GetClassLoader();
- Handle<mirror::Class> compiling_class(hs.NewHandle(GetCompilingClass()));
- // We fetch the referenced class eagerly (that is, the class pointed by in the MethodId
- // at method_idx), as `CanAccessResolvedMethod` expects it be be in the dex cache.
- Handle<mirror::Class> methods_class(hs.NewHandle(class_linker->ResolveReferencedClassOfMethod(
- method_idx, dex_compilation_unit_->GetDexCache(), class_loader)));
-
- if (UNLIKELY(methods_class == nullptr)) {
- // Clean up any exception left by type resolution.
- soa.Self()->ClearException();
- return nullptr;
- }
- ArtMethod* resolved_method = class_linker->ResolveMethod<ClassLinker::kForceICCECheck>(
- *dex_compilation_unit_->GetDexFile(),
- method_idx,
- dex_compilation_unit_->GetDexCache(),
- class_loader,
- /* referrer */ nullptr,
- invoke_type);
+ ArtMethod* resolved_method =
+ class_linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
+ *dex_compilation_unit_->GetDexFile(),
+ method_idx,
+ dex_compilation_unit_->GetDexCache(),
+ class_loader,
+ graph_->GetArtMethod(),
+ invoke_type);
if (UNLIKELY(resolved_method == nullptr)) {
// Clean up any exception left by type resolution.
@@ -740,17 +726,14 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in
return nullptr;
}
- // Check access. The class linker has a fast path for looking into the dex cache
- // and does not check the access if it hits it.
- if (compiling_class == nullptr) {
+ // The referrer may be unresolved for AOT if we're compiling a class that cannot be
+ // resolved because, for example, we don't find a superclass in the classpath.
+ if (graph_->GetArtMethod() == nullptr) {
+ // The class linker cannot check access without a referrer, so we have to do it.
+ // Fall back to HInvokeUnresolved if the method isn't public.
if (!resolved_method->IsPublic()) {
return nullptr;
}
- } else if (!compiling_class->CanAccessResolvedMethod(resolved_method->GetDeclaringClass(),
- resolved_method,
- dex_compilation_unit_->GetDexCache().Get(),
- method_idx)) {
- return nullptr;
}
// We have to special case the invoke-super case, as ClassLinker::ResolveMethod does not.
@@ -758,19 +741,26 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in
// make this an invoke-unresolved to handle cross-dex invokes or abstract super methods, both of
// which require runtime handling.
if (invoke_type == kSuper) {
+ ObjPtr<mirror::Class> compiling_class = GetCompilingClass();
if (compiling_class == nullptr) {
// We could not determine the method's class we need to wait until runtime.
DCHECK(Runtime::Current()->IsAotCompiler());
return nullptr;
}
- if (!methods_class->IsAssignableFrom(compiling_class.Get())) {
+ ObjPtr<mirror::Class> referenced_class = class_linker->LookupResolvedType(
+ *dex_compilation_unit_->GetDexFile(),
+ dex_compilation_unit_->GetDexFile()->GetMethodId(method_idx).class_idx_,
+ dex_compilation_unit_->GetDexCache().Get(),
+ class_loader.Get());
+ DCHECK(referenced_class != nullptr); // We have already resolved a method from this class.
+ if (!referenced_class->IsAssignableFrom(compiling_class)) {
// We cannot statically determine the target method. The runtime will throw a
// NoSuchMethodError on this one.
return nullptr;
}
ArtMethod* actual_method;
- if (methods_class->IsInterface()) {
- actual_method = methods_class->FindVirtualMethodForInterfaceSuper(
+ if (referenced_class->IsInterface()) {
+ actual_method = referenced_class->FindVirtualMethodForInterfaceSuper(
resolved_method, class_linker->GetImagePointerSize());
} else {
uint16_t vtable_index = resolved_method->GetMethodIndex();
@@ -797,12 +787,6 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in
resolved_method = actual_method;
}
- // Check for incompatible class changes. The class linker has a fast path for
- // looking into the dex cache and does not check incompatible class changes if it hits it.
- if (resolved_method->CheckIncompatibleClassChange(invoke_type)) {
- return nullptr;
- }
-
return resolved_method;
}
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index d14716601c..f2a8cc0333 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -1867,33 +1867,35 @@ void InstructionSimplifierVisitor::SimplifySystemArrayCopy(HInvoke* instruction)
ArtMethod* method = nullptr;
switch (source_component_type) {
case Primitive::kPrimBoolean:
- method = system->FindDeclaredDirectMethod("arraycopy", "([ZI[ZII)V", image_size);
+ method = system->FindClassMethod("arraycopy", "([ZI[ZII)V", image_size);
break;
case Primitive::kPrimByte:
- method = system->FindDeclaredDirectMethod("arraycopy", "([BI[BII)V", image_size);
+ method = system->FindClassMethod("arraycopy", "([BI[BII)V", image_size);
break;
case Primitive::kPrimChar:
- method = system->FindDeclaredDirectMethod("arraycopy", "([CI[CII)V", image_size);
+ method = system->FindClassMethod("arraycopy", "([CI[CII)V", image_size);
break;
case Primitive::kPrimShort:
- method = system->FindDeclaredDirectMethod("arraycopy", "([SI[SII)V", image_size);
+ method = system->FindClassMethod("arraycopy", "([SI[SII)V", image_size);
break;
case Primitive::kPrimInt:
- method = system->FindDeclaredDirectMethod("arraycopy", "([II[III)V", image_size);
+ method = system->FindClassMethod("arraycopy", "([II[III)V", image_size);
break;
case Primitive::kPrimFloat:
- method = system->FindDeclaredDirectMethod("arraycopy", "([FI[FII)V", image_size);
+ method = system->FindClassMethod("arraycopy", "([FI[FII)V", image_size);
break;
case Primitive::kPrimLong:
- method = system->FindDeclaredDirectMethod("arraycopy", "([JI[JII)V", image_size);
+ method = system->FindClassMethod("arraycopy", "([JI[JII)V", image_size);
break;
case Primitive::kPrimDouble:
- method = system->FindDeclaredDirectMethod("arraycopy", "([DI[DII)V", image_size);
+ method = system->FindClassMethod("arraycopy", "([DI[DII)V", image_size);
break;
default:
LOG(FATAL) << "Unreachable";
}
DCHECK(method != nullptr);
+ DCHECK(method->IsStatic());
+ DCHECK(method->GetDeclaringClass() == system);
invoke->SetResolvedMethod(method);
// Sharpen the new invoke. Note that we do not update the dex method index of
// the invoke, as we would need to look it up in the current dex file, and it
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 98332d35fb..ecbf52b547 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -526,7 +526,7 @@ void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* inst
// but then we would need to pass it to RTPVisitor just for this debug check. Since
// the method is from the String class, the null loader is good enough.
Handle<mirror::ClassLoader> loader;
- ArtMethod* method = cl->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
+ ArtMethod* method = cl->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
dex_file, invoke->GetDexMethodIndex(), dex_cache, loader, nullptr, kDirect);
DCHECK(method != nullptr);
mirror::Class* declaring_class = method->GetDeclaringClass();
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 686da2136f..72e2a6ce9f 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -155,13 +155,14 @@ class VerifierDepsTest : public CommonCompilerTest {
ArtMethod* method = nullptr;
while (it.HasNextDirectMethod()) {
- ArtMethod* resolved_method = class_linker_->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
- *primary_dex_file_,
- it.GetMemberIndex(),
- dex_cache_handle,
- class_loader_handle,
- nullptr,
- it.GetMethodInvokeType(*class_def));
+ ArtMethod* resolved_method =
+ class_linker_->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
+ *primary_dex_file_,
+ it.GetMemberIndex(),
+ dex_cache_handle,
+ class_loader_handle,
+ nullptr,
+ it.GetMethodInvokeType(*class_def));
CHECK(resolved_method != nullptr);
if (method_name == resolved_method->GetName()) {
method = resolved_method;
@@ -369,18 +370,14 @@ class VerifierDepsTest : public CommonCompilerTest {
// Iterates over all method resolution records, finds an entry which matches
// the given field kind+class+name+signature and tests its properties.
- bool HasMethod(const std::string& expected_kind,
- const std::string& expected_klass,
+ bool HasMethod(const std::string& expected_klass,
const std::string& expected_name,
const std::string& expected_signature,
bool expected_resolved,
const std::string& expected_access_flags = "",
const std::string& expected_decl_klass = "") {
for (auto& dex_dep : verifier_deps_->dex_deps_) {
- auto& storage = (expected_kind == "direct") ? dex_dep.second->direct_methods_
- : (expected_kind == "virtual") ? dex_dep.second->virtual_methods_
- : dex_dep.second->interface_methods_;
- for (auto& entry : storage) {
+ for (const VerifierDeps::MethodResolution& entry : dex_dep.second->methods_) {
if (expected_resolved != entry.IsResolved()) {
continue;
}
@@ -441,9 +438,7 @@ class VerifierDepsTest : public CommonCompilerTest {
has_assignability |= !entry.second->unassignable_types_.empty();
has_classes |= !entry.second->classes_.empty();
has_fields |= !entry.second->fields_.empty();
- has_methods |= !entry.second->direct_methods_.empty();
- has_methods |= !entry.second->virtual_methods_.empty();
- has_methods |= !entry.second->interface_methods_.empty();
+ has_methods |= !entry.second->methods_.empty();
has_unverified_classes |= !entry.second->unverified_classes_.empty();
}
@@ -455,18 +450,6 @@ class VerifierDepsTest : public CommonCompilerTest {
has_unverified_classes;
}
- static std::set<VerifierDeps::MethodResolution>* GetMethods(
- VerifierDeps::DexFileDeps* deps, MethodResolutionKind resolution_kind) {
- if (resolution_kind == kDirectMethodResolution) {
- return &deps->direct_methods_;
- } else if (resolution_kind == kVirtualMethodResolution) {
- return &deps->virtual_methods_;
- } else {
- DCHECK_EQ(resolution_kind, kInterfaceMethodResolution);
- return &deps->interface_methods_;
- }
- }
-
std::unique_ptr<verifier::VerifierDeps> verifier_deps_;
std::vector<const DexFile*> dex_files_;
const DexFile* primary_dex_file_;
@@ -604,11 +587,10 @@ TEST_F(VerifierDepsTest, InvokeArgumentType) {
ASSERT_TRUE(VerifyMethod("InvokeArgumentType"));
ASSERT_TRUE(HasClass("Ljava/text/SimpleDateFormat;", true, "public"));
ASSERT_TRUE(HasClass("Ljava/util/SimpleTimeZone;", true, "public"));
- ASSERT_TRUE(HasMethod("virtual",
- "Ljava/text/SimpleDateFormat;",
+ ASSERT_TRUE(HasMethod("Ljava/text/SimpleDateFormat;",
"setTimeZone",
"(Ljava/util/TimeZone;)V",
- true,
+ /* expect_resolved */ true,
"public",
"Ljava/text/DateFormat;"));
ASSERT_TRUE(HasAssignable("Ljava/util/TimeZone;", "Ljava/util/SimpleTimeZone;", true));
@@ -840,11 +822,10 @@ TEST_F(VerifierDepsTest, InstanceField_Unresolved_ReferrerInDex) {
TEST_F(VerifierDepsTest, InvokeStatic_Resolved_DeclaredInReferenced) {
ASSERT_TRUE(VerifyMethod("InvokeStatic_Resolved_DeclaredInReferenced"));
ASSERT_TRUE(HasClass("Ljava/net/Socket;", true, "public"));
- ASSERT_TRUE(HasMethod("direct",
- "Ljava/net/Socket;",
+ ASSERT_TRUE(HasMethod("Ljava/net/Socket;",
"setSocketImplFactory",
"(Ljava/net/SocketImplFactory;)V",
- true,
+ /* expect_resolved */ true,
"public static",
"Ljava/net/Socket;"));
}
@@ -852,22 +833,20 @@ TEST_F(VerifierDepsTest, InvokeStatic_Resolved_DeclaredInReferenced) {
TEST_F(VerifierDepsTest, InvokeStatic_Resolved_DeclaredInSuperclass1) {
ASSERT_TRUE(VerifyMethod("InvokeStatic_Resolved_DeclaredInSuperclass1"));
ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public"));
- ASSERT_TRUE(HasMethod("direct",
- "Ljavax/net/ssl/SSLSocket;",
+ ASSERT_TRUE(HasMethod("Ljavax/net/ssl/SSLSocket;",
"setSocketImplFactory",
"(Ljava/net/SocketImplFactory;)V",
- true,
+ /* expect_resolved */ true,
"public static",
"Ljava/net/Socket;"));
}
TEST_F(VerifierDepsTest, InvokeStatic_Resolved_DeclaredInSuperclass2) {
ASSERT_TRUE(VerifyMethod("InvokeStatic_Resolved_DeclaredInSuperclass2"));
- ASSERT_TRUE(HasMethod("direct",
- "LMySSLSocket;",
+ ASSERT_TRUE(HasMethod("LMySSLSocket;",
"setSocketImplFactory",
"(Ljava/net/SocketImplFactory;)V",
- true,
+ /* expect_resolved */ true,
"public static",
"Ljava/net/Socket;"));
}
@@ -875,11 +854,10 @@ TEST_F(VerifierDepsTest, InvokeStatic_Resolved_DeclaredInSuperclass2) {
TEST_F(VerifierDepsTest, InvokeStatic_DeclaredInInterface1) {
ASSERT_TRUE(VerifyMethod("InvokeStatic_DeclaredInInterface1"));
ASSERT_TRUE(HasClass("Ljava/util/Map$Entry;", true, "public interface"));
- ASSERT_TRUE(HasMethod("direct",
- "Ljava/util/Map$Entry;",
+ ASSERT_TRUE(HasMethod("Ljava/util/Map$Entry;",
"comparingByKey",
"()Ljava/util/Comparator;",
- true,
+ /* expect_resolved */ true,
"public static",
"Ljava/util/Map$Entry;"));
}
@@ -887,68 +865,85 @@ TEST_F(VerifierDepsTest, InvokeStatic_DeclaredInInterface1) {
TEST_F(VerifierDepsTest, InvokeStatic_DeclaredInInterface2) {
ASSERT_FALSE(VerifyMethod("InvokeStatic_DeclaredInInterface2"));
ASSERT_TRUE(HasClass("Ljava/util/AbstractMap$SimpleEntry;", true, "public"));
- ASSERT_TRUE(HasMethod("direct",
- "Ljava/util/AbstractMap$SimpleEntry;",
+ ASSERT_TRUE(HasMethod("Ljava/util/AbstractMap$SimpleEntry;",
"comparingByKey",
"()Ljava/util/Comparator;",
- false));
+ /* expect_resolved */ false));
}
TEST_F(VerifierDepsTest, InvokeStatic_Unresolved1) {
ASSERT_FALSE(VerifyMethod("InvokeStatic_Unresolved1"));
ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public"));
- ASSERT_TRUE(HasMethod("direct", "Ljavax/net/ssl/SSLSocket;", "x", "()V", false));
+ ASSERT_TRUE(HasMethod("Ljavax/net/ssl/SSLSocket;",
+ "x",
+ "()V",
+ /* expect_resolved */ false));
}
TEST_F(VerifierDepsTest, InvokeStatic_Unresolved2) {
ASSERT_FALSE(VerifyMethod("InvokeStatic_Unresolved2"));
- ASSERT_TRUE(HasMethod("direct", "LMySSLSocket;", "x", "()V", false));
+ ASSERT_TRUE(HasMethod("LMySSLSocket;",
+ "x",
+ "()V",
+ /* expect_resolved */ false));
}
TEST_F(VerifierDepsTest, InvokeDirect_Resolved_DeclaredInReferenced) {
ASSERT_TRUE(VerifyMethod("InvokeDirect_Resolved_DeclaredInReferenced"));
ASSERT_TRUE(HasClass("Ljava/net/Socket;", true, "public"));
- ASSERT_TRUE(HasMethod(
- "direct", "Ljava/net/Socket;", "<init>", "()V", true, "public", "Ljava/net/Socket;"));
+ ASSERT_TRUE(HasMethod("Ljava/net/Socket;",
+ "<init>",
+ "()V",
+ /* expect_resolved */ true,
+ "public",
+ "Ljava/net/Socket;"));
}
TEST_F(VerifierDepsTest, InvokeDirect_Resolved_DeclaredInSuperclass1) {
ASSERT_FALSE(VerifyMethod("InvokeDirect_Resolved_DeclaredInSuperclass1"));
ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public"));
- ASSERT_TRUE(HasMethod("direct",
- "Ljavax/net/ssl/SSLSocket;",
+ ASSERT_TRUE(HasMethod("Ljavax/net/ssl/SSLSocket;",
"checkOldImpl",
"()V",
- true,
+ /* expect_resolved */ true,
"private",
"Ljava/net/Socket;"));
}
TEST_F(VerifierDepsTest, InvokeDirect_Resolved_DeclaredInSuperclass2) {
ASSERT_FALSE(VerifyMethod("InvokeDirect_Resolved_DeclaredInSuperclass2"));
- ASSERT_TRUE(HasMethod(
- "direct", "LMySSLSocket;", "checkOldImpl", "()V", true, "private", "Ljava/net/Socket;"));
+ ASSERT_TRUE(HasMethod("LMySSLSocket;",
+ "checkOldImpl",
+ "()V",
+ /* expect_resolved */ true,
+ "private",
+ "Ljava/net/Socket;"));
}
TEST_F(VerifierDepsTest, InvokeDirect_Unresolved1) {
ASSERT_FALSE(VerifyMethod("InvokeDirect_Unresolved1"));
ASSERT_TRUE(HasClass("Ljavax/net/ssl/SSLSocket;", true, "public"));
- ASSERT_TRUE(HasMethod("direct", "Ljavax/net/ssl/SSLSocket;", "x", "()V", false));
+ ASSERT_TRUE(HasMethod("Ljavax/net/ssl/SSLSocket;",
+ "x",
+ "()V",
+ /* expect_resolved */ false));
}
TEST_F(VerifierDepsTest, InvokeDirect_Unresolved2) {
ASSERT_FALSE(VerifyMethod("InvokeDirect_Unresolved2"));
- ASSERT_TRUE(HasMethod("direct", "LMySSLSocket;", "x", "()V", false));
+ ASSERT_TRUE(HasMethod("LMySSLSocket;",
+ "x",
+ "()V",
+ /* expect_resolved */ false));
}
TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInReferenced) {
ASSERT_TRUE(VerifyMethod("InvokeVirtual_Resolved_DeclaredInReferenced"));
ASSERT_TRUE(HasClass("Ljava/lang/Throwable;", true, "public"));
- ASSERT_TRUE(HasMethod("virtual",
- "Ljava/lang/Throwable;",
+ ASSERT_TRUE(HasMethod("Ljava/lang/Throwable;",
"getMessage",
"()Ljava/lang/String;",
- true,
+ /* expect_resolved */ true,
"public",
"Ljava/lang/Throwable;"));
// Type dependency on `this` argument.
@@ -958,11 +953,10 @@ TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInReferenced) {
TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperclass1) {
ASSERT_TRUE(VerifyMethod("InvokeVirtual_Resolved_DeclaredInSuperclass1"));
ASSERT_TRUE(HasClass("Ljava/io/InterruptedIOException;", true, "public"));
- ASSERT_TRUE(HasMethod("virtual",
- "Ljava/io/InterruptedIOException;",
+ ASSERT_TRUE(HasMethod("Ljava/io/InterruptedIOException;",
"getMessage",
"()Ljava/lang/String;",
- true,
+ /* expect_resolved */ true,
"public",
"Ljava/lang/Throwable;"));
// Type dependency on `this` argument.
@@ -971,22 +965,20 @@ TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperclass1) {
TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperclass2) {
ASSERT_TRUE(VerifyMethod("InvokeVirtual_Resolved_DeclaredInSuperclass2"));
- ASSERT_TRUE(HasMethod("virtual",
- "LMySocketTimeoutException;",
+ ASSERT_TRUE(HasMethod("LMySocketTimeoutException;",
"getMessage",
"()Ljava/lang/String;",
- true,
+ /* expect_resolved */ true,
"public",
"Ljava/lang/Throwable;"));
}
TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperinterface) {
ASSERT_TRUE(VerifyMethod("InvokeVirtual_Resolved_DeclaredInSuperinterface"));
- ASSERT_TRUE(HasMethod("virtual",
- "LMyThreadSet;",
+ ASSERT_TRUE(HasMethod("LMyThreadSet;",
"size",
"()I",
- true,
+ /* expect_resolved */ true,
"public",
"Ljava/util/Set;"));
}
@@ -994,61 +986,59 @@ TEST_F(VerifierDepsTest, InvokeVirtual_Resolved_DeclaredInSuperinterface) {
TEST_F(VerifierDepsTest, InvokeVirtual_Unresolved1) {
ASSERT_FALSE(VerifyMethod("InvokeVirtual_Unresolved1"));
ASSERT_TRUE(HasClass("Ljava/io/InterruptedIOException;", true, "public"));
- ASSERT_TRUE(HasMethod("virtual", "Ljava/io/InterruptedIOException;", "x", "()V", false));
+ ASSERT_TRUE(HasMethod("Ljava/io/InterruptedIOException;",
+ "x",
+ "()V",
+ /* expect_resolved */ false));
}
TEST_F(VerifierDepsTest, InvokeVirtual_Unresolved2) {
ASSERT_FALSE(VerifyMethod("InvokeVirtual_Unresolved2"));
- ASSERT_TRUE(HasMethod("virtual", "LMySocketTimeoutException;", "x", "()V", false));
-}
-
-TEST_F(VerifierDepsTest, InvokeVirtual_ActuallyDirect) {
- ASSERT_FALSE(VerifyMethod("InvokeVirtual_ActuallyDirect"));
- ASSERT_TRUE(HasMethod("virtual", "LMyThread;", "activeCount", "()I", false));
- ASSERT_TRUE(HasMethod("direct",
- "LMyThread;",
- "activeCount",
- "()I",
- true,
- "public static",
- "Ljava/lang/Thread;"));
+ ASSERT_TRUE(HasMethod("LMySocketTimeoutException;",
+ "x",
+ "()V",
+ /* expect_resolved */ false));
}
TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInReferenced) {
ASSERT_TRUE(VerifyMethod("InvokeInterface_Resolved_DeclaredInReferenced"));
ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public interface"));
- ASSERT_TRUE(HasMethod("interface",
- "Ljava/lang/Runnable;",
+ ASSERT_TRUE(HasMethod("Ljava/lang/Runnable;",
"run",
"()V",
- true,
+ /* expect_resolved */ true,
"public",
"Ljava/lang/Runnable;"));
}
TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInSuperclass) {
ASSERT_FALSE(VerifyMethod("InvokeInterface_Resolved_DeclaredInSuperclass"));
- ASSERT_TRUE(HasMethod("interface", "LMyThread;", "join", "()V", false));
+ // TODO: Maybe we should not record dependency if the invoke type does not match the lookup type.
+ ASSERT_TRUE(HasMethod("LMyThread;",
+ "join",
+ "()V",
+ /* expect_resolved */ true,
+ "public",
+ "Ljava/lang/Thread;"));
}
TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInSuperinterface1) {
ASSERT_FALSE(VerifyMethod("InvokeInterface_Resolved_DeclaredInSuperinterface1"));
- ASSERT_TRUE(HasMethod("interface",
- "LMyThreadSet;",
+ // TODO: Maybe we should not record dependency if the invoke type does not match the lookup type.
+ ASSERT_TRUE(HasMethod("LMyThreadSet;",
"run",
"()V",
- true,
+ /* expect_resolved */ true,
"public",
- "Ljava/lang/Runnable;"));
+ "Ljava/lang/Thread;"));
}
TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInSuperinterface2) {
ASSERT_FALSE(VerifyMethod("InvokeInterface_Resolved_DeclaredInSuperinterface2"));
- ASSERT_TRUE(HasMethod("interface",
- "LMyThreadSet;",
+ ASSERT_TRUE(HasMethod("LMyThreadSet;",
"isEmpty",
"()Z",
- true,
+ /* expect_resolved */ true,
"public",
"Ljava/util/Set;"));
}
@@ -1056,23 +1046,25 @@ TEST_F(VerifierDepsTest, InvokeInterface_Resolved_DeclaredInSuperinterface2) {
TEST_F(VerifierDepsTest, InvokeInterface_Unresolved1) {
ASSERT_FALSE(VerifyMethod("InvokeInterface_Unresolved1"));
ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public interface"));
- ASSERT_TRUE(HasMethod("interface", "Ljava/lang/Runnable;", "x", "()V", false));
+ ASSERT_TRUE(HasMethod("Ljava/lang/Runnable;",
+ "x",
+ "()V",
+ /* expect_resolved */ false));
}
TEST_F(VerifierDepsTest, InvokeInterface_Unresolved2) {
ASSERT_FALSE(VerifyMethod("InvokeInterface_Unresolved2"));
- ASSERT_TRUE(HasMethod("interface", "LMyThreadSet;", "x", "()V", false));
+ ASSERT_TRUE(HasMethod("LMyThreadSet;", "x", "()V", /* expect_resolved */ false));
}
TEST_F(VerifierDepsTest, InvokeSuper_ThisAssignable) {
ASSERT_TRUE(VerifyMethod("InvokeSuper_ThisAssignable"));
ASSERT_TRUE(HasClass("Ljava/lang/Runnable;", true, "public interface"));
ASSERT_TRUE(HasAssignable("Ljava/lang/Runnable;", "Ljava/lang/Thread;", true));
- ASSERT_TRUE(HasMethod("interface",
- "Ljava/lang/Runnable;",
+ ASSERT_TRUE(HasMethod("Ljava/lang/Runnable;",
"run",
"()V",
- true,
+ /* expect_resolved */ true,
"public",
"Ljava/lang/Runnable;"));
}
@@ -1081,8 +1073,10 @@ TEST_F(VerifierDepsTest, InvokeSuper_ThisNotAssignable) {
ASSERT_FALSE(VerifyMethod("InvokeSuper_ThisNotAssignable"));
ASSERT_TRUE(HasClass("Ljava/lang/Integer;", true, "public"));
ASSERT_TRUE(HasAssignable("Ljava/lang/Integer;", "Ljava/lang/Thread;", false));
- ASSERT_TRUE(HasMethod(
- "virtual", "Ljava/lang/Integer;", "intValue", "()I", true, "public", "Ljava/lang/Integer;"));
+ ASSERT_TRUE(HasMethod("Ljava/lang/Integer;",
+ "intValue", "()I",
+ /* expect_resolved */ true,
+ "public", "Ljava/lang/Integer;"));
}
TEST_F(VerifierDepsTest, ArgumentType_ResolvedReferenceArray) {
@@ -1150,18 +1144,6 @@ TEST_F(VerifierDepsTest, UnverifiedClasses) {
ASSERT_TRUE(HasUnverifiedClass("LMyClassWithNoSuperButFailures;"));
}
-// Returns the next resolution kind in the enum.
-static MethodResolutionKind GetNextResolutionKind(MethodResolutionKind resolution_kind) {
- if (resolution_kind == kDirectMethodResolution) {
- return kVirtualMethodResolution;
- } else if (resolution_kind == kVirtualMethodResolution) {
- return kInterfaceMethodResolution;
- } else {
- DCHECK_EQ(resolution_kind, kInterfaceMethodResolution);
- return kDirectMethodResolution;
- }
-}
-
TEST_F(VerifierDepsTest, VerifyDeps) {
VerifyDexFile();
@@ -1338,131 +1320,82 @@ TEST_F(VerifierDepsTest, VerifyDeps) {
}
// Mess up with methods.
- for (MethodResolutionKind resolution_kind :
- { kDirectMethodResolution, kVirtualMethodResolution, kInterfaceMethodResolution }) {
- {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
- for (const auto& entry : *methods) {
- if (entry.IsResolved()) {
- methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
- VerifierDeps::kUnresolvedMarker,
- entry.GetDeclaringClassIndex()));
- found = true;
- break;
- }
- }
- ASSERT_TRUE(found);
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
- }
-
- {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
- for (const auto& entry : *methods) {
- if (!entry.IsResolved()) {
- constexpr dex::StringIndex kStringIndexZero(0); // We know there is a class there.
- methods->insert(VerifierDeps::MethodResolution(0 /* we know there is a method there */,
- VerifierDeps::kUnresolvedMarker - 1,
- kStringIndexZero));
- found = true;
- break;
- }
- }
- ASSERT_TRUE(found);
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
- }
-
- {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
- for (const auto& entry : *methods) {
- if (entry.IsResolved()) {
- methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
- entry.GetAccessFlags() - 1,
- entry.GetDeclaringClassIndex()));
- found = true;
- break;
- }
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = &deps->methods_;
+ for (const auto& entry : *methods) {
+ if (entry.IsResolved()) {
+ methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ VerifierDeps::kUnresolvedMarker,
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ break;
}
- ASSERT_TRUE(found);
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
+ }
- {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
- for (const auto& entry : *methods) {
- constexpr dex::StringIndex kNewTypeIndex(0);
- if (entry.IsResolved() && entry.GetDeclaringClassIndex() != kNewTypeIndex) {
- methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
- entry.GetAccessFlags(),
- kNewTypeIndex));
- found = true;
- break;
- }
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = &deps->methods_;
+ for (const auto& entry : *methods) {
+ if (!entry.IsResolved()) {
+ constexpr dex::StringIndex kStringIndexZero(0); // We know there is a class there.
+ methods->insert(VerifierDeps::MethodResolution(0 /* we know there is a method there */,
+ VerifierDeps::kUnresolvedMarker - 1,
+ kStringIndexZero));
+ found = true;
+ break;
}
- ASSERT_TRUE(found);
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
+ }
- // The two tests below make sure that fiddling with the method kind
- // (static, virtual, interface) is detected by `ValidateDependencies`.
-
- // An interface method lookup can succeed with a virtual method lookup on the same class.
- // That's OK, as we only want to make sure there is a method being defined with the right
- // flags. Therefore, polluting the interface methods with virtual methods does not have
- // to fail verification.
- if (resolution_kind != kVirtualMethodResolution) {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
- for (const auto& entry : *methods) {
- if (entry.IsResolved()) {
- GetMethods(deps, GetNextResolutionKind(resolution_kind))->insert(
- VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
- entry.GetAccessFlags(),
- entry.GetDeclaringClassIndex()));
- found = true;
- }
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = &deps->methods_;
+ for (const auto& entry : *methods) {
+ if (entry.IsResolved()) {
+ methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ entry.GetAccessFlags() - 1,
+ entry.GetDeclaringClassIndex()));
+ found = true;
+ break;
}
- ASSERT_TRUE(found);
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
+ }
- // See comment above that applies the same way.
- if (resolution_kind != kInterfaceMethodResolution) {
- VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
- VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
- bool found = false;
- std::set<VerifierDeps::MethodResolution>* methods = GetMethods(deps, resolution_kind);
- for (const auto& entry : *methods) {
- if (entry.IsResolved()) {
- GetMethods(deps, GetNextResolutionKind(GetNextResolutionKind(resolution_kind)))->insert(
- VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
- entry.GetAccessFlags(),
- entry.GetDeclaringClassIndex()));
- found = true;
- }
+ {
+ VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ std::set<VerifierDeps::MethodResolution>* methods = &deps->methods_;
+ for (const auto& entry : *methods) {
+ constexpr dex::StringIndex kNewTypeIndex(0);
+ if (entry.IsResolved() && entry.GetDeclaringClassIndex() != kNewTypeIndex) {
+ methods->insert(VerifierDeps::MethodResolution(entry.GetDexMethodIndex(),
+ entry.GetAccessFlags(),
+ kNewTypeIndex));
+ found = true;
+ break;
}
- ASSERT_TRUE(found);
- new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
+ ASSERT_TRUE(found);
+ new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
}
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 45dd5965fb..ef9c457b02 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -67,9 +67,10 @@ ArtMethod* ArtMethod::GetCanonicalMethod(PointerSize pointer_size) {
return this;
} else {
mirror::Class* declaring_class = GetDeclaringClass();
- ArtMethod* ret = declaring_class->FindDeclaredVirtualMethod(declaring_class->GetDexCache(),
- GetDexMethodIndex(),
- pointer_size);
+ DCHECK(declaring_class->IsInterface());
+ ArtMethod* ret = declaring_class->FindInterfaceMethod(declaring_class->GetDexCache(),
+ GetDexMethodIndex(),
+ pointer_size);
DCHECK(ret != nullptr);
return ret;
}
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 3c51f52616..d29db15f0a 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -90,33 +90,105 @@ inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMetho
return resolved_type.Ptr();
}
+template <bool kThrowOnError, typename ClassGetter>
+inline bool ClassLinker::CheckInvokeClassMismatch(ObjPtr<mirror::DexCache> dex_cache,
+ InvokeType type,
+ ClassGetter class_getter) {
+ switch (type) {
+ case kStatic:
+ case kSuper:
+ break;
+ case kInterface: {
+ // We have to check whether the method id really belongs to an interface (dex static bytecode
+ // constraints A15, A16). Otherwise you must not invoke-interface on it.
+ ObjPtr<mirror::Class> klass = class_getter();
+ if (UNLIKELY(!klass->IsInterface())) {
+ if (kThrowOnError) {
+ ThrowIncompatibleClassChangeError(klass,
+ "Found class %s, but interface was expected",
+ klass->PrettyDescriptor().c_str());
+ }
+ return true;
+ }
+ break;
+ }
+ case kDirect:
+ if (dex_cache->GetDexFile()->GetVersion() >= DexFile::kDefaultMethodsVersion) {
+ break;
+ }
+ FALLTHROUGH_INTENDED;
+ case kVirtual: {
+ // Similarly, invoke-virtual (and invoke-direct without default methods) must reference
+ // a non-interface class (dex static bytecode constraint A24, A25).
+ ObjPtr<mirror::Class> klass = class_getter();
+ if (UNLIKELY(klass->IsInterface())) {
+ if (kThrowOnError) {
+ ThrowIncompatibleClassChangeError(klass,
+ "Found interface %s, but class was expected",
+ klass->PrettyDescriptor().c_str());
+ }
+ return true;
+ }
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unreachable - invocation type: " << type;
+ UNREACHABLE();
+ }
+ return false;
+}
+
+template <bool kThrow>
+inline bool ClassLinker::CheckInvokeClassMismatch(ObjPtr<mirror::DexCache> dex_cache,
+ InvokeType type,
+ uint32_t method_idx,
+ ObjPtr<mirror::ClassLoader> class_loader) {
+ return CheckInvokeClassMismatch<kThrow>(
+ dex_cache,
+ type,
+ [this, dex_cache, method_idx, class_loader]() REQUIRES_SHARED(Locks::mutator_lock_) {
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
+ ObjPtr<mirror::Class> klass =
+ LookupResolvedType(dex_file, method_id.class_idx_, dex_cache, class_loader);
+ DCHECK(klass != nullptr);
+ return klass;
+ });
+}
+
+template <InvokeType type, ClassLinker::ResolveMode kResolveMode>
inline ArtMethod* ClassLinker::GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer) {
+ DCHECK(referrer != nullptr);
+ // Note: The referrer can be a Proxy constructor. In that case, we need to do the
+ // lookup in the context of the original method from where it steals the code.
+ // However, we delay the GetInterfaceMethodIfProxy() until needed.
+ DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor());
ArtMethod* resolved_method = referrer->GetDexCacheResolvedMethod(method_idx, image_pointer_size_);
if (resolved_method == nullptr || resolved_method->IsRuntimeMethod()) {
return nullptr;
}
- return resolved_method;
-}
-
-inline mirror::Class* ClassLinker::ResolveReferencedClassOfMethod(
- uint32_t method_idx,
- Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader) {
- // NB: We cannot simply use `GetResolvedMethod(method_idx, ...)->GetDeclaringClass()`. This is
- // because if we did so than an invoke-super could be incorrectly dispatched in cases where
- // GetMethodId(method_idx).class_idx_ refers to a non-interface, non-direct-superclass
- // (super*-class?) of the referrer and the direct superclass of the referrer contains a concrete
- // implementation of the method. If this class's implementation of the method is copied from an
- // interface (either miranda, default or conflict) we would incorrectly assume that is what we
- // want to invoke on, instead of the 'concrete' implementation that the direct superclass
- // contains.
- const DexFile* dex_file = dex_cache->GetDexFile();
- const DexFile::MethodId& method = dex_file->GetMethodId(method_idx);
- ObjPtr<mirror::Class> resolved_type = dex_cache->GetResolvedType(method.class_idx_);
- if (UNLIKELY(resolved_type == nullptr)) {
- resolved_type = ResolveType(*dex_file, method.class_idx_, dex_cache, class_loader);
+ if (kResolveMode == ResolveMode::kCheckICCEAndIAE) {
+ referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
+ // Check if the invoke type matches the class type.
+ ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache();
+ ObjPtr<mirror::ClassLoader> class_loader = referrer->GetClassLoader();
+ if (CheckInvokeClassMismatch</* kThrow */ false>(dex_cache, type, method_idx, class_loader)) {
+ return nullptr;
+ }
+ // Check access.
+ ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
+ if (!referring_class->CanAccessResolvedMethod(resolved_method->GetDeclaringClass(),
+ resolved_method,
+ dex_cache,
+ method_idx)) {
+ return nullptr;
+ }
+ // Check if the invoke type matches the method type.
+ if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
+ return nullptr;
+ }
}
- return resolved_type.Ptr();
+ return resolved_method;
}
template <ClassLinker::ResolveMode kResolveMode>
@@ -124,9 +196,15 @@ inline ArtMethod* ClassLinker::ResolveMethod(Thread* self,
uint32_t method_idx,
ArtMethod* referrer,
InvokeType type) {
- ArtMethod* resolved_method = GetResolvedMethod(method_idx, referrer);
+ DCHECK(referrer != nullptr);
+ // Note: The referrer can be a Proxy constructor. In that case, we need to do the
+ // lookup in the context of the original method from where it steals the code.
+ // However, we delay the GetInterfaceMethodIfProxy() until needed.
+ DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor());
Thread::PoisonObjectPointersIfDebug();
- if (UNLIKELY(resolved_method == nullptr)) {
+ ArtMethod* resolved_method = referrer->GetDexCacheResolvedMethod(method_idx, image_pointer_size_);
+ if (UNLIKELY(resolved_method == nullptr || resolved_method->IsRuntimeMethod())) {
+ referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
StackHandleScope<2> hs(self);
Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(referrer->GetDexCache()));
@@ -138,6 +216,33 @@ inline ArtMethod* ClassLinker::ResolveMethod(Thread* self,
h_class_loader,
referrer,
type);
+ } else if (kResolveMode == ResolveMode::kCheckICCEAndIAE) {
+ referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
+ // Check if the invoke type matches the class type.
+ ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache();
+ ObjPtr<mirror::ClassLoader> class_loader = referrer->GetClassLoader();
+ if (CheckInvokeClassMismatch</* kThrow */ true>(dex_cache, type, method_idx, class_loader)) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return nullptr;
+ }
+ // Check access.
+ ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
+ if (!referring_class->CheckResolvedMethodAccess(resolved_method->GetDeclaringClass(),
+ resolved_method,
+ dex_cache,
+ method_idx,
+ type)) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return nullptr;
+ }
+ // Check if the invoke type matches the method type.
+ if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
+ ThrowIncompatibleClassChangeError(type,
+ resolved_method->GetInvokeType(),
+ resolved_method,
+ referrer);
+ return nullptr;
+ }
}
// Note: We cannot check here to see whether we added the method to the cache. It
// might be an erroneous class, which results in it being hidden from us.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index a9237ef303..a227d18861 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -150,8 +150,8 @@ static bool HasInitWithString(Thread* self, ClassLinker* class_linker, const cha
return false;
}
- ArtMethod* exception_init_method = exception_class->FindDeclaredDirectMethod(
- "<init>", "(Ljava/lang/String;)V", class_linker->GetImagePointerSize());
+ ArtMethod* exception_init_method = exception_class->FindConstructor(
+ "(Ljava/lang/String;)V", class_linker->GetImagePointerSize());
return exception_init_method != nullptr;
}
@@ -4638,10 +4638,8 @@ void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod*
// Find the <init>(InvocationHandler)V method. The exact method offset varies depending
// on which front-end compiler was used to build the libcore DEX files.
- ArtMethod* proxy_constructor = GetClassRoot(kJavaLangReflectProxy)->
- FindDeclaredDirectMethod("<init>",
- "(Ljava/lang/reflect/InvocationHandler;)V",
- image_pointer_size_);
+ ArtMethod* proxy_constructor = GetClassRoot(kJavaLangReflectProxy)->FindConstructor(
+ "(Ljava/lang/reflect/InvocationHandler;)V", image_pointer_size_);
DCHECK(proxy_constructor != nullptr)
<< "Could not find <init> method in java.lang.reflect.Proxy";
@@ -4653,8 +4651,9 @@ void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod*
// code_ too)
DCHECK(out != nullptr);
out->CopyFrom(proxy_constructor, image_pointer_size_);
- // Make this constructor public and fix the class to be our Proxy version
+ // Make this constructor public and fix the class to be our Proxy version.
// Mark kAccCompileDontBother so that we don't take JIT samples for the method. b/62349349
+ // Note that the compiler calls a ResolveMethod() overload that does not handle a Proxy referrer.
out->SetAccessFlags((out->GetAccessFlags() & ~kAccProtected) |
kAccPublic |
kAccCompileDontBother);
@@ -7363,10 +7362,8 @@ bool ClassLinker::LinkInterfaceMethods(
// defaults. This means we don't need to do any trickery when creating the Miranda methods, since
// they will already be null. This has the additional benefit that the declarer of a miranda
// method will actually declare an abstract method.
- for (size_t i = ifcount; i != 0; ) {
+ for (size_t i = ifcount; i != 0u; ) {
--i;
-
- DCHECK_GE(i, 0u);
DCHECK_LT(i, ifcount);
size_t num_methods = iftable->GetInterface(i)->NumDeclaredVirtualMethods();
@@ -7947,201 +7944,95 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file,
ArtMethod* referrer,
InvokeType type) {
DCHECK(dex_cache != nullptr);
+ DCHECK(referrer == nullptr || !referrer->IsProxyMethod());
// Check for hit in the dex cache.
- ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_);
+ PointerSize pointer_size = image_pointer_size_;
+ ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, pointer_size);
Thread::PoisonObjectPointersIfDebug();
- if (resolved != nullptr && !resolved->IsRuntimeMethod()) {
+ bool valid_dex_cache_method = resolved != nullptr && !resolved->IsRuntimeMethod();
+ if (kResolveMode == ResolveMode::kNoChecks && valid_dex_cache_method) {
+ // We have a valid method from the DexCache and no checks to perform.
DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
- if (kResolveMode == ClassLinker::kForceICCECheck) {
- if (resolved->CheckIncompatibleClassChange(type)) {
- ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer);
- return nullptr;
- }
- }
return resolved;
}
- // Fail, get the declaring class.
const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
- ObjPtr<mirror::Class> klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader);
- if (klass == nullptr) {
+ ObjPtr<mirror::Class> klass = nullptr;
+ if (valid_dex_cache_method) {
+ // We have a valid method from the DexCache but we need to perform ICCE and IAE checks.
+ DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
+ klass = LookupResolvedType(dex_file, method_id.class_idx_, dex_cache.Get(), class_loader.Get());
+ DCHECK(klass != nullptr);
+ } else {
+ // The method was not in the DexCache, resolve the declaring class.
+ klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader);
+ if (klass == nullptr) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return nullptr;
+ }
+ }
+
+ // Check if the invoke type matches the class type.
+ if (kResolveMode == ResolveMode::kCheckICCEAndIAE &&
+ CheckInvokeClassMismatch</* kThrow */ true>(
+ dex_cache.Get(), type, [klass]() { return klass; })) {
DCHECK(Thread::Current()->IsExceptionPending());
return nullptr;
}
- // Scan using method_idx, this saves string compares but will only hit for matching dex
- // caches/files.
- switch (type) {
- case kDirect: // Fall-through.
- case kStatic:
- resolved = klass->FindDirectMethod(dex_cache.Get(), method_idx, image_pointer_size_);
- DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr);
- break;
- case kInterface:
- // We have to check whether the method id really belongs to an interface (dex static bytecode
- // constraint A15). Otherwise you must not invoke-interface on it.
- //
- // This is not symmetric to A12-A14 (direct, static, virtual), as using FindInterfaceMethod
- // assumes that the given type is an interface, and will check the interface table if the
- // method isn't declared in the class. So it may find an interface method (usually by name
- // in the handling below, but we do the constraint check early). In that case,
- // CheckIncompatibleClassChange will succeed (as it is called on an interface method)
- // unexpectedly.
- // Example:
- // interface I {
- // foo()
- // }
- // class A implements I {
- // ...
- // }
- // class B extends A {
- // ...
- // }
- // invoke-interface B.foo
- // -> FindInterfaceMethod finds I.foo (interface method), not A.foo (miranda method)
- if (UNLIKELY(!klass->IsInterface())) {
- ThrowIncompatibleClassChangeError(klass,
- "Found class %s, but interface was expected",
- klass->PrettyDescriptor().c_str());
- return nullptr;
- } else {
- resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_);
- DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface());
- }
- break;
- case kSuper:
- if (klass->IsInterface()) {
- resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_);
- } else {
- resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx, image_pointer_size_);
- }
- break;
- case kVirtual:
- resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx, image_pointer_size_);
- break;
- default:
- LOG(FATAL) << "Unreachable - invocation type: " << type;
- UNREACHABLE();
+
+ if (!valid_dex_cache_method) {
+ // Search for the method using dex_cache and method_idx. The Class::Find*Method()
+ // functions can optimize the search if the dex_cache is the same as the DexCache
+ // of the class, with fall-back to name and signature search otherwise.
+ if (klass->IsInterface()) {
+ resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, pointer_size);
+ } else {
+ resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, pointer_size);
+ }
+ DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr);
+ if (resolved != nullptr) {
+ // Be a good citizen and update the dex cache to speed subsequent calls.
+ dex_cache->SetResolvedMethod(method_idx, resolved, pointer_size);
+ }
}
- if (resolved == nullptr) {
- // Search by name, which works across dex files.
- const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
- const Signature signature = dex_file.GetMethodSignature(method_id);
- switch (type) {
- case kDirect: // Fall-through.
- case kStatic:
- resolved = klass->FindDirectMethod(name, signature, image_pointer_size_);
- DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr);
- break;
- case kInterface:
- resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_);
- DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface());
- break;
- case kSuper:
- if (klass->IsInterface()) {
- resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_);
- } else {
- resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_);
- }
- break;
- case kVirtual:
- resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_);
- break;
+
+ // Note: We can check for IllegalAccessError only if we have a referrer.
+ if (kResolveMode == ResolveMode::kCheckICCEAndIAE && resolved != nullptr && referrer != nullptr) {
+ ObjPtr<mirror::Class> methods_class = resolved->GetDeclaringClass();
+ ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
+ if (!referring_class->CheckResolvedMethodAccess(methods_class,
+ resolved,
+ dex_cache.Get(),
+ method_idx,
+ type)) {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ return nullptr;
}
}
+
// If we found a method, check for incompatible class changes.
- if (LIKELY(resolved != nullptr && !resolved->CheckIncompatibleClassChange(type))) {
- // Be a good citizen and update the dex cache to speed subsequent calls.
- dex_cache->SetResolvedMethod(method_idx, resolved, image_pointer_size_);
+ if (LIKELY(resolved != nullptr) &&
+ LIKELY(kResolveMode == ResolveMode::kNoChecks ||
+ !resolved->CheckIncompatibleClassChange(type))) {
return resolved;
} else {
- // If we had a method, it's an incompatible-class-change error.
+ // If we had a method, or if we can find one with another lookup type,
+ // it's an incompatible-class-change error.
+ if (resolved == nullptr) {
+ if (klass->IsInterface()) {
+ resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, pointer_size);
+ } else {
+ // If there was an interface method with the same signature,
+ // we would have found it also in the "copied" methods.
+ DCHECK(klass->FindInterfaceMethod(dex_cache.Get(), method_idx, pointer_size) == nullptr);
+ }
+ }
if (resolved != nullptr) {
ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer);
} else {
- // We failed to find the method which means either an access error, an incompatible class
- // change, or no such method. First try to find the method among direct and virtual methods.
+ // We failed to find the method (using all lookup types), so throw a NoSuchMethodError.
const char* name = dex_file.StringDataByIdx(method_id.name_idx_);
const Signature signature = dex_file.GetMethodSignature(method_id);
- switch (type) {
- case kDirect:
- case kStatic:
- resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_);
- // Note: kDirect and kStatic are also mutually exclusive, but in that case we would
- // have had a resolved method before, which triggers the "true" branch above.
- break;
- case kInterface:
- case kVirtual:
- case kSuper:
- resolved = klass->FindDirectMethod(name, signature, image_pointer_size_);
- break;
- }
-
- // If we found something, check that it can be accessed by the referrer.
- bool exception_generated = false;
- if (resolved != nullptr && referrer != nullptr) {
- ObjPtr<mirror::Class> methods_class = resolved->GetDeclaringClass();
- ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
- if (!referring_class->CanAccess(methods_class)) {
- ThrowIllegalAccessErrorClassForMethodDispatch(referring_class,
- methods_class,
- resolved,
- type);
- exception_generated = true;
- } else if (!referring_class->CanAccessMember(methods_class, resolved->GetAccessFlags())) {
- ThrowIllegalAccessErrorMethod(referring_class, resolved);
- exception_generated = true;
- }
- }
- if (!exception_generated) {
- // Otherwise, throw an IncompatibleClassChangeError if we found something, and check
- // interface methods and throw if we find the method there. If we find nothing, throw a
- // NoSuchMethodError.
- switch (type) {
- case kDirect:
- case kStatic:
- if (resolved != nullptr) {
- ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer);
- } else {
- resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_);
- if (resolved != nullptr) {
- ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer);
- } else {
- ThrowNoSuchMethodError(type, klass, name, signature);
- }
- }
- break;
- case kInterface:
- if (resolved != nullptr) {
- ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer);
- } else {
- resolved = klass->FindVirtualMethod(name, signature, image_pointer_size_);
- if (resolved != nullptr) {
- ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer);
- } else {
- ThrowNoSuchMethodError(type, klass, name, signature);
- }
- }
- break;
- case kSuper:
- if (resolved != nullptr) {
- ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer);
- } else {
- ThrowNoSuchMethodError(type, klass, name, signature);
- }
- break;
- case kVirtual:
- if (resolved != nullptr) {
- ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer);
- } else {
- resolved = klass->FindInterfaceMethod(name, signature, image_pointer_size_);
- if (resolved != nullptr) {
- ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer);
- } else {
- ThrowNoSuchMethodError(type, klass, name, signature);
- }
- }
- break;
- }
- }
+ ThrowNoSuchMethodError(type, klass, name, signature);
}
Thread::Current()->AssertPendingException();
return nullptr;
@@ -8160,21 +8051,16 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(const DexFile& dex_file,
}
// Fail, get the declaring class.
const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
- ObjPtr<mirror::Class> klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader);
+ ObjPtr<mirror::Class> klass =
+ ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader);
if (klass == nullptr) {
Thread::Current()->AssertPendingException();
return nullptr;
}
if (klass->IsInterface()) {
- LOG(FATAL) << "ResolveAmbiguousMethod: unexpected method in interface: "
- << klass->PrettyClass();
- return nullptr;
- }
-
- // Search both direct and virtual methods
- resolved = klass->FindDirectMethod(dex_cache.Get(), method_idx, image_pointer_size_);
- if (resolved == nullptr) {
- resolved = klass->FindVirtualMethod(dex_cache.Get(), method_idx, image_pointer_size_);
+ resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_);
+ } else {
+ resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_);
}
return resolved;
@@ -8489,19 +8375,19 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod(
case DexFile::MethodHandleType::kInvokeStatic: {
kind = mirror::MethodHandle::Kind::kInvokeStatic;
receiver_count = 0;
- target_method = ResolveMethod<kNoICCECheckForCache>(self,
- method_handle.field_or_method_idx_,
- referrer,
- InvokeType::kStatic);
+ target_method = ResolveMethod<ResolveMode::kNoChecks>(self,
+ method_handle.field_or_method_idx_,
+ referrer,
+ InvokeType::kStatic);
break;
}
case DexFile::MethodHandleType::kInvokeInstance: {
kind = mirror::MethodHandle::Kind::kInvokeVirtual;
receiver_count = 1;
- target_method = ResolveMethod<kNoICCECheckForCache>(self,
- method_handle.field_or_method_idx_,
- referrer,
- InvokeType::kVirtual);
+ target_method = ResolveMethod<ResolveMode::kNoChecks>(self,
+ method_handle.field_or_method_idx_,
+ referrer,
+ InvokeType::kVirtual);
break;
}
case DexFile::MethodHandleType::kInvokeConstructor: {
@@ -8509,10 +8395,10 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod(
// are special cased later in this method.
kind = mirror::MethodHandle::Kind::kInvokeTransform;
receiver_count = 0;
- target_method = ResolveMethod<kNoICCECheckForCache>(self,
- method_handle.field_or_method_idx_,
- referrer,
- InvokeType::kDirect);
+ target_method = ResolveMethod<ResolveMode::kNoChecks>(self,
+ method_handle.field_or_method_idx_,
+ referrer,
+ InvokeType::kDirect);
break;
}
case DexFile::MethodHandleType::kInvokeDirect: {
@@ -8535,16 +8421,16 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod(
if (target_method->IsPrivate()) {
kind = mirror::MethodHandle::Kind::kInvokeDirect;
- target_method = ResolveMethod<kNoICCECheckForCache>(self,
- method_handle.field_or_method_idx_,
- referrer,
- InvokeType::kDirect);
+ target_method = ResolveMethod<ResolveMode::kNoChecks>(self,
+ method_handle.field_or_method_idx_,
+ referrer,
+ InvokeType::kDirect);
} else {
kind = mirror::MethodHandle::Kind::kInvokeSuper;
- target_method = ResolveMethod<kNoICCECheckForCache>(self,
- method_handle.field_or_method_idx_,
- referrer,
- InvokeType::kSuper);
+ target_method = ResolveMethod<ResolveMode::kNoChecks>(self,
+ method_handle.field_or_method_idx_,
+ referrer,
+ InvokeType::kSuper);
if (UNLIKELY(target_method == nullptr)) {
break;
}
@@ -8560,10 +8446,10 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod(
case DexFile::MethodHandleType::kInvokeInterface: {
kind = mirror::MethodHandle::Kind::kInvokeInterface;
receiver_count = 1;
- target_method = ResolveMethod<kNoICCECheckForCache>(self,
- method_handle.field_or_method_idx_,
- referrer,
- InvokeType::kInterface);
+ target_method = ResolveMethod<ResolveMode::kNoChecks>(self,
+ method_handle.field_or_method_idx_,
+ referrer,
+ InvokeType::kInterface);
break;
}
}
@@ -9180,14 +9066,14 @@ mirror::IfTable* ClassLinker::AllocIfTable(Thread* self, size_t ifcount) {
}
// Instantiate ResolveMethod.
-template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::kForceICCECheck>(
+template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
const DexFile& dex_file,
uint32_t method_idx,
Handle<mirror::DexCache> dex_cache,
Handle<mirror::ClassLoader> class_loader,
ArtMethod* referrer,
InvokeType type);
-template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::kNoICCECheckForCache>(
+template ArtMethod* ClassLinker::ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
const DexFile& dex_file,
uint32_t method_idx,
Handle<mirror::DexCache> dex_cache,
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 864d37fa89..3cf59f0295 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -281,10 +281,10 @@ class ClassLinker {
REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
// Determine whether a dex cache result should be trusted, or an IncompatibleClassChangeError
- // check should be performed even after a hit.
- enum ResolveMode { // private.
- kNoICCECheckForCache,
- kForceICCECheck
+ // check and IllegalAccessError check should be performed even after a hit.
+ enum class ResolveMode { // private.
+ kNoChecks,
+ kCheckICCEAndIAE
};
// Resolve a method with a given ID from the DexFile, storing the
@@ -302,17 +302,10 @@ class ClassLinker {
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
+ template <InvokeType type, ResolveMode kResolveMode>
ArtMethod* GetResolvedMethod(uint32_t method_idx, ArtMethod* referrer)
REQUIRES_SHARED(Locks::mutator_lock_);
- // This returns the class referred to by GetMethodId(method_idx).class_idx_. This might be
- // different then the declaring class of the resolved method due to copied
- // miranda/default/conflict methods.
- mirror::Class* ResolveReferencedClassOfMethod(uint32_t method_idx,
- Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
template <ResolveMode kResolveMode>
ArtMethod* ResolveMethod(Thread* self, uint32_t method_idx, ArtMethod* referrer, InvokeType type)
REQUIRES_SHARED(Locks::mutator_lock_)
@@ -1205,6 +1198,23 @@ class ClassLinker {
bool* new_conflict,
ArtMethod** imt) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Check invoke type against the referenced class. Throws IncompatibleClassChangeError
+ // (if `kThrowOnError`) and returns true on mismatch (kInterface on a non-interface class,
+ // kVirtual on interface, kDefault on interface for dex files not supporting default methods),
+ // otherwise returns false.
+ template <bool kThrowOnError, typename ClassGetter>
+ static bool CheckInvokeClassMismatch(ObjPtr<mirror::DexCache> dex_cache,
+ InvokeType type,
+ ClassGetter class_getter)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Helper that feeds the above function with `ClassGetter` doing `LookupResolvedType()`.
+ template <bool kThrow>
+ bool CheckInvokeClassMismatch(ObjPtr<mirror::DexCache> dex_cache,
+ InvokeType type,
+ uint32_t method_idx,
+ ObjPtr<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
std::vector<const DexFile*> boot_class_path_;
std::vector<std::unique_ptr<const DexFile>> boot_dex_files_;
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 03cc6c59c4..98d7c7ccca 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -1121,7 +1121,7 @@ TEST_F(ClassLinkerTest, StaticFields) {
// Static final primitives that are initialized by a compile-time constant
// expression resolve to a copy of a constant value from the constant pool.
// So <clinit> should be null.
- ArtMethod* clinit = statics->FindDirectMethod("<clinit>", "()V", kRuntimePointerSize);
+ ArtMethod* clinit = statics->FindClassMethod("<clinit>", "()V", kRuntimePointerSize);
EXPECT_TRUE(clinit == nullptr);
EXPECT_EQ(9U, statics->NumStaticFields());
@@ -1208,24 +1208,30 @@ TEST_F(ClassLinkerTest, Interfaces) {
EXPECT_TRUE(J->IsAssignableFrom(B.Get()));
const Signature void_sig = I->GetDexCache()->GetDexFile()->CreateSignature("()V");
- ArtMethod* Ii = I->FindVirtualMethod("i", void_sig, kRuntimePointerSize);
- ArtMethod* Jj1 = J->FindVirtualMethod("j1", void_sig, kRuntimePointerSize);
- ArtMethod* Jj2 = J->FindVirtualMethod("j2", void_sig, kRuntimePointerSize);
+ ArtMethod* Ii = I->FindClassMethod("i", void_sig, kRuntimePointerSize);
+ ArtMethod* Jj1 = J->FindClassMethod("j1", void_sig, kRuntimePointerSize);
+ ArtMethod* Jj2 = J->FindClassMethod("j2", void_sig, kRuntimePointerSize);
ArtMethod* Kj1 = K->FindInterfaceMethod("j1", void_sig, kRuntimePointerSize);
ArtMethod* Kj2 = K->FindInterfaceMethod("j2", void_sig, kRuntimePointerSize);
ArtMethod* Kk = K->FindInterfaceMethod("k", void_sig, kRuntimePointerSize);
- ArtMethod* Ai = A->FindVirtualMethod("i", void_sig, kRuntimePointerSize);
- ArtMethod* Aj1 = A->FindVirtualMethod("j1", void_sig, kRuntimePointerSize);
- ArtMethod* Aj2 = A->FindVirtualMethod("j2", void_sig, kRuntimePointerSize);
+ ArtMethod* Ai = A->FindClassMethod("i", void_sig, kRuntimePointerSize);
+ ArtMethod* Aj1 = A->FindClassMethod("j1", void_sig, kRuntimePointerSize);
+ ArtMethod* Aj2 = A->FindClassMethod("j2", void_sig, kRuntimePointerSize);
ASSERT_TRUE(Ii != nullptr);
+ ASSERT_FALSE(Ii->IsDirect());
ASSERT_TRUE(Jj1 != nullptr);
+ ASSERT_FALSE(Jj1->IsDirect());
ASSERT_TRUE(Jj2 != nullptr);
+ ASSERT_FALSE(Jj2->IsDirect());
ASSERT_TRUE(Kj1 != nullptr);
ASSERT_TRUE(Kj2 != nullptr);
ASSERT_TRUE(Kk != nullptr);
ASSERT_TRUE(Ai != nullptr);
+ ASSERT_FALSE(Ai->IsDirect());
ASSERT_TRUE(Aj1 != nullptr);
+ ASSERT_FALSE(Aj1->IsDirect());
ASSERT_TRUE(Aj2 != nullptr);
+ ASSERT_FALSE(Aj2->IsDirect());
EXPECT_NE(Ii, Ai);
EXPECT_NE(Jj1, Aj1);
EXPECT_NE(Jj2, Aj2);
@@ -1266,7 +1272,10 @@ TEST_F(ClassLinkerTest, ResolveVerifyAndClinit) {
hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", class_loader);
ArtMethod* clinit = klass->FindClassInitializer(kRuntimePointerSize);
- ArtMethod* getS0 = klass->FindDirectMethod("getS0", "()Ljava/lang/Object;", kRuntimePointerSize);
+ ArtMethod* getS0 =
+ klass->FindClassMethod("getS0", "()Ljava/lang/Object;", kRuntimePointerSize);
+ ASSERT_TRUE(getS0 != nullptr);
+ ASSERT_TRUE(getS0->IsStatic());
const DexFile::TypeId* type_id = dex_file->FindTypeId("LStaticsFromCode;");
ASSERT_TRUE(type_id != nullptr);
dex::TypeIndex type_idx = dex_file->GetIndexForTypeId(*type_id);
@@ -1489,9 +1498,12 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) {
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LMethodTypes;", class_loader)));
class_linker_->EnsureInitialized(soa.Self(), method_types, true, true);
- ArtMethod* method1 = method_types->FindVirtualMethod("method1",
- "(Ljava/lang/String;)Ljava/lang/String;",
- kRuntimePointerSize);
+ ArtMethod* method1 = method_types->FindClassMethod(
+ "method1",
+ "(Ljava/lang/String;)Ljava/lang/String;",
+ kRuntimePointerSize);
+ ASSERT_TRUE(method1 != nullptr);
+ ASSERT_FALSE(method1->IsDirect());
const DexFile& dex_file = *(method1->GetDexFile());
Handle<mirror::DexCache> dex_cache = hs.NewHandle(
@@ -1522,10 +1534,12 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) {
// Resolve the MethodType associated with a different method signature
// and assert it's different.
- ArtMethod* method2 = method_types->FindVirtualMethod(
+ ArtMethod* method2 = method_types->FindClassMethod(
"method2",
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
kRuntimePointerSize);
+ ASSERT_TRUE(method2 != nullptr);
+ ASSERT_FALSE(method2->IsDirect());
const DexFile::MethodId& method2_id = dex_file.GetMethodId(method2->GetDexMethodIndex());
Handle<mirror::MethodType> method2_type = hs.NewHandle(
class_linker_->ResolveMethodType(dex_file, method2_id.proto_idx_, dex_cache, class_loader));
diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h
index 41db4d8219..b163cdb8dc 100644
--- a/runtime/dex_file-inl.h
+++ b/runtime/dex_file-inl.h
@@ -181,19 +181,18 @@ inline bool Signature::operator==(const Signature& rhs) const {
if (lhs_shorty.find('L', 1) != StringPiece::npos) {
const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
const DexFile::TypeList* rhs_params = rhs.dex_file_->GetProtoParameters(*rhs.proto_id_);
- // Both lists are empty or have contents, or else shorty is broken.
- DCHECK_EQ(params == nullptr, rhs_params == nullptr);
- if (params != nullptr) {
- uint32_t params_size = params->Size();
- DCHECK_EQ(params_size, rhs_params->Size()); // Parameter list size must match.
- for (uint32_t i = 0; i < params_size; ++i) {
- const DexFile::TypeId& param_id = dex_file_->GetTypeId(params->GetTypeItem(i).type_idx_);
- const DexFile::TypeId& rhs_param_id =
- rhs.dex_file_->GetTypeId(rhs_params->GetTypeItem(i).type_idx_);
- if (!DexFileStringEquals(dex_file_, param_id.descriptor_idx_,
- rhs.dex_file_, rhs_param_id.descriptor_idx_)) {
- return false; // Parameter type mismatch.
- }
+ // We found a reference parameter in the matching shorty, so both lists must be non-empty.
+ DCHECK(params != nullptr);
+ DCHECK(rhs_params != nullptr);
+ uint32_t params_size = params->Size();
+ DCHECK_EQ(params_size, rhs_params->Size()); // Parameter list size must match.
+ for (uint32_t i = 0; i < params_size; ++i) {
+ const DexFile::TypeId& param_id = dex_file_->GetTypeId(params->GetTypeItem(i).type_idx_);
+ const DexFile::TypeId& rhs_param_id =
+ rhs.dex_file_->GetTypeId(rhs_params->GetTypeItem(i).type_idx_);
+ if (!DexFileStringEquals(dex_file_, param_id.descriptor_idx_,
+ rhs.dex_file_, rhs_param_id.descriptor_idx_)) {
+ return false; // Parameter type mismatch.
}
}
}
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 6547299853..828148a8b1 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -50,6 +50,8 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method,
const InlineInfoEncoding& encoding,
uint8_t inlining_depth)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(!outer_method->IsObsolete());
+
// This method is being used by artQuickResolutionTrampoline, before it sets up
// the passed parameters in a GC friendly way. Therefore we must never be
// suspended while executing it.
@@ -78,7 +80,8 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method,
}
// Lookup the declaring class of the inlined method.
- const DexFile* dex_file = caller->GetDexFile();
+ ObjPtr<mirror::DexCache> dex_cache = caller->GetDexCache();
+ const DexFile* dex_file = dex_cache->GetDexFile();
const DexFile::MethodId& method_id = dex_file->GetMethodId(method_index);
ArtMethod* inlined_method = caller->GetDexCacheResolvedMethod(method_index, kRuntimePointerSize);
if (inlined_method != nullptr && !inlined_method->IsRuntimeMethod()) {
@@ -90,25 +93,17 @@ inline ArtMethod* GetResolvedMethod(ArtMethod* outer_method,
mirror::ClassLoader* class_loader = caller->GetDeclaringClass()->GetClassLoader();
mirror::Class* klass = class_linker->LookupClass(self, descriptor, class_loader);
if (klass == nullptr) {
- LOG(FATAL) << "Could not find an inlined method from an .oat file: "
- << "the class " << descriptor << " was not found in the class loader of "
- << caller->PrettyMethod() << ". "
- << "This must be due to playing wrongly with class loaders";
+ LOG(FATAL) << "Could not find an inlined method from an .oat file: the class " << descriptor
+ << " was not found in the class loader of " << caller->PrettyMethod() << ". "
+ << "This must be due to playing wrongly with class loaders";
}
- // Lookup the method.
- const char* method_name = dex_file->GetMethodName(method_id);
- const Signature signature = dex_file->GetMethodSignature(method_id);
-
- inlined_method = klass->FindDeclaredDirectMethod(method_name, signature, kRuntimePointerSize);
+ inlined_method = klass->FindClassMethod(dex_cache, method_index, kRuntimePointerSize);
if (inlined_method == nullptr) {
- inlined_method = klass->FindDeclaredVirtualMethod(method_name, signature, kRuntimePointerSize);
- if (inlined_method == nullptr) {
- LOG(FATAL) << "Could not find an inlined method from an .oat file: "
- << "the class " << descriptor << " does not have "
- << method_name << signature << " declared. "
- << "This must be due to duplicate classes or playing wrongly with class loaders";
- }
+ LOG(FATAL) << "Could not find an inlined method from an .oat file: the class " << descriptor
+ << " does not have " << dex_file->GetMethodName(method_id)
+ << dex_file->GetMethodSignature(method_id) << " declared. "
+ << "This must be due to duplicate classes or playing wrongly with class loaders";
}
caller->SetDexCacheResolvedMethod(method_index, inlined_method, kRuntimePointerSize);
@@ -444,39 +439,20 @@ inline ArtMethod* FindMethodFromCode(uint32_t method_idx,
ArtMethod* referrer,
Thread* self) {
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- ArtMethod* resolved_method = class_linker->GetResolvedMethod(method_idx, referrer);
- if (resolved_method == nullptr) {
+ constexpr ClassLinker::ResolveMode resolve_mode =
+ access_check ? ClassLinker::ResolveMode::kCheckICCEAndIAE
+ : ClassLinker::ResolveMode::kNoChecks;
+ ArtMethod* resolved_method;
+ if (type == kStatic) {
+ resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, referrer, type);
+ } else {
StackHandleScope<1> hs(self);
- ObjPtr<mirror::Object> null_this = nullptr;
- HandleWrapperObjPtr<mirror::Object> h_this(
- hs.NewHandleWrapper(type == kStatic ? &null_this : this_object));
- constexpr ClassLinker::ResolveMode resolve_mode =
- access_check ? ClassLinker::kForceICCECheck
- : ClassLinker::kNoICCECheckForCache;
+ HandleWrapperObjPtr<mirror::Object> h_this(hs.NewHandleWrapper(this_object));
resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, referrer, type);
}
- // Resolution and access check.
if (UNLIKELY(resolved_method == nullptr)) {
DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
return nullptr; // Failure.
- } else if (access_check) {
- mirror::Class* methods_class = resolved_method->GetDeclaringClass();
- bool can_access_resolved_method =
- referrer->GetDeclaringClass()->CheckResolvedMethodAccess(methods_class,
- resolved_method,
- referrer->GetDexCache(),
- method_idx,
- type);
- if (UNLIKELY(!can_access_resolved_method)) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
- return nullptr; // Failure.
- }
- // Incompatible class change should have been handled in resolve method.
- if (UNLIKELY(resolved_method->CheckIncompatibleClassChange(type))) {
- ThrowIncompatibleClassChangeError(type, resolved_method->GetInvokeType(), resolved_method,
- referrer);
- return nullptr; // Failure.
- }
}
// Next, null pointer check.
if (UNLIKELY(*this_object == nullptr && type != kStatic)) {
@@ -690,24 +666,14 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx,
}
ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
ObjPtr<mirror::DexCache> dex_cache = referrer->GetDexCache();
- ArtMethod* resolved_method = dex_cache->GetResolvedMethod(method_idx, kRuntimePointerSize);
+ constexpr ClassLinker::ResolveMode resolve_mode = access_check
+ ? ClassLinker::ResolveMode::kCheckICCEAndIAE
+ : ClassLinker::ResolveMode::kNoChecks;
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
+ ArtMethod* resolved_method = linker->GetResolvedMethod<type, resolve_mode>(method_idx, referrer);
if (UNLIKELY(resolved_method == nullptr)) {
return nullptr;
}
- if (access_check) {
- // Check for incompatible class change errors and access.
- bool icce = resolved_method->CheckIncompatibleClassChange(type);
- if (UNLIKELY(icce)) {
- return nullptr;
- }
- ObjPtr<mirror::Class> methods_class = resolved_method->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CanAccess(methods_class) ||
- !referring_class->CanAccessMember(methods_class,
- resolved_method->GetAccessFlags()))) {
- // Potential illegal access, may need to refine the method's class.
- return nullptr;
- }
- }
if (type == kInterface) { // Most common form of slow path dispatch.
return this_object->GetClass()->FindVirtualMethodForInterface(resolved_method,
kRuntimePointerSize);
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 36885d8a1f..6abf7c5d6a 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1182,7 +1182,7 @@ extern "C" const void* artQuickResolutionTrampoline(
HandleWrapper<mirror::Object> h_receiver(
hs.NewHandleWrapper(virtual_or_interface ? &receiver : &dummy));
DCHECK_EQ(caller->GetDexFile(), called_method.dex_file);
- called = linker->ResolveMethod<ClassLinker::kForceICCECheck>(
+ called = linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
self, called_method.dex_method_index, caller, invoke_type);
// Update .bss entry in oat file if any.
@@ -1235,8 +1235,11 @@ extern "C" const void* artQuickResolutionTrampoline(
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(caller->GetDeclaringClass()->GetClassLoader()));
// TODO Maybe put this into a mirror::Class function.
- mirror::Class* ref_class = linker->ResolveReferencedClassOfMethod(
- called_method.dex_method_index, dex_cache, class_loader);
+ ObjPtr<mirror::Class> ref_class = linker->LookupResolvedType(
+ *dex_cache->GetDexFile(),
+ dex_cache->GetDexFile()->GetMethodId(called_method.dex_method_index).class_idx_,
+ dex_cache.Get(),
+ class_loader.Get());
if (ref_class->IsInterface()) {
called = ref_class->FindVirtualMethodForInterfaceSuper(called, kRuntimePointerSize);
} else {
@@ -2622,10 +2625,8 @@ extern "C" uintptr_t artInvokePolymorphic(
// Resolve method - it's either MethodHandle.invoke() or MethodHandle.invokeExact().
ClassLinker* linker = Runtime::Current()->GetClassLinker();
- ArtMethod* resolved_method = linker->ResolveMethod<ClassLinker::kForceICCECheck>(self,
- inst->VRegB(),
- caller_method,
- kVirtual);
+ ArtMethod* resolved_method = linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
+ self, inst->VRegB(), caller_method, kVirtual);
DCHECK((resolved_method ==
jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact)) ||
(resolved_method ==
diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc
index 2a601c9cf2..9e9fa71e60 100644
--- a/runtime/instrumentation_test.cc
+++ b/runtime/instrumentation_test.cc
@@ -16,6 +16,7 @@
#include "instrumentation.h"
+#include "art_method-inl.h"
#include "base/enums.h"
#include "common_runtime_test.h"
#include "common_throws.h"
@@ -484,10 +485,11 @@ TEST_F(InstrumentationTest, MethodExitObjectEvent) {
Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
ASSERT_TRUE(klass != nullptr);
- ArtMethod* method = klass->FindDeclaredDirectMethod("returnReference",
- "()Ljava/lang/Object;",
- kRuntimePointerSize);
+ ArtMethod* method =
+ klass->FindClassMethod("returnReference", "()Ljava/lang/Object;", kRuntimePointerSize);
ASSERT_TRUE(method != nullptr);
+ ASSERT_TRUE(method->IsDirect());
+ ASSERT_TRUE(method->GetDeclaringClass() == klass);
TestEvent(instrumentation::Instrumentation::kMethodExited,
/*event_method*/ method,
/*event_field*/ nullptr,
@@ -503,10 +505,10 @@ TEST_F(InstrumentationTest, MethodExitPrimEvent) {
Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
ASSERT_TRUE(klass != nullptr);
- ArtMethod* method = klass->FindDeclaredDirectMethod("returnPrimitive",
- "()I",
- kRuntimePointerSize);
+ ArtMethod* method = klass->FindClassMethod("returnPrimitive", "()I", kRuntimePointerSize);
ASSERT_TRUE(method != nullptr);
+ ASSERT_TRUE(method->IsDirect());
+ ASSERT_TRUE(method->GetDeclaringClass() == klass);
TestEvent(instrumentation::Instrumentation::kMethodExited,
/*event_method*/ method,
/*event_field*/ nullptr,
@@ -583,9 +585,11 @@ TEST_F(InstrumentationTest, DeoptimizeDirectMethod) {
Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
ASSERT_TRUE(klass != nullptr);
- ArtMethod* method_to_deoptimize = klass->FindDeclaredDirectMethod("instanceMethod", "()V",
- kRuntimePointerSize);
+ ArtMethod* method_to_deoptimize =
+ klass->FindClassMethod("instanceMethod", "()V", kRuntimePointerSize);
ASSERT_TRUE(method_to_deoptimize != nullptr);
+ ASSERT_TRUE(method_to_deoptimize->IsDirect());
+ ASSERT_TRUE(method_to_deoptimize->GetDeclaringClass() == klass);
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
EXPECT_FALSE(instr->IsDeoptimized(method_to_deoptimize));
@@ -630,9 +634,11 @@ TEST_F(InstrumentationTest, MixedDeoptimization) {
Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
mirror::Class* klass = class_linker->FindClass(soa.Self(), "LInstrumentation;", loader);
ASSERT_TRUE(klass != nullptr);
- ArtMethod* method_to_deoptimize = klass->FindDeclaredDirectMethod("instanceMethod", "()V",
- kRuntimePointerSize);
+ ArtMethod* method_to_deoptimize =
+ klass->FindClassMethod("instanceMethod", "()V", kRuntimePointerSize);
ASSERT_TRUE(method_to_deoptimize != nullptr);
+ ASSERT_TRUE(method_to_deoptimize->IsDirect());
+ ASSERT_TRUE(method_to_deoptimize->GetDeclaringClass() == klass);
EXPECT_FALSE(instr->AreAllMethodsDeoptimized());
EXPECT_FALSE(instr->IsDeoptimized(method_to_deoptimize));
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 0687b753d8..be2d34d5b0 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -593,10 +593,8 @@ bool DoInvokePolymorphic(Thread* self,
}
ArtMethod* invoke_method =
- class_linker->ResolveMethod<ClassLinker::kForceICCECheck>(self,
- invoke_method_idx,
- shadow_frame.GetMethod(),
- kVirtual);
+ class_linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
+ self, invoke_method_idx, shadow_frame.GetMethod(), kVirtual);
// There is a common dispatch method for method handles that takes
// arguments either from a range or an array of arguments depending
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 11eb4e0dc1..2c7282115f 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -265,7 +265,7 @@ void UnstartedRuntime::UnstartedClassNewInstance(
bool ok = false;
auto* cl = Runtime::Current()->GetClassLinker();
if (cl->EnsureInitialized(self, h_klass, true, true)) {
- auto* cons = h_klass->FindDeclaredDirectMethod("<init>", "()V", cl->GetImagePointerSize());
+ auto* cons = h_klass->FindConstructor("()V", cl->GetImagePointerSize());
if (cons != nullptr) {
Handle<mirror::Object> h_obj(hs.NewHandle(klass->AllocObject(self)));
CHECK(h_obj != nullptr); // We don't expect OOM at compile-time.
@@ -591,8 +591,7 @@ static void GetResourceAsStream(Thread* self,
}
auto* cl = Runtime::Current()->GetClassLinker();
- ArtMethod* constructor = h_class->FindDeclaredDirectMethod(
- "<init>", "([B)V", cl->GetImagePointerSize());
+ ArtMethod* constructor = h_class->FindConstructor("([B)V", cl->GetImagePointerSize());
if (constructor == nullptr) {
AbortTransactionOrFail(self, "Could not find ByteArrayInputStream constructor");
return;
@@ -1010,8 +1009,7 @@ static ObjPtr<mirror::Object> CreateInstanceOf(Thread* self, const char* class_d
Handle<mirror::Class> h_class(hs.NewHandle(klass));
Handle<mirror::Object> h_obj(hs.NewHandle(h_class->AllocObject(self)));
if (h_obj != nullptr) {
- ArtMethod* init_method = h_class->FindDirectMethod(
- "<init>", "()V", class_linker->GetImagePointerSize());
+ ArtMethod* init_method = h_class->FindConstructor("()V", class_linker->GetImagePointerSize());
if (init_method == nullptr) {
AbortTransactionOrFail(self, "Could not find <init> for %s", class_descriptor);
return nullptr;
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index c2ef72460d..3461a6503a 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -387,8 +387,9 @@ TEST_F(UnstartedRuntimeTest, StringInit) {
ScopedObjectAccess soa(self);
mirror::Class* klass = mirror::String::GetJavaLangString();
ArtMethod* method =
- klass->FindDeclaredDirectMethod("<init>", "(Ljava/lang/String;)V",
- Runtime::Current()->GetClassLinker()->GetImagePointerSize());
+ klass->FindConstructor("(Ljava/lang/String;)V",
+ Runtime::Current()->GetClassLinker()->GetImagePointerSize());
+ ASSERT_TRUE(method != nullptr);
// create instruction data for invoke-direct {v0, v1} of method with fake index
uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 };
@@ -966,12 +967,14 @@ TEST_F(UnstartedRuntimeTest, ThreadLocalGet) {
ASSERT_TRUE(floating_decimal != nullptr);
ASSERT_TRUE(class_linker->EnsureInitialized(self, floating_decimal, true, true));
- ArtMethod* caller_method = floating_decimal->FindDeclaredDirectMethod(
+ ArtMethod* caller_method = floating_decimal->FindClassMethod(
"getBinaryToASCIIBuffer",
"()Lsun/misc/FloatingDecimal$BinaryToASCIIBuffer;",
class_linker->GetImagePointerSize());
// floating_decimal->DumpClass(LOG_STREAM(ERROR), mirror::Class::kDumpClassFullDetail);
ASSERT_TRUE(caller_method != nullptr);
+ ASSERT_TRUE(caller_method->IsDirect());
+ ASSERT_TRUE(caller_method->GetDeclaringClass() == floating_decimal.Get());
ShadowFrame* caller_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, caller_method, 0);
shadow_frame->SetLink(caller_frame);
@@ -1020,10 +1023,12 @@ TEST_F(UnstartedRuntimeTest, FloatConversion) {
ASSERT_TRUE(double_class != nullptr);
ASSERT_TRUE(class_linker->EnsureInitialized(self, double_class, true, true));
- ArtMethod* method = double_class->FindDeclaredDirectMethod("toString",
- "(D)Ljava/lang/String;",
- class_linker->GetImagePointerSize());
+ ArtMethod* method = double_class->FindClassMethod("toString",
+ "(D)Ljava/lang/String;",
+ class_linker->GetImagePointerSize());
ASSERT_TRUE(method != nullptr);
+ ASSERT_TRUE(method->IsDirect());
+ ASSERT_TRUE(method->GetDeclaringClass() == double_class.Get());
// create instruction data for invoke-direct {v0, v1} of method with fake index
uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 };
@@ -1179,8 +1184,8 @@ class UnstartedClassForNameTest : public UnstartedRuntimeTest {
boot_cp.Assign(boot_cp_class->AllocObject(self)->AsClassLoader());
CHECK(boot_cp != nullptr);
- ArtMethod* boot_cp_init = boot_cp_class->FindDeclaredDirectMethod(
- "<init>", "()V", class_linker->GetImagePointerSize());
+ ArtMethod* boot_cp_init = boot_cp_class->FindConstructor(
+ "()V", class_linker->GetImagePointerSize());
CHECK(boot_cp_init != nullptr);
JValue result;
@@ -1333,8 +1338,8 @@ TEST_F(UnstartedRuntimeTest, ConstructorNewInstance0) {
Handle<mirror::String> input = hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, "abd"));
// Find the constructor.
- ArtMethod* throw_cons = throw_class->FindDeclaredDirectMethod(
- "<init>", "(Ljava/lang/String;)V", class_linker->GetImagePointerSize());
+ ArtMethod* throw_cons = throw_class->FindConstructor(
+ "(Ljava/lang/String;)V", class_linker->GetImagePointerSize());
ASSERT_TRUE(throw_cons != nullptr);
Handle<mirror::Constructor> cons;
if (class_linker->GetImagePointerSize() == PointerSize::k64) {
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 40b40d7a7a..927f94b588 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -233,17 +233,10 @@ static jmethodID FindMethodID(ScopedObjectAccess& soa, jclass jni_class,
}
ArtMethod* method = nullptr;
auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- if (is_static) {
- method = c->FindDirectMethod(name, sig, pointer_size);
- } else if (c->IsInterface()) {
+ if (c->IsInterface()) {
method = c->FindInterfaceMethod(name, sig, pointer_size);
} else {
- method = c->FindVirtualMethod(name, sig, pointer_size);
- if (method == nullptr) {
- // No virtual method matching the signature. Search declared
- // private methods and constructors.
- method = c->FindDeclaredDirectMethod(name, sig, pointer_size);
- }
+ method = c->FindClassMethod(name, sig, pointer_size);
}
if (method == nullptr || method->IsStatic() != is_static) {
ThrowNoSuchMethodError(soa, c, name, sig, is_static ? "static" : "non-static");
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 728b7c7708..3f00450319 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -626,9 +626,9 @@ class JniInternalTest : public CommonCompilerTest {
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_)));
mirror::Class* c = class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader);
const auto pointer_size = class_linker_->GetImagePointerSize();
- ArtMethod* method = direct ? c->FindDirectMethod(method_name, method_sig, pointer_size) :
- c->FindVirtualMethod(method_name, method_sig, pointer_size);
+ ArtMethod* method = c->FindClassMethod(method_name, method_sig, pointer_size);
ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig;
+ ASSERT_EQ(direct, method->IsDirect());
method->SetEntryPointFromQuickCompiledCode(class_linker_->GetRuntimeQuickGenericJniStub());
}
// Start runtime.
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 003cd4e2bc..121c259abb 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -533,7 +533,11 @@ inline ArtMethod* Class::FindVirtualMethodForInterface(ArtMethod* method,
PointerSize pointer_size) {
ObjPtr<Class> declaring_class = method->GetDeclaringClass();
DCHECK(declaring_class != nullptr) << PrettyClass();
- DCHECK(declaring_class->IsInterface()) << method->PrettyMethod();
+ if (UNLIKELY(!declaring_class->IsInterface())) {
+ DCHECK(declaring_class->IsObjectClass()) << method->PrettyMethod();
+ DCHECK(method->IsPublic() && !method->IsStatic());
+ return FindVirtualMethodForVirtual(method, pointer_size);
+ }
DCHECK(!method->IsCopied());
// TODO cache to improve lookup speed
const int32_t iftable_count = GetIfTableCount();
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index b0e5b6adbf..6f70b19aef 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -396,185 +396,276 @@ void Class::SetClassLoader(ObjPtr<ClassLoader> new_class_loader) {
}
}
-ArtMethod* Class::FindInterfaceMethod(const StringPiece& name,
- const StringPiece& signature,
- PointerSize pointer_size) {
- // Check the current class before checking the interfaces.
- ArtMethod* method = FindDeclaredVirtualMethod(name, signature, pointer_size);
- if (method != nullptr) {
- return method;
- }
-
- int32_t iftable_count = GetIfTableCount();
- ObjPtr<IfTable> iftable = GetIfTable();
- for (int32_t i = 0; i < iftable_count; ++i) {
- method = iftable->GetInterface(i)->FindDeclaredVirtualMethod(name, signature, pointer_size);
- if (method != nullptr) {
- return method;
+template <typename SignatureType>
+static inline ArtMethod* FindInterfaceMethodWithSignature(ObjPtr<Class> klass,
+ const StringPiece& name,
+ const SignatureType& signature,
+ PointerSize pointer_size)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // If the current class is not an interface, skip the search of its declared methods;
+ // such lookup is used only to distinguish between IncompatibleClassChangeError and
+ // NoSuchMethodError and the caller has already tried to search methods in the class.
+ if (LIKELY(klass->IsInterface())) {
+ // Search declared methods, both direct and virtual.
+ // (This lookup is used also for invoke-static on interface classes.)
+ for (ArtMethod& method : klass->GetDeclaredMethodsSlice(pointer_size)) {
+ if (method.GetName() == name && method.GetSignature() == signature) {
+ return &method;
+ }
+ }
+ }
+
+ // TODO: If there is a unique maximally-specific non-abstract superinterface method,
+ // we should return it, otherwise an arbitrary one can be returned.
+ ObjPtr<IfTable> iftable = klass->GetIfTable();
+ for (int32_t i = 0, iftable_count = iftable->Count(); i < iftable_count; ++i) {
+ ObjPtr<Class> iface = iftable->GetInterface(i);
+ for (ArtMethod& method : iface->GetVirtualMethodsSlice(pointer_size)) {
+ if (method.GetName() == name && method.GetSignature() == signature) {
+ return &method;
+ }
+ }
+ }
+
+ // Then search for public non-static methods in the java.lang.Object.
+ if (LIKELY(klass->IsInterface())) {
+ ObjPtr<Class> object_class = klass->GetSuperClass();
+ DCHECK(object_class->IsObjectClass());
+ for (ArtMethod& method : object_class->GetDeclaredMethodsSlice(pointer_size)) {
+ if (method.IsPublic() && !method.IsStatic() &&
+ method.GetName() == name && method.GetSignature() == signature) {
+ return &method;
+ }
}
}
return nullptr;
}
ArtMethod* Class::FindInterfaceMethod(const StringPiece& name,
+ const StringPiece& signature,
+ PointerSize pointer_size) {
+ return FindInterfaceMethodWithSignature(this, name, signature, pointer_size);
+}
+
+ArtMethod* Class::FindInterfaceMethod(const StringPiece& name,
const Signature& signature,
PointerSize pointer_size) {
- // Check the current class before checking the interfaces.
- ArtMethod* method = FindDeclaredVirtualMethod(name, signature, pointer_size);
- if (method != nullptr) {
- return method;
- }
-
- int32_t iftable_count = GetIfTableCount();
- ObjPtr<IfTable> iftable = GetIfTable();
- for (int32_t i = 0; i < iftable_count; ++i) {
- method = iftable->GetInterface(i)->FindDeclaredVirtualMethod(name, signature, pointer_size);
- if (method != nullptr) {
- return method;
- }
- }
- return nullptr;
+ return FindInterfaceMethodWithSignature(this, name, signature, pointer_size);
}
ArtMethod* Class::FindInterfaceMethod(ObjPtr<DexCache> dex_cache,
uint32_t dex_method_idx,
PointerSize pointer_size) {
- // Check the current class before checking the interfaces.
- ArtMethod* method = FindDeclaredVirtualMethod(dex_cache, dex_method_idx, pointer_size);
- if (method != nullptr) {
- return method;
- }
-
- int32_t iftable_count = GetIfTableCount();
- ObjPtr<IfTable> iftable = GetIfTable();
- for (int32_t i = 0; i < iftable_count; ++i) {
- method = iftable->GetInterface(i)->FindDeclaredVirtualMethod(
- dex_cache, dex_method_idx, pointer_size);
- if (method != nullptr) {
- return method;
- }
- }
- return nullptr;
+ // We always search by name and signature, ignoring the type index in the MethodId.
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
+ StringPiece name = dex_file.StringDataByIdx(method_id.name_idx_);
+ const Signature signature = dex_file.GetMethodSignature(method_id);
+ return FindInterfaceMethod(name, signature, pointer_size);
}
-ArtMethod* Class::FindDeclaredDirectMethod(const StringPiece& name,
- const StringPiece& signature,
- PointerSize pointer_size) {
- for (auto& method : GetDirectMethods(pointer_size)) {
- if (name == method.GetName() && method.GetSignature() == signature) {
- return &method;
+static inline bool IsInheritedMethod(ObjPtr<mirror::Class> klass,
+ ObjPtr<mirror::Class> declaring_class,
+ ArtMethod& method)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK_EQ(declaring_class, method.GetDeclaringClass());
+ DCHECK_NE(klass, declaring_class);
+ DCHECK(klass->IsArrayClass() ? declaring_class->IsObjectClass()
+ : klass->IsSubClass(declaring_class));
+ uint32_t access_flags = method.GetAccessFlags();
+ if ((access_flags & (kAccPublic | kAccProtected)) != 0) {
+ return true;
+ }
+ if ((access_flags & kAccPrivate) != 0) {
+ return false;
+ }
+ for (; klass != declaring_class; klass = klass->GetSuperClass()) {
+ if (!klass->IsInSamePackage(declaring_class)) {
+ return false;
}
}
- return nullptr;
+ return true;
}
-ArtMethod* Class::FindDeclaredDirectMethod(const StringPiece& name,
- const Signature& signature,
- PointerSize pointer_size) {
- for (auto& method : GetDirectMethods(pointer_size)) {
- if (name == method.GetName() && signature == method.GetSignature()) {
+template <typename SignatureType>
+static inline ArtMethod* FindClassMethodWithSignature(ObjPtr<Class> this_klass,
+ const StringPiece& name,
+ const SignatureType& signature,
+ PointerSize pointer_size)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Search declared methods first.
+ for (ArtMethod& method : this_klass->GetDeclaredMethodsSlice(pointer_size)) {
+ ArtMethod* np_method = method.GetInterfaceMethodIfProxy(pointer_size);
+ if (np_method->GetName() == name && np_method->GetSignature() == signature) {
return &method;
}
}
- return nullptr;
-}
-ArtMethod* Class::FindDeclaredDirectMethod(ObjPtr<DexCache> dex_cache,
- uint32_t dex_method_idx,
- PointerSize pointer_size) {
- if (GetDexCache() == dex_cache) {
- for (auto& method : GetDirectMethods(pointer_size)) {
- if (method.GetDexMethodIndex() == dex_method_idx) {
- return &method;
+ // Then search the superclass chain. If we find an inherited method, return it.
+ // If we find a method that's not inherited because of access restrictions,
+ // try to find a method inherited from an interface in copied methods.
+ ObjPtr<Class> klass = this_klass->GetSuperClass();
+ ArtMethod* uninherited_method = nullptr;
+ for (; klass != nullptr; klass = klass->GetSuperClass()) {
+ DCHECK(!klass->IsProxyClass());
+ for (ArtMethod& method : klass->GetDeclaredMethodsSlice(pointer_size)) {
+ if (method.GetName() == name && method.GetSignature() == signature) {
+ if (IsInheritedMethod(this_klass, klass, method)) {
+ return &method;
+ }
+ uninherited_method = &method;
+ break;
}
}
+ if (uninherited_method != nullptr) {
+ break;
+ }
}
- return nullptr;
-}
-ArtMethod* Class::FindDirectMethod(const StringPiece& name,
- const StringPiece& signature,
- PointerSize pointer_size) {
- for (ObjPtr<Class> klass = this; klass != nullptr; klass = klass->GetSuperClass()) {
- ArtMethod* method = klass->FindDeclaredDirectMethod(name, signature, pointer_size);
- if (method != nullptr) {
- return method;
+ // Then search copied methods.
+ // If we found a method that's not inherited, stop the search in its declaring class.
+ ObjPtr<Class> end_klass = klass;
+ DCHECK_EQ(uninherited_method != nullptr, end_klass != nullptr);
+ klass = this_klass;
+ if (UNLIKELY(klass->IsProxyClass())) {
+ DCHECK(klass->GetCopiedMethodsSlice(pointer_size).empty());
+ klass = klass->GetSuperClass();
+ }
+ for (; klass != end_klass; klass = klass->GetSuperClass()) {
+ DCHECK(!klass->IsProxyClass());
+ for (ArtMethod& method : klass->GetCopiedMethodsSlice(pointer_size)) {
+ if (method.GetName() == name && method.GetSignature() == signature) {
+ return &method; // No further check needed, copied methods are inherited by definition.
+ }
}
}
- return nullptr;
+ return uninherited_method; // Return the `uninherited_method` if any.
}
-ArtMethod* Class::FindDirectMethod(const StringPiece& name,
- const Signature& signature,
- PointerSize pointer_size) {
- for (ObjPtr<Class> klass = this; klass != nullptr; klass = klass->GetSuperClass()) {
- ArtMethod* method = klass->FindDeclaredDirectMethod(name, signature, pointer_size);
- if (method != nullptr) {
- return method;
- }
- }
- return nullptr;
+
+ArtMethod* Class::FindClassMethod(const StringPiece& name,
+ const StringPiece& signature,
+ PointerSize pointer_size) {
+ return FindClassMethodWithSignature(this, name, signature, pointer_size);
}
-ArtMethod* Class::FindDirectMethod(ObjPtr<DexCache> dex_cache,
- uint32_t dex_method_idx,
- PointerSize pointer_size) {
- for (ObjPtr<Class> klass = this; klass != nullptr; klass = klass->GetSuperClass()) {
- ArtMethod* method = klass->FindDeclaredDirectMethod(dex_cache, dex_method_idx, pointer_size);
- if (method != nullptr) {
- return method;
+ArtMethod* Class::FindClassMethod(const StringPiece& name,
+ const Signature& signature,
+ PointerSize pointer_size) {
+ return FindClassMethodWithSignature(this, name, signature, pointer_size);
+}
+
+ArtMethod* Class::FindClassMethod(ObjPtr<DexCache> dex_cache,
+ uint32_t dex_method_idx,
+ PointerSize pointer_size) {
+ // FIXME: Hijacking a proxy class by a custom class loader can break this assumption.
+ DCHECK(!IsProxyClass());
+
+ // First try to find a declared method by dex_method_idx if we have a dex_cache match.
+ ObjPtr<DexCache> this_dex_cache = GetDexCache();
+ if (this_dex_cache == dex_cache) {
+ // Lookup is always performed in the class referenced by the MethodId.
+ DCHECK_EQ(dex_type_idx_, GetDexFile().GetMethodId(dex_method_idx).class_idx_.index_);
+ for (ArtMethod& method : GetDeclaredMethodsSlice(pointer_size)) {
+ if (method.GetDexMethodIndex() == dex_method_idx) {
+ return &method;
+ }
+ }
+ }
+ // If not found, we need to search by name and signature.
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
+ const Signature signature = dex_file.GetMethodSignature(method_id);
+ StringPiece name; // Delay strlen() until actually needed.
+ // If we do not have a dex_cache match, try to find the declared method in this class now.
+ if (this_dex_cache != dex_cache && !GetDeclaredMethodsSlice(pointer_size).empty()) {
+ DCHECK(name.empty());
+ name = dex_file.StringDataByIdx(method_id.name_idx_);
+ for (ArtMethod& method : GetDeclaredMethodsSlice(pointer_size)) {
+ if (method.GetName() == name && method.GetSignature() == signature) {
+ return &method;
+ }
}
}
- return nullptr;
-}
-ArtMethod* Class::FindDeclaredDirectMethodByName(const StringPiece& name,
- PointerSize pointer_size) {
- for (auto& method : GetDirectMethods(pointer_size)) {
- ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size);
- if (name == np_method->GetName()) {
- return &method;
+ // Then search the superclass chain. If we find an inherited method, return it.
+ // If we find a method that's not inherited because of access restrictions,
+ // try to find a method inherited from an interface in copied methods.
+ ArtMethod* uninherited_method = nullptr;
+ ObjPtr<Class> klass = GetSuperClass();
+ for (; klass != nullptr; klass = klass->GetSuperClass()) {
+ ArtMethod* candidate_method = nullptr;
+ ArraySlice<ArtMethod> declared_methods = klass->GetDeclaredMethodsSlice(pointer_size);
+ if (klass->GetDexCache() == dex_cache) {
+ // Matching dex_cache. We cannot compare the `dex_method_idx` anymore because
+ // the type index differs, so compare the name index and proto index.
+ for (ArtMethod& method : declared_methods) {
+ const DexFile::MethodId& cmp_method_id = dex_file.GetMethodId(method.GetDexMethodIndex());
+ if (cmp_method_id.name_idx_ == method_id.name_idx_ &&
+ cmp_method_id.proto_idx_ == method_id.proto_idx_) {
+ candidate_method = &method;
+ break;
+ }
+ }
+ } else {
+ if (!declared_methods.empty() && name.empty()) {
+ name = dex_file.StringDataByIdx(method_id.name_idx_);
+ }
+ for (ArtMethod& method : declared_methods) {
+ if (method.GetName() == name && method.GetSignature() == signature) {
+ candidate_method = &method;
+ break;
+ }
+ }
+ }
+ if (candidate_method != nullptr) {
+ if (IsInheritedMethod(this, klass, *candidate_method)) {
+ return candidate_method;
+ } else {
+ uninherited_method = candidate_method;
+ break;
+ }
}
}
- return nullptr;
-}
-// TODO These should maybe be changed to be named FindOwnedVirtualMethod or something similar
-// because they do not only find 'declared' methods and will return copied methods. This behavior is
-// desired and correct but the naming can lead to confusion because in the java language declared
-// excludes interface methods which might be found by this.
-ArtMethod* Class::FindDeclaredVirtualMethod(const StringPiece& name,
- const StringPiece& signature,
- PointerSize pointer_size) {
- for (auto& method : GetVirtualMethods(pointer_size)) {
- ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size);
- if (name == np_method->GetName() && np_method->GetSignature() == signature) {
- return &method;
+ // Then search copied methods.
+ // If we found a method that's not inherited, stop the search in its declaring class.
+ ObjPtr<Class> end_klass = klass;
+ DCHECK_EQ(uninherited_method != nullptr, end_klass != nullptr);
+ // After we have searched the declared methods of the super-class chain,
+ // search copied methods which can contain methods from interfaces.
+ for (klass = this; klass != end_klass; klass = klass->GetSuperClass()) {
+ ArraySlice<ArtMethod> copied_methods = klass->GetCopiedMethodsSlice(pointer_size);
+ if (!copied_methods.empty() && name.empty()) {
+ name = dex_file.StringDataByIdx(method_id.name_idx_);
+ }
+ for (ArtMethod& method : copied_methods) {
+ if (method.GetName() == name && method.GetSignature() == signature) {
+ return &method; // No further check needed, copied methods are inherited by definition.
+ }
}
}
- return nullptr;
+ return uninherited_method; // Return the `uninherited_method` if any.
}
-ArtMethod* Class::FindDeclaredVirtualMethod(const StringPiece& name,
- const Signature& signature,
- PointerSize pointer_size) {
- for (auto& method : GetVirtualMethods(pointer_size)) {
- ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size);
- if (name == np_method->GetName() && signature == np_method->GetSignature()) {
+ArtMethod* Class::FindConstructor(const StringPiece& signature, PointerSize pointer_size) {
+ // Internal helper, never called on proxy classes. We can skip GetInterfaceMethodIfProxy().
+ DCHECK(!IsProxyClass());
+ StringPiece name("<init>");
+ for (ArtMethod& method : GetDirectMethodsSliceUnchecked(pointer_size)) {
+ if (method.GetName() == name && method.GetSignature() == signature) {
return &method;
}
}
return nullptr;
}
-ArtMethod* Class::FindDeclaredVirtualMethod(ObjPtr<DexCache> dex_cache,
- uint32_t dex_method_idx,
- PointerSize pointer_size) {
- if (GetDexCache() == dex_cache) {
- for (auto& method : GetDeclaredVirtualMethods(pointer_size)) {
- if (method.GetDexMethodIndex() == dex_method_idx) {
- return &method;
- }
+ArtMethod* Class::FindDeclaredDirectMethodByName(const StringPiece& name,
+ PointerSize pointer_size) {
+ for (auto& method : GetDirectMethods(pointer_size)) {
+ ArtMethod* const np_method = method.GetInterfaceMethodIfProxy(pointer_size);
+ if (name == np_method->GetName()) {
+ return &method;
}
}
return nullptr;
@@ -591,42 +682,6 @@ ArtMethod* Class::FindDeclaredVirtualMethodByName(const StringPiece& name,
return nullptr;
}
-ArtMethod* Class::FindVirtualMethod(const StringPiece& name,
- const StringPiece& signature,
- PointerSize pointer_size) {
- for (ObjPtr<Class> klass = this; klass != nullptr; klass = klass->GetSuperClass()) {
- ArtMethod* method = klass->FindDeclaredVirtualMethod(name, signature, pointer_size);
- if (method != nullptr) {
- return method;
- }
- }
- return nullptr;
-}
-
-ArtMethod* Class::FindVirtualMethod(const StringPiece& name,
- const Signature& signature,
- PointerSize pointer_size) {
- for (ObjPtr<Class> klass = this; klass != nullptr; klass = klass->GetSuperClass()) {
- ArtMethod* method = klass->FindDeclaredVirtualMethod(name, signature, pointer_size);
- if (method != nullptr) {
- return method;
- }
- }
- return nullptr;
-}
-
-ArtMethod* Class::FindVirtualMethod(ObjPtr<DexCache> dex_cache,
- uint32_t dex_method_idx,
- PointerSize pointer_size) {
- for (ObjPtr<Class> klass = this; klass != nullptr; klass = klass->GetSuperClass()) {
- ArtMethod* method = klass->FindDeclaredVirtualMethod(dex_cache, dex_method_idx, pointer_size);
- if (method != nullptr) {
- return method;
- }
- }
- return nullptr;
-}
-
ArtMethod* Class::FindVirtualMethodForInterfaceSuper(ArtMethod* method, PointerSize pointer_size) {
DCHECK(method->GetDeclaringClass()->IsInterface());
DCHECK(IsInterface()) << "Should only be called on a interface class";
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index e516a0636b..c62689796d 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -915,6 +915,13 @@ class MANAGED Class FINAL : public Object {
ArtMethod* FindVirtualMethodForVirtualOrInterface(ArtMethod* method, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Find a method with the given name and signature in an interface class.
+ //
+ // Search for the method declared in the class, then search for a method declared in any
+ // superinterface, then search the superclass java.lang.Object (implicitly declared methods
+ // in an interface without superinterfaces, see JLS 9.2, can be inherited, see JLS 9.4.1).
+ // TODO: Implement search for a unique maximally-specific non-abstract superinterface method.
+
ArtMethod* FindInterfaceMethod(const StringPiece& name,
const StringPiece& signature,
PointerSize pointer_size)
@@ -930,49 +937,46 @@ class MANAGED Class FINAL : public Object {
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- ArtMethod* FindDeclaredDirectMethod(const StringPiece& name,
- const StringPiece& signature,
- PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- ArtMethod* FindDeclaredDirectMethod(const StringPiece& name,
- const Signature& signature,
- PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- ArtMethod* FindDeclaredDirectMethod(ObjPtr<DexCache> dex_cache,
- uint32_t dex_method_idx,
- PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- ArtMethod* FindDirectMethod(const StringPiece& name,
- const StringPiece& signature,
- PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- ArtMethod* FindDirectMethod(const StringPiece& name,
- const Signature& signature,
- PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ // Find a method with the given name and signature in a non-interface class.
+ //
+ // Search for the method in the class, following the JLS rules which conflict with the RI
+ // in some cases. The JLS says that inherited methods are searched (JLS 15.12.2.1) and
+ // these can come from a superclass or a superinterface (JLS 8.4.8). We perform the
+ // following search:
+ // 1. Search the methods declared directly in the class. If we find a method with the
+ // given name and signature, return that method.
+ // 2. Search the methods declared in superclasses until we find a method with the given
+ // signature or complete the search in java.lang.Object. If we find a method with the
+ // given name and signature, check if it's been inherited by the class where we're
+ // performing the lookup (qualifying type). If it's inherited, return it. Otherwise,
+ // just remember the method and its declaring class and proceed to step 3.
+ // 3. Search "copied" methods (containing methods inherited from interfaces) in the class
+ // and its superclass chain. If we found a method in step 2 (which was not inherited,
+ // otherwise we would not be performing step 3), end the search when we reach its
+ // declaring class, otherwise search the entire superclass chain. If we find a method
+ // with the given name and signature, return that method.
+ // 4. Return the method found in step 2 if any (not inherited), or null.
+ //
+ // It's the responsibility of the caller to throw exceptions if the returned method (or null)
+ // does not satisfy the request. Special consideration should be given to the case where this
+ // function returns a method that's not inherited (found in step 2, returned in step 4).
- ArtMethod* FindDirectMethod(ObjPtr<DexCache> dex_cache,
- uint32_t dex_method_idx,
- PointerSize pointer_size)
+ ArtMethod* FindClassMethod(const StringPiece& name,
+ const StringPiece& signature,
+ PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- ArtMethod* FindDeclaredVirtualMethod(const StringPiece& name,
- const StringPiece& signature,
- PointerSize pointer_size)
+ ArtMethod* FindClassMethod(const StringPiece& name,
+ const Signature& signature,
+ PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- ArtMethod* FindDeclaredVirtualMethod(const StringPiece& name,
- const Signature& signature,
- PointerSize pointer_size)
+ ArtMethod* FindClassMethod(ObjPtr<DexCache> dex_cache,
+ uint32_t dex_method_idx,
+ PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- ArtMethod* FindDeclaredVirtualMethod(ObjPtr<DexCache> dex_cache,
- uint32_t dex_method_idx,
- PointerSize pointer_size)
+ ArtMethod* FindConstructor(const StringPiece& signature, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
ArtMethod* FindDeclaredVirtualMethodByName(const StringPiece& name,
@@ -983,21 +987,6 @@ class MANAGED Class FINAL : public Object {
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- ArtMethod* FindVirtualMethod(const StringPiece& name,
- const StringPiece& signature,
- PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- ArtMethod* FindVirtualMethod(const StringPiece& name,
- const Signature& signature,
- PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- ArtMethod* FindVirtualMethod(ObjPtr<DexCache> dex_cache,
- uint32_t dex_method_idx,
- PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
ArtMethod* FindClassInitializer(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_);
bool HasDefaultMethods() REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index 5b1ba8d010..194d9bca4a 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -128,14 +128,18 @@ TEST_F(DexCacheMethodHandlesTest, TestResolvedMethodTypes) {
hs.NewHandle(class_linker_->FindClass(soa.Self(), "LMethodTypes;", class_loader)));
class_linker_->EnsureInitialized(soa.Self(), method_types, true, true);
- ArtMethod* method1 = method_types->FindVirtualMethod(
+ ArtMethod* method1 = method_types->FindClassMethod(
"method1",
"(Ljava/lang/String;)Ljava/lang/String;",
kRuntimePointerSize);
- ArtMethod* method2 = method_types->FindVirtualMethod(
+ ASSERT_TRUE(method1 != nullptr);
+ ASSERT_FALSE(method1->IsDirect());
+ ArtMethod* method2 = method_types->FindClassMethod(
"method2",
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
kRuntimePointerSize);
+ ASSERT_TRUE(method2 != nullptr);
+ ASSERT_FALSE(method2->IsDirect());
const DexFile& dex_file = *(method1->GetDexFile());
Handle<mirror::DexCache> dex_cache = hs.NewHandle(
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 4f5ec8c2ae..e6e55a22e1 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -372,8 +372,7 @@ static void PreloadDexCachesResolveField(Handle<mirror::DexCache> dex_cache, uin
}
// Based on ClassLinker::ResolveMethod.
-static void PreloadDexCachesResolveMethod(Handle<mirror::DexCache> dex_cache, uint32_t method_idx,
- InvokeType invoke_type)
+static void PreloadDexCachesResolveMethod(Handle<mirror::DexCache> dex_cache, uint32_t method_idx)
REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* method = dex_cache->GetResolvedMethod(method_idx, kRuntimePointerSize);
if (method != nullptr) {
@@ -381,25 +380,15 @@ static void PreloadDexCachesResolveMethod(Handle<mirror::DexCache> dex_cache, ui
}
const DexFile* dex_file = dex_cache->GetDexFile();
const DexFile::MethodId& method_id = dex_file->GetMethodId(method_idx);
- ObjPtr<mirror::Class> klass = dex_cache->GetResolvedType(method_id.class_idx_);
+ ObjPtr<mirror::Class> klass =
+ ClassLinker::LookupResolvedType(method_id.class_idx_, dex_cache.Get(), nullptr);
if (klass == nullptr) {
return;
}
- switch (invoke_type) {
- case kDirect:
- case kStatic:
- method = klass->FindDirectMethod(dex_cache.Get(), method_idx, kRuntimePointerSize);
- break;
- case kInterface:
- method = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, kRuntimePointerSize);
- break;
- case kSuper:
- case kVirtual:
- method = klass->FindVirtualMethod(dex_cache.Get(), method_idx, kRuntimePointerSize);
- break;
- default:
- LOG(FATAL) << "Unreachable - invocation type: " << invoke_type;
- UNREACHABLE();
+ if (klass->IsInterface()) {
+ method = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, kRuntimePointerSize);
+ } else {
+ method = klass->FindClassMethod(dex_cache.Get(), method_idx, kRuntimePointerSize);
}
if (method == nullptr) {
return;
@@ -557,13 +546,11 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) {
}
for (; it.HasNextDirectMethod(); it.Next()) {
uint32_t method_idx = it.GetMemberIndex();
- InvokeType invoke_type = it.GetMethodInvokeType(class_def);
- PreloadDexCachesResolveMethod(dex_cache, method_idx, invoke_type);
+ PreloadDexCachesResolveMethod(dex_cache, method_idx);
}
for (; it.HasNextVirtualMethod(); it.Next()) {
uint32_t method_idx = it.GetMemberIndex();
- InvokeType invoke_type = it.GetMethodInvokeType(class_def);
- PreloadDexCachesResolveMethod(dex_cache, method_idx, invoke_type);
+ PreloadDexCachesResolveMethod(dex_cache, method_idx);
}
}
}
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 533a3c8768..c679d731fe 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -40,6 +40,7 @@
#include "art_method-inl.h"
#include "base/array_ref.h"
#include "base/logging.h"
+#include "base/stringpiece.h"
#include "class_linker-inl.h"
#include "debugger.h"
#include "dex_file.h"
@@ -572,13 +573,15 @@ void Redefiner::ClassRedefinition::FindAndAllocateObsoleteMethods(art::mirror::C
// Try and get the declared method. First try to get a virtual method then a direct method if that's
// not found.
static art::ArtMethod* FindMethod(art::Handle<art::mirror::Class> klass,
- const char* name,
+ art::StringPiece name,
art::Signature sig) REQUIRES_SHARED(art::Locks::mutator_lock_) {
- art::ArtMethod* m = klass->FindDeclaredVirtualMethod(name, sig, art::kRuntimePointerSize);
- if (m == nullptr) {
- m = klass->FindDeclaredDirectMethod(name, sig, art::kRuntimePointerSize);
+ DCHECK(!klass->IsProxyClass());
+ for (art::ArtMethod& m : klass->GetDeclaredMethodsSlice(art::kRuntimePointerSize)) {
+ if (m.GetName() == name && m.GetSignature() == sig) {
+ return &m;
+ }
}
- return m;
+ return nullptr;
}
bool Redefiner::ClassRedefinition::CheckSameMethods() {
diff --git a/runtime/openjdkjvmti/ti_search.cc b/runtime/openjdkjvmti/ti_search.cc
index 50a01fcd55..25bc5d6eb3 100644
--- a/runtime/openjdkjvmti/ti_search.cc
+++ b/runtime/openjdkjvmti/ti_search.cc
@@ -105,17 +105,21 @@ static void Update() REQUIRES_SHARED(art::Locks::mutator_lock_) {
}
art::ArtMethod* get_property =
- properties_class->FindDeclaredVirtualMethod(
+ properties_class->FindClassMethod(
"getProperty",
"(Ljava/lang/String;)Ljava/lang/String;",
art::kRuntimePointerSize);
DCHECK(get_property != nullptr);
+ DCHECK(!get_property->IsDirect());
+ DCHECK(get_property->GetDeclaringClass() == properties_class);
art::ArtMethod* set_property =
- properties_class->FindDeclaredVirtualMethod(
+ properties_class->FindClassMethod(
"setProperty",
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;",
art::kRuntimePointerSize);
DCHECK(set_property != nullptr);
+ DCHECK(!set_property->IsDirect());
+ DCHECK(set_property->GetDeclaringClass() == properties_class);
// This is an allocation. Do this late to avoid the need for handles.
ScopedLocalRef<jobject> cp_jobj(self->GetJniEnv(), nullptr);
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index 4e95b019b9..b055bf94d8 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -18,6 +18,7 @@
#include <vector>
#include "art_field-inl.h"
+#include "art_method-inl.h"
#include "base/enums.h"
#include "class_linker-inl.h"
#include "common_compiler_test.h"
@@ -63,21 +64,27 @@ class ProxyTest : public CommonCompilerTest {
jsize array_index = 0;
// Fill the method array
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
- ArtMethod* method = javaLangObject->FindDeclaredVirtualMethod(
+ ArtMethod* method = javaLangObject->FindClassMethod(
"equals", "(Ljava/lang/Object;)Z", kRuntimePointerSize);
CHECK(method != nullptr);
+ CHECK(!method->IsDirect());
+ CHECK(method->GetDeclaringClass() == javaLangObject);
DCHECK(!Runtime::Current()->IsActiveTransaction());
soa.Env()->SetObjectArrayElement(
proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
- method = javaLangObject->FindDeclaredVirtualMethod("hashCode", "()I", kRuntimePointerSize);
+ method = javaLangObject->FindClassMethod("hashCode", "()I", kRuntimePointerSize);
CHECK(method != nullptr);
+ CHECK(!method->IsDirect());
+ CHECK(method->GetDeclaringClass() == javaLangObject);
soa.Env()->SetObjectArrayElement(
proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
- method = javaLangObject->FindDeclaredVirtualMethod(
+ method = javaLangObject->FindClassMethod(
"toString", "()Ljava/lang/String;", kRuntimePointerSize);
CHECK(method != nullptr);
+ CHECK(!method->IsDirect());
+ CHECK(method->GetDeclaringClass() == javaLangObject);
soa.Env()->SetObjectArrayElement(
proxyClassMethods, array_index++, soa.AddLocalReference<jobject>(
mirror::Method::CreateFromArtMethod<kRuntimePointerSize, false>(soa.Self(), method)));
diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc
index 260be8f41f..d830387b24 100644
--- a/runtime/reference_table_test.cc
+++ b/runtime/reference_table_test.cc
@@ -56,8 +56,8 @@ static mirror::Object* CreateWeakReference(mirror::Object* referent)
h_ref_class->AllocObject(self)));
CHECK(h_ref_instance != nullptr);
- ArtMethod* constructor = h_ref_class->FindDeclaredDirectMethod(
- "<init>", "(Ljava/lang/Object;)V", class_linker->GetImagePointerSize());
+ ArtMethod* constructor = h_ref_class->FindConstructor(
+ "(Ljava/lang/Object;)V", class_linker->GetImagePointerSize());
CHECK(constructor != nullptr);
uint32_t args[2];
diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc
index 5eb75ed8b0..fa2f1e5793 100644
--- a/runtime/reflection_test.cc
+++ b/runtime/reflection_test.cc
@@ -108,9 +108,9 @@ class ReflectionTest : public CommonCompilerTest {
class_loader);
CHECK(c != nullptr);
- *method = is_static ? c->FindDirectMethod(method_name, method_signature, kRuntimePointerSize)
- : c->FindVirtualMethod(method_name, method_signature, kRuntimePointerSize);
- CHECK(method != nullptr);
+ *method = c->FindClassMethod(method_name, method_signature, kRuntimePointerSize);
+ CHECK(*method != nullptr);
+ CHECK_EQ(is_static, (*method)->IsStatic());
if (is_static) {
*receiver = nullptr;
@@ -520,10 +520,11 @@ TEST_F(ReflectionTest, StaticMainMethod) {
mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader);
ASSERT_TRUE(klass != nullptr);
- ArtMethod* method = klass->FindDirectMethod("main",
- "([Ljava/lang/String;)V",
- kRuntimePointerSize);
+ ArtMethod* method = klass->FindClassMethod("main",
+ "([Ljava/lang/String;)V",
+ kRuntimePointerSize);
ASSERT_TRUE(method != nullptr);
+ ASSERT_TRUE(method->IsStatic());
// Start runtime.
bool started = runtime_->Start();
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index adad7033f9..ebee5ea187 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -632,9 +632,10 @@ static jobject CreateSystemClassLoader(Runtime* runtime) {
hs.NewHandle(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_ClassLoader)));
CHECK(cl->EnsureInitialized(soa.Self(), class_loader_class, true, true));
- ArtMethod* getSystemClassLoader = class_loader_class->FindDirectMethod(
+ ArtMethod* getSystemClassLoader = class_loader_class->FindClassMethod(
"getSystemClassLoader", "()Ljava/lang/ClassLoader;", pointer_size);
CHECK(getSystemClassLoader != nullptr);
+ CHECK(getSystemClassLoader->IsStatic());
JValue result = InvokeWithJValues(soa,
nullptr,
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 9d4f116182..cdbb90888a 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2780,7 +2780,7 @@ void Thread::ThrowNewWrappedException(const char* exception_class_descriptor,
}
}
ArtMethod* exception_init_method =
- exception_class->FindDeclaredDirectMethod("<init>", signature, cl->GetImagePointerSize());
+ exception_class->FindConstructor(signature, cl->GetImagePointerSize());
CHECK(exception_init_method != nullptr) << "No <init>" << signature << " in "
<< PrettyDescriptor(exception_class_descriptor);
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index 634bd47f05..48b703a8ba 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -192,18 +192,21 @@ TEST_F(UtilsTest, JniShortName_JniLongName) {
ASSERT_TRUE(c != nullptr);
ArtMethod* m;
- m = c->FindVirtualMethod("charAt", "(I)C", kRuntimePointerSize);
+ m = c->FindClassMethod("charAt", "(I)C", kRuntimePointerSize);
ASSERT_TRUE(m != nullptr);
+ ASSERT_FALSE(m->IsDirect());
EXPECT_EQ("Java_java_lang_String_charAt", m->JniShortName());
EXPECT_EQ("Java_java_lang_String_charAt__I", m->JniLongName());
- m = c->FindVirtualMethod("indexOf", "(Ljava/lang/String;I)I", kRuntimePointerSize);
+ m = c->FindClassMethod("indexOf", "(Ljava/lang/String;I)I", kRuntimePointerSize);
ASSERT_TRUE(m != nullptr);
+ ASSERT_FALSE(m->IsDirect());
EXPECT_EQ("Java_java_lang_String_indexOf", m->JniShortName());
EXPECT_EQ("Java_java_lang_String_indexOf__Ljava_lang_String_2I", m->JniLongName());
- m = c->FindDirectMethod("copyValueOf", "([CII)Ljava/lang/String;", kRuntimePointerSize);
+ m = c->FindClassMethod("copyValueOf", "([CII)Ljava/lang/String;", kRuntimePointerSize);
ASSERT_TRUE(m != nullptr);
+ ASSERT_TRUE(m->IsStatic());
EXPECT_EQ("Java_java_lang_String_copyValueOf", m->JniShortName());
EXPECT_EQ("Java_java_lang_String_copyValueOf___3CII", m->JniLongName());
}
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index ea480f47cf..0351fd3afb 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -72,8 +72,8 @@ class VdexFile {
private:
static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' };
- // Last update: Change quickening info format.
- static constexpr uint8_t kVdexVersion[] = { '0', '0', '8', '\0' };
+ // Last update: Change method lookup.
+ static constexpr uint8_t kVdexVersion[] = { '0', '0', '9', '\0' };
uint8_t magic_[4];
uint8_t version_[4];
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index efb02f6205..6dc7953ee1 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -39,7 +39,6 @@
#include "indenter.h"
#include "intern_table.h"
#include "leb128.h"
-#include "method_resolution_kind.h"
#include "mirror/class.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache-inl.h"
@@ -230,7 +229,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethods(Thread* self,
}
previous_method_idx = method_idx;
InvokeType type = it->GetMethodInvokeType(class_def);
- ArtMethod* method = linker->ResolveMethod<ClassLinker::kNoICCECheckForCache>(
+ ArtMethod* method = linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
*dex_file, method_idx, dex_cache, class_loader, nullptr, type);
if (method == nullptr) {
DCHECK(self->IsExceptionPending());
@@ -3821,21 +3820,6 @@ const RegType& MethodVerifier::GetCaughtExceptionType() {
return *common_super;
}
-inline static MethodResolutionKind GetMethodResolutionKind(
- MethodType method_type, bool is_interface) {
- if (method_type == METHOD_DIRECT || method_type == METHOD_STATIC) {
- return kDirectMethodResolution;
- } else if (method_type == METHOD_INTERFACE) {
- return kInterfaceMethodResolution;
- } else if (method_type == METHOD_SUPER && is_interface) {
- return kInterfaceMethodResolution;
- } else {
- DCHECK(method_type == METHOD_VIRTUAL || method_type == METHOD_SUPER
- || method_type == METHOD_POLYMORPHIC);
- return kVirtualMethodResolution;
- }
-}
-
ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(
uint32_t dex_method_idx, MethodType method_type) {
const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx);
@@ -3849,47 +3833,41 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(
if (klass_type.IsUnresolvedTypes()) {
return nullptr; // Can't resolve Class so no more to do here
}
- mirror::Class* klass = klass_type.GetClass();
+ ObjPtr<mirror::Class> klass = klass_type.GetClass();
const RegType& referrer = GetDeclaringClass();
auto* cl = Runtime::Current()->GetClassLinker();
auto pointer_size = cl->GetImagePointerSize();
- MethodResolutionKind res_kind = GetMethodResolutionKind(method_type, klass->IsInterface());
ArtMethod* res_method = dex_cache_->GetResolvedMethod(dex_method_idx, pointer_size);
- bool stash_method = false;
if (res_method == nullptr) {
- const char* name = dex_file_->GetMethodName(method_id);
- const Signature signature = dex_file_->GetMethodSignature(method_id);
-
- if (res_kind == kDirectMethodResolution) {
- res_method = klass->FindDirectMethod(name, signature, pointer_size);
- } else if (res_kind == kVirtualMethodResolution) {
- res_method = klass->FindVirtualMethod(name, signature, pointer_size);
+ // Try to find the method with the appropriate lookup for the klass type (interface or not).
+ // If this lookup does not match `method_type`, errors shall be reported below.
+ if (klass->IsInterface()) {
+ res_method = klass->FindInterfaceMethod(dex_cache_.Get(), dex_method_idx, pointer_size);
} else {
- DCHECK_EQ(res_kind, kInterfaceMethodResolution);
- res_method = klass->FindInterfaceMethod(name, signature, pointer_size);
+ res_method = klass->FindClassMethod(dex_cache_.Get(), dex_method_idx, pointer_size);
}
-
if (res_method != nullptr) {
- stash_method = true;
- } else {
- // If a virtual or interface method wasn't found with the expected type, look in
- // the direct methods. This can happen when the wrong invoke type is used or when
- // a class has changed, and will be flagged as an error in later checks.
- // Note that in this case, we do not put the resolved method in the Dex cache
- // because it was not discovered using the expected type of method resolution.
- if (res_kind != kDirectMethodResolution) {
- // Record result of the initial resolution attempt.
- VerifierDeps::MaybeRecordMethodResolution(*dex_file_, dex_method_idx, res_kind, nullptr);
- // Change resolution type to 'direct' and try to resolve again.
- res_kind = kDirectMethodResolution;
- res_method = klass->FindDirectMethod(name, signature, pointer_size);
- }
+ dex_cache_->SetResolvedMethod(dex_method_idx, res_method, pointer_size);
}
}
- // Record result of method resolution attempt.
- VerifierDeps::MaybeRecordMethodResolution(*dex_file_, dex_method_idx, res_kind, res_method);
+ // Record result of method resolution attempt. The klass resolution has recorded whether
+ // the class is an interface or not and therefore the type of the lookup performed above.
+ // TODO: Maybe we should not record dependency if the invoke type does not match the lookup type.
+ VerifierDeps::MaybeRecordMethodResolution(*dex_file_, dex_method_idx, res_method);
+
+ if (res_method == nullptr) {
+ // Try to find the method also with the other type for better error reporting below
+ // but do not store such bogus lookup result in the DexCache or VerifierDeps.
+ if (klass->IsInterface()) {
+ res_method = klass->FindClassMethod(dex_cache_.Get(), dex_method_idx, pointer_size);
+ } else {
+ // If there was an interface method with the same signature,
+ // we would have found it also in the "copied" methods.
+ DCHECK(klass->FindInterfaceMethod(dex_cache_.Get(), dex_method_idx, pointer_size) == nullptr);
+ }
+ }
if (res_method == nullptr) {
Fail(VERIFY_ERROR_NO_METHOD) << "couldn't find method "
@@ -3940,11 +3918,6 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(
}
}
- // Only stash after the above passed. Otherwise the method wasn't guaranteed to be correct.
- if (stash_method) {
- dex_cache_->SetResolvedMethod(dex_method_idx, res_method, pointer_size);
- }
-
// Check if access is allowed.
if (!referrer.CanAccessMember(res_method->GetDeclaringClass(), res_method->GetAccessFlags())) {
Fail(VERIFY_ERROR_ACCESS_METHOD) << "illegal method access (call "
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 122e05f079..112eec847d 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -54,9 +54,7 @@ void VerifierDeps::MergeWith(const VerifierDeps& other,
MergeSets(my_deps->unassignable_types_, other_deps.unassignable_types_);
MergeSets(my_deps->classes_, other_deps.classes_);
MergeSets(my_deps->fields_, other_deps.fields_);
- MergeSets(my_deps->direct_methods_, other_deps.direct_methods_);
- MergeSets(my_deps->virtual_methods_, other_deps.virtual_methods_);
- MergeSets(my_deps->interface_methods_, other_deps.interface_methods_);
+ MergeSets(my_deps->methods_, other_deps.methods_);
for (dex::TypeIndex entry : other_deps.unverified_classes_) {
my_deps->unverified_classes_.push_back(entry);
}
@@ -317,7 +315,6 @@ void VerifierDeps::AddFieldResolution(const DexFile& dex_file,
void VerifierDeps::AddMethodResolution(const DexFile& dex_file,
uint32_t method_idx,
- MethodResolutionKind resolution_kind,
ArtMethod* method) {
DexFileDeps* dex_deps = GetDexFileDeps(dex_file);
if (dex_deps == nullptr) {
@@ -334,14 +331,7 @@ void VerifierDeps::AddMethodResolution(const DexFile& dex_file,
MethodResolution method_tuple(method_idx,
GetAccessFlags(method),
GetMethodDeclaringClassStringId(dex_file, method_idx, method));
- if (resolution_kind == kDirectMethodResolution) {
- dex_deps->direct_methods_.emplace(method_tuple);
- } else if (resolution_kind == kVirtualMethodResolution) {
- dex_deps->virtual_methods_.emplace(method_tuple);
- } else {
- DCHECK_EQ(resolution_kind, kInterfaceMethodResolution);
- dex_deps->interface_methods_.emplace(method_tuple);
- }
+ dex_deps->methods_.insert(method_tuple);
}
mirror::Class* VerifierDeps::FindOneClassPathBoundaryForInterface(mirror::Class* destination,
@@ -537,11 +527,10 @@ void VerifierDeps::MaybeRecordFieldResolution(const DexFile& dex_file,
void VerifierDeps::MaybeRecordMethodResolution(const DexFile& dex_file,
uint32_t method_idx,
- MethodResolutionKind resolution_kind,
ArtMethod* method) {
VerifierDeps* thread_deps = GetThreadLocalVerifierDeps();
if (thread_deps != nullptr) {
- thread_deps->AddMethodResolution(dex_file, method_idx, resolution_kind, method);
+ thread_deps->AddMethodResolution(dex_file, method_idx, method);
}
}
@@ -698,9 +687,7 @@ void VerifierDeps::Encode(const std::vector<const DexFile*>& dex_files,
EncodeSet(buffer, deps.unassignable_types_);
EncodeSet(buffer, deps.classes_);
EncodeSet(buffer, deps.fields_);
- EncodeSet(buffer, deps.direct_methods_);
- EncodeSet(buffer, deps.virtual_methods_);
- EncodeSet(buffer, deps.interface_methods_);
+ EncodeSet(buffer, deps.methods_);
EncodeUint16Vector(buffer, deps.unverified_classes_);
}
}
@@ -723,9 +710,7 @@ VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files,
DecodeSet(&data_start, data_end, &deps->unassignable_types_);
DecodeSet(&data_start, data_end, &deps->classes_);
DecodeSet(&data_start, data_end, &deps->fields_);
- DecodeSet(&data_start, data_end, &deps->direct_methods_);
- DecodeSet(&data_start, data_end, &deps->virtual_methods_);
- DecodeSet(&data_start, data_end, &deps->interface_methods_);
+ DecodeSet(&data_start, data_end, &deps->methods_);
DecodeUint16Vector(&data_start, data_end, &deps->unverified_classes_);
}
CHECK_LE(data_start, data_end);
@@ -763,9 +748,7 @@ bool VerifierDeps::DexFileDeps::Equals(const VerifierDeps::DexFileDeps& rhs) con
(unassignable_types_ == rhs.unassignable_types_) &&
(classes_ == rhs.classes_) &&
(fields_ == rhs.fields_) &&
- (direct_methods_ == rhs.direct_methods_) &&
- (virtual_methods_ == rhs.virtual_methods_) &&
- (interface_methods_ == rhs.interface_methods_) &&
+ (methods_ == rhs.methods_) &&
(unverified_classes_ == rhs.unverified_classes_);
}
@@ -825,27 +808,21 @@ void VerifierDeps::Dump(VariableIndentationOutputStream* vios) const {
}
}
- for (const auto& entry :
- { std::make_pair(kDirectMethodResolution, dep.second->direct_methods_),
- std::make_pair(kVirtualMethodResolution, dep.second->virtual_methods_),
- std::make_pair(kInterfaceMethodResolution, dep.second->interface_methods_) }) {
- for (const MethodResolution& method : entry.second) {
- const DexFile::MethodId& method_id = dex_file.GetMethodId(method.GetDexMethodIndex());
+ for (const MethodResolution& method : dep.second->methods_) {
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(method.GetDexMethodIndex());
+ vios->Stream()
+ << dex_file.GetMethodDeclaringClassDescriptor(method_id) << "->"
+ << dex_file.GetMethodName(method_id)
+ << dex_file.GetMethodSignature(method_id).ToString()
+ << " is expected to be ";
+ if (!method.IsResolved()) {
+ vios->Stream() << "unresolved\n";
+ } else {
vios->Stream()
- << dex_file.GetMethodDeclaringClassDescriptor(method_id) << "->"
- << dex_file.GetMethodName(method_id)
- << dex_file.GetMethodSignature(method_id).ToString()
- << " is expected to be ";
- if (!method.IsResolved()) {
- vios->Stream() << "unresolved\n";
- } else {
- vios->Stream()
- << "in class "
- << GetStringFromId(dex_file, method.GetDeclaringClassIndex())
- << ", have the access flags " << std::hex << method.GetAccessFlags() << std::dec
- << ", and be of kind " << entry.first
- << "\n";
- }
+ << "in class "
+ << GetStringFromId(dex_file, method.GetDeclaringClassIndex())
+ << ", have the access flags " << std::hex << method.GetAccessFlags() << std::dec
+ << "\n";
}
}
@@ -1030,7 +1007,6 @@ static std::string GetMethodDescription(const DexFile& dex_file, uint32_t index)
bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const std::set<MethodResolution>& methods,
- MethodResolutionKind kind,
Thread* self) const {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
PointerSize pointer_size = class_linker->GetImagePointerSize();
@@ -1054,27 +1030,20 @@ bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader,
}
DCHECK(cls->IsResolved());
ArtMethod* method = nullptr;
- if (kind == kDirectMethodResolution) {
- method = cls->FindDirectMethod(name, signature, pointer_size);
- } else if (kind == kVirtualMethodResolution) {
- method = cls->FindVirtualMethod(name, signature, pointer_size);
- } else {
- DCHECK_EQ(kind, kInterfaceMethodResolution);
+ if (cls->IsInterface()) {
method = cls->FindInterfaceMethod(name, signature, pointer_size);
+ } else {
+ method = cls->FindClassMethod(name, signature, pointer_size);
}
if (entry.IsResolved()) {
std::string temp;
if (method == nullptr) {
- LOG(INFO) << "VerifierDeps: Could not resolve "
- << kind
- << " method "
+ LOG(INFO) << "VerifierDeps: Could not resolve method "
<< GetMethodDescription(dex_file, entry.GetDexMethodIndex());
return false;
} else if (expected_decl_klass != method->GetDeclaringClass()->GetDescriptor(&temp)) {
- LOG(INFO) << "VerifierDeps: Unexpected declaring class for "
- << kind
- << " method resolution "
+ LOG(INFO) << "VerifierDeps: Unexpected declaring class for method resolution "
<< GetMethodDescription(dex_file, entry.GetDexMethodIndex())
<< " (expected="
<< expected_decl_klass
@@ -1083,9 +1052,7 @@ bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader,
<< ")";
return false;
} else if (entry.GetAccessFlags() != GetAccessFlags(method)) {
- LOG(INFO) << "VerifierDeps: Unexpected access flags for resolved "
- << kind
- << " method resolution "
+ LOG(INFO) << "VerifierDeps: Unexpected access flags for resolved method resolution "
<< GetMethodDescription(dex_file, entry.GetDexMethodIndex())
<< std::hex
<< " (expected="
@@ -1096,9 +1063,7 @@ bool VerifierDeps::VerifyMethods(Handle<mirror::ClassLoader> class_loader,
return false;
}
} else if (method != nullptr) {
- LOG(INFO) << "VerifierDeps: Unexpected successful resolution of "
- << kind
- << " method "
+ LOG(INFO) << "VerifierDeps: Unexpected successful resolution of method "
<< GetMethodDescription(dex_file, entry.GetDexMethodIndex());
return false;
}
@@ -1118,12 +1083,7 @@ bool VerifierDeps::VerifyDexFile(Handle<mirror::ClassLoader> class_loader,
result = result && VerifyClasses(class_loader, dex_file, deps.classes_, self);
result = result && VerifyFields(class_loader, dex_file, deps.fields_, self);
- result = result && VerifyMethods(
- class_loader, dex_file, deps.direct_methods_, kDirectMethodResolution, self);
- result = result && VerifyMethods(
- class_loader, dex_file, deps.virtual_methods_, kVirtualMethodResolution, self);
- result = result && VerifyMethods(
- class_loader, dex_file, deps.interface_methods_, kInterfaceMethodResolution, self);
+ result = result && VerifyMethods(class_loader, dex_file, deps.methods_, self);
return result;
}
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index 43eb948c64..b883a9e642 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -25,7 +25,6 @@
#include "base/mutex.h"
#include "dex_file_types.h"
#include "handle.h"
-#include "method_resolution_kind.h"
#include "obj_ptr.h"
#include "thread.h"
#include "verifier_enums.h" // For MethodVerifier::FailureKind.
@@ -88,12 +87,10 @@ class VerifierDeps {
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::verifier_deps_lock_);
- // Record the outcome `method` of resolving method `method_idx` from `dex_file`
- // using `res_kind` kind of method resolution algorithm. If `method` is null,
- // the method is assumed unresolved.
+ // Record the outcome `method` of resolving method `method_idx` from `dex_file`.
+ // If `method` is null, the method is assumed unresolved.
static void MaybeRecordMethodResolution(const DexFile& dex_file,
uint32_t method_idx,
- MethodResolutionKind res_kind,
ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::verifier_deps_lock_);
@@ -193,9 +190,7 @@ class VerifierDeps {
// Sets of recorded class/field/method resolutions.
std::set<ClassResolution> classes_;
std::set<FieldResolution> fields_;
- std::set<MethodResolution> direct_methods_;
- std::set<MethodResolution> virtual_methods_;
- std::set<MethodResolution> interface_methods_;
+ std::set<MethodResolution> methods_;
// List of classes that were not fully verified in that dex file.
std::vector<dex::TypeIndex> unverified_classes_;
@@ -267,7 +262,6 @@ class VerifierDeps {
void AddMethodResolution(const DexFile& dex_file,
uint32_t method_idx,
- MethodResolutionKind res_kind,
ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::verifier_deps_lock_);
@@ -321,7 +315,6 @@ class VerifierDeps {
bool VerifyMethods(Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const std::set<MethodResolution>& methods,
- MethodResolutionKind kind,
Thread* self) const
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/test/162-method-resolution/expected.txt b/test/162-method-resolution/expected.txt
new file mode 100644
index 0000000000..1bf39c90de
--- /dev/null
+++ b/test/162-method-resolution/expected.txt
@@ -0,0 +1,43 @@
+Calling Test1Derived.test():
+Test1Derived.foo()
+Calling Test1User.test():
+Caught java.lang.reflect.InvocationTargetException
+ caused by java.lang.IllegalAccessError
+Calling Test1User2.test():
+Caught java.lang.reflect.InvocationTargetException
+ caused by java.lang.IllegalAccessError
+Calling Test2User.test():
+Caught java.lang.reflect.InvocationTargetException
+ caused by java.lang.IncompatibleClassChangeError
+Calling Test2User2.test():
+Test2Base.foo()
+Calling Test3User.test():
+Caught java.lang.reflect.InvocationTargetException
+ caused by java.lang.IncompatibleClassChangeError
+Calling Test4User.test():
+Test4Derived@...
+Calling Test5User.test():
+Test5Derived.foo()
+Calling Test5User2.test():
+Caught java.lang.reflect.InvocationTargetException
+ caused by java.lang.IncompatibleClassChangeError
+Calling Test6User.test():
+Test6Derived@...
+Calling Test6User2.test():
+Caught java.lang.reflect.InvocationTargetException
+ caused by java.lang.IncompatibleClassChangeError
+Calling Test7User.test():
+Test7Interface.foo()
+Calling Test7User2.test():
+Caught java.lang.reflect.InvocationTargetException
+ caused by java.lang.IllegalAccessError
+Calling Test8User.test():
+Test8Derived.foo()
+Calling Test8User2.test():
+Caught java.lang.reflect.InvocationTargetException
+ caused by java.lang.IncompatibleClassChangeError
+Calling Test9User.test():
+Test9Derived.foo()
+Calling Test9User2.test():
+Caught java.lang.reflect.InvocationTargetException
+ caused by java.lang.IncompatibleClassChangeError
diff --git a/test/162-method-resolution/info.txt b/test/162-method-resolution/info.txt
new file mode 100644
index 0000000000..ff57a9ae9f
--- /dev/null
+++ b/test/162-method-resolution/info.txt
@@ -0,0 +1,4 @@
+Tests that the method resolution is consistent with JLS and the RI.
+Where the RI conflicts with JLS, we follow the JLS and suppress the divergence
+when the test is executed with --jvm.
+(See Main.java for per-test details.)
diff --git a/test/162-method-resolution/jasmin-multidex/Test1User.j b/test/162-method-resolution/jasmin-multidex/Test1User.j
new file mode 100644
index 0000000000..09ba77b689
--- /dev/null
+++ b/test/162-method-resolution/jasmin-multidex/Test1User.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2017 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.
+
+.class public Test1User
+.super java/lang/Object
+
+.method public static test()V
+ .limit stack 2
+ .limit locals 0
+ new Test1Derived
+ dup
+ invokespecial Test1Derived.<init>()V
+ invokevirtual Test1Derived.foo()V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin-multidex/Test3User.j b/test/162-method-resolution/jasmin-multidex/Test3User.j
new file mode 100644
index 0000000000..90f3a4e578
--- /dev/null
+++ b/test/162-method-resolution/jasmin-multidex/Test3User.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2017 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.
+
+.class public Test3User
+.super java/lang/Object
+
+.method public static test()V
+ .limit stack 2
+ .limit locals 0
+ new Test3Derived
+ dup
+ invokespecial Test3Derived.<init>()V
+ invokevirtual Test3Derived.foo()V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test1Derived.j b/test/162-method-resolution/jasmin/Test1Derived.j
new file mode 100644
index 0000000000..d754c64c64
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test1Derived.j
@@ -0,0 +1,43 @@
+; Copyright (C) 2017 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.
+
+.class public Test1Derived
+.super Test1Base
+
+.method public <init>()V
+ .limit stack 1
+ .limit locals 1
+ aload_0
+ invokespecial Test1Base.<init>()V
+ return
+.end method
+
+.method public static test()V
+ .limit stack 2
+ .limit locals 0
+ new Test1Derived
+ dup
+ invokespecial Test1Derived.<init>()V
+ invokespecial Test1Derived.foo()V
+ return
+.end method
+
+.method private foo()V
+ .limit stack 2
+ .limit locals 1
+ getstatic java/lang/System/out Ljava/io/PrintStream;
+ ldc "Test1Derived.foo()"
+ invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test1User2.j b/test/162-method-resolution/jasmin/Test1User2.j
new file mode 100644
index 0000000000..8af9aab930
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test1User2.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2017 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.
+
+.class public Test1User2
+.super java/lang/Object
+
+.method public static test()V
+ .limit stack 2
+ .limit locals 0
+ new Test1Derived
+ dup
+ invokespecial Test1Derived.<init>()V
+ invokevirtual Test1Derived.foo()V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test2Derived.j b/test/162-method-resolution/jasmin/Test2Derived.j
new file mode 100644
index 0000000000..bb4525d250
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test2Derived.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2017 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.
+
+.class public Test2Derived
+.super Test2Base
+.implements Test2Interface
+
+.method public <init>()V
+ .limit stack 1
+ .limit locals 1
+ aload_0
+ invokespecial Test2Base.<init>()V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test2User.j b/test/162-method-resolution/jasmin/Test2User.j
new file mode 100644
index 0000000000..2cce074898
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test2User.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2017 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.
+
+.class public Test2User
+.super java/lang/Object
+
+.method public static test()V
+ .limit stack 2
+ .limit locals 0
+ new Test2Derived
+ dup
+ invokespecial Test2Derived.<init>()V
+ invokevirtual Test2Derived.foo()V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test2User2.j b/test/162-method-resolution/jasmin/Test2User2.j
new file mode 100644
index 0000000000..eb80f3213e
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test2User2.j
@@ -0,0 +1,23 @@
+; Copyright (C) 2017 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.
+
+.class public Test2User2
+.super java/lang/Object
+
+.method public static test()V
+ .limit stack 0
+ .limit locals 0
+ invokestatic Test2Derived.foo()V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test3Derived.j b/test/162-method-resolution/jasmin/Test3Derived.j
new file mode 100644
index 0000000000..2bf4bf1552
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test3Derived.j
@@ -0,0 +1,25 @@
+; Copyright (C) 2017 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.
+
+.class public Test3Derived
+.super Test3Base
+.implements Test3Interface
+
+.method public <init>()V
+ .limit stack 1
+ .limit locals 1
+ aload_0
+ invokespecial Test3Base.<init>()V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test4User.j b/test/162-method-resolution/jasmin/Test4User.j
new file mode 100644
index 0000000000..5b65368a87
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test4User.j
@@ -0,0 +1,29 @@
+; Copyright (C) 2017 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.
+
+.class public Test4User
+.super java/lang/Object
+
+.method public static test()V
+ .limit stack 3
+ .limit locals 0
+ getstatic java/lang/System/out Ljava/io/PrintStream;
+ new Test4Derived
+ dup
+ invokespecial Test4Derived.<init>()V
+ invokeinterface Test4Interface.toString()Ljava/lang/String; 1
+ invokestatic Main.normalizeToString(Ljava/lang/String;)Ljava/lang/String;
+ invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test5User.j b/test/162-method-resolution/jasmin/Test5User.j
new file mode 100644
index 0000000000..036e366dd5
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test5User.j
@@ -0,0 +1,40 @@
+; Copyright (C) 2017 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.
+
+.class public Test5User
+.super java/lang/Object
+
+.method public static test()V
+ .limit stack 2
+ .limit locals 1
+ new Test5Derived
+ dup
+ invokespecial Test5Derived.<init>()V
+ astore_0
+
+ ; Call an unresolved method bar() to force verification at runtime
+ ; to populate the dex cache entry for Test5Base.foo()V.
+ ; try { b.bar(); } catch (IncompatibleClassChangeError icce) { }
+ aload_0
+ dup ; Bogus operand to be swallowed by the pop in the non-exceptional path.
+ catch_begin:
+ invokevirtual Test5Derived.bar()V
+ catch_end:
+ pop ; Pops the exception or the bogus operand from above.
+ .catch java/lang/IncompatibleClassChangeError from catch_begin to catch_end using catch_end
+
+ aload_0
+ invokevirtual Test5Derived.foo()V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test5User2.j b/test/162-method-resolution/jasmin/Test5User2.j
new file mode 100644
index 0000000000..9484a69d4d
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test5User2.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2017 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.
+
+.class public Test5User2
+.super java/lang/Object
+
+.method public static test()V
+ .limit stack 2
+ .limit locals 0
+ new Test5Derived
+ dup
+ invokespecial Test5Derived.<init>()V
+ invokeinterface Test5Derived.foo()V 1
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test6User.j b/test/162-method-resolution/jasmin/Test6User.j
new file mode 100644
index 0000000000..55b43f1fe2
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test6User.j
@@ -0,0 +1,29 @@
+; Copyright (C) 2017 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.
+
+.class public Test6User
+.super java/lang/Object
+
+.method public static test()V
+ .limit stack 3
+ .limit locals 0
+ getstatic java/lang/System/out Ljava/io/PrintStream;
+ new Test6Derived
+ dup
+ invokespecial Test6Derived.<init>()V
+ invokeinterface Test6Interface.toString()Ljava/lang/String; 1
+ invokestatic Main.normalizeToString(Ljava/lang/String;)Ljava/lang/String;
+ invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test6User2.j b/test/162-method-resolution/jasmin/Test6User2.j
new file mode 100644
index 0000000000..ab9ac0eddc
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test6User2.j
@@ -0,0 +1,29 @@
+; Copyright (C) 2017 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.
+
+.class public Test6User2
+.super java/lang/Object
+
+.method public static test()V
+ .limit stack 3
+ .limit locals 0
+ getstatic java/lang/System/out Ljava/io/PrintStream;
+ new Test6Derived
+ dup
+ invokespecial Test6Derived.<init>()V
+ invokevirtual Test6Interface.toString()Ljava/lang/String;
+ invokestatic Main.normalizeToString(Ljava/lang/String;)Ljava/lang/String;
+ invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test8Derived.j b/test/162-method-resolution/jasmin/Test8Derived.j
new file mode 100644
index 0000000000..73f8b28b57
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test8Derived.j
@@ -0,0 +1,33 @@
+; Copyright (C) 2017 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.
+
+.class public Test8Derived
+.super Test8Base
+
+.method public <init>()V
+ .limit stack 1
+ .limit locals 1
+ aload_0
+ invokespecial Test8Base.<init>()V
+ return
+.end method
+
+.method public foo()V
+ .limit stack 2
+ .limit locals 1
+ getstatic java/lang/System/out Ljava/io/PrintStream;
+ ldc "Test8Derived.foo()"
+ invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test8User.j b/test/162-method-resolution/jasmin/Test8User.j
new file mode 100644
index 0000000000..af60c6ecb1
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test8User.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2017 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.
+
+.class public Test8User
+.super java/lang/Object
+
+.method public static test()V
+ .limit stack 2
+ .limit locals 0
+ new Test8Derived
+ dup
+ invokespecial Test8Derived.<init>()V
+ invokevirtual Test8Derived.foo()V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test8User2.j b/test/162-method-resolution/jasmin/Test8User2.j
new file mode 100644
index 0000000000..5cdb95ca8b
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test8User2.j
@@ -0,0 +1,23 @@
+; Copyright (C) 2017 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.
+
+.class public Test8User2
+.super java/lang/Object
+
+.method public static test()V
+ .limit stack 0
+ .limit locals 0
+ invokestatic Test8Derived.foo()V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test9Derived.j b/test/162-method-resolution/jasmin/Test9Derived.j
new file mode 100644
index 0000000000..789f0f2859
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test9Derived.j
@@ -0,0 +1,33 @@
+; Copyright (C) 2017 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.
+
+.class public Test9Derived
+.super Test9Base
+
+.method public <init>()V
+ .limit stack 1
+ .limit locals 1
+ aload_0
+ invokespecial Test9Base.<init>()V
+ return
+.end method
+
+.method public static foo()V
+ .limit stack 2
+ .limit locals 1
+ getstatic java/lang/System/out Ljava/io/PrintStream;
+ ldc "Test9Derived.foo()"
+ invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test9User.j b/test/162-method-resolution/jasmin/Test9User.j
new file mode 100644
index 0000000000..81f9a7d457
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test9User.j
@@ -0,0 +1,23 @@
+; Copyright (C) 2017 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.
+
+.class public Test9User
+.super java/lang/Object
+
+.method public static test()V
+ .limit stack 0
+ .limit locals 0
+ invokestatic Test9Derived.foo()V
+ return
+.end method
diff --git a/test/162-method-resolution/jasmin/Test9User2.j b/test/162-method-resolution/jasmin/Test9User2.j
new file mode 100644
index 0000000000..ae53905b76
--- /dev/null
+++ b/test/162-method-resolution/jasmin/Test9User2.j
@@ -0,0 +1,26 @@
+; Copyright (C) 2017 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.
+
+.class public Test9User2
+.super java/lang/Object
+
+.method public static test()V
+ .limit stack 2
+ .limit locals 0
+ new Test9Derived
+ dup
+ invokespecial Test9Derived.<init>()V
+ invokevirtual Test9Derived.foo()V
+ return
+.end method
diff --git a/test/162-method-resolution/multidex.jpp b/test/162-method-resolution/multidex.jpp
new file mode 100644
index 0000000000..22e3aeeadc
--- /dev/null
+++ b/test/162-method-resolution/multidex.jpp
@@ -0,0 +1,117 @@
+Test1Base:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test1Base
+Test1Derived:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test1Derived
+Test1User2:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test1User2
+
+Test2Base:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test2Base
+Test2Derived:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test2Derived
+Test2Interface:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test2Interface
+Test2User:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test2User
+Test2User2:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test2User2
+
+Test3Base:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test3Base
+Test3Derived:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test3Derived
+Test3Interface:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test3Interface
+
+Test4Interface:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test4Interface
+Test4Derived:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test4Derived
+Test4User:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test4User
+
+Test5Interface:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test5Interface
+Test5Base:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test5Base
+Test5Derived:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test5Derived
+Test5User:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test5User
+Test5User2:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test5User2
+
+Test6Interface:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test6Interface
+Test6Derived:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test6Derived
+Test6User:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test6User
+Test6User2:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test6User2
+
+Test7Base:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test7Base
+Test7Interface:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test7Interface
+Test7Derived:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test7Derived
+Test7User:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test7User
+
+Test8Base:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test8Base
+Test8Derived:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test8Derived
+Test8User:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test8User
+Test8User2:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test8User2
+
+Test9Base:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test9Base
+Test9Derived:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test9Derived
+Test9User:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test9User
+Test9User2:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Test9User2
+
+Main:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Main
diff --git a/test/162-method-resolution/src/Main.java b/test/162-method-resolution/src/Main.java
new file mode 100644
index 0000000000..fa95aa755c
--- /dev/null
+++ b/test/162-method-resolution/src/Main.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+ public static void main(String[] args) {
+ // Check if we're running dalvik or RI.
+ usingRI = false;
+ try {
+ Class.forName("dalvik.system.PathClassLoader");
+ } catch (ClassNotFoundException e) {
+ usingRI = true;
+ }
+
+ try {
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+ test6();
+ test7();
+ test8();
+ test9();
+
+ // TODO: How to test that interface method resolution returns the unique
+ // maximally-specific non-abstract superinterface method if there is one?
+ // Maybe reflection? (This is not even implemented yet!)
+ } catch (Throwable t) {
+ t.printStackTrace(System.out);
+ }
+ }
+
+ /*
+ * Test1
+ * -----
+ * Tested functions:
+ * public class Test1Base {
+ * public void foo() { ... }
+ * }
+ * public class Test1Derived extends Test1Base {
+ * private void foo() { ... }
+ * ...
+ * }
+ * Tested invokes:
+ * invoke-direct Test1Derived.foo()V from Test1Derived in first dex file
+ * expected: executes Test1Derived.foo()V
+ * invoke-virtual Test1Derived.foo()V from Test1User in second dex file
+ * expected: throws IllegalAccessError (JLS 15.12.4.3)
+ * invoke-virtual Test1Derived.foo()V from Test1User2 in first dex file
+ * expected: throws IllegalAccessError (JLS 15.12.4.3)
+ *
+ * Previously, the behavior was inconsistent between dex files, throwing ICCE
+ * from one and invoking the method from another. This was because the lookups for
+ * direct and virtual methods were independent but results were stored in a single
+ * slot in the DexCache method array and then retrieved from there without checking
+ * the resolution kind. Thus, the first invoke-direct stored the private
+ * Test1Derived.foo() in the DexCache and the attempt to use invoke-virtual
+ * from the same dex file (by Test1User2) would throw ICCE. However, the same
+ * invoke-virtual from a different dex file (by Test1User) would ignore the
+ * direct method Test1Derived.foo() and find the Test1Base.foo() and call it.
+ *
+ * The method lookup has been changed and we now consistently find the private
+ * Derived.foo() and throw ICCE for both invoke-virtual calls.
+ *
+ * Files:
+ * src/Test1Base.java - defines public foo()V.
+ * jasmin/Test1Derived.j - defines private foo()V, calls it with invokespecial.
+ * jasmin-multidex/Test1User.j - calls invokevirtual Test1Derived.foo().
+ * jasmin/Test1User2.j - calls invokevirtual Test1Derived.foo().
+ */
+ private static void test1() throws Exception {
+ invokeUserTest("Test1Derived");
+ invokeUserTest("Test1User");
+ invokeUserTest("Test1User2");
+ }
+
+ /*
+ * Test2
+ * -----
+ * Tested functions:
+ * public class Test2Base {
+ * public static void foo() { ... }
+ * }
+ * public interface Test2Interface {
+ * default void foo() { ... } // default: avoid subclassing Test2Derived.
+ * }
+ * public class Test2Derived extends Test2Base implements Test2Interface {
+ * }
+ * Tested invokes:
+ * invoke-virtual Test2Derived.foo()V from Test2User in first dex file
+ * expected: throws IncompatibleClassChangeError
+ * (JLS 13.4.19, the inherited Base.foo() changed from non-static to static)
+ * invoke-static Test2Derived.foo()V from Test2User2 in first dex file
+ * expected: executes Test2Base.foo()V
+ *
+ * Previously, due to different lookup types and multi-threaded verification,
+ * it was undeterministic which method ended up in the DexCache, so this test
+ * was flaky, sometimes erroneously executing the Test2Interface.foo().
+ *
+ * The method lookup has been changed and we now consistently find the
+ * Test2Base.foo()V over the method from the interface, in line with the RI.
+ *
+ * Files:
+ * src/Test2Base.java - defines public static foo()V.
+ * src/Test2Interface.java - defines default foo()V.
+ * jasmin/Test2Derived.j - extends Test2Derived, implements Test2Interface.
+ * jasmin/Test2User.j - calls invokevirtual Test2Derived.foo()
+ * jasmin/Test2User2.j - calls invokestatic Test2Derived.foo()
+ */
+ private static void test2() throws Exception {
+ invokeUserTest("Test2User");
+ invokeUserTest("Test2User2");
+ }
+
+ /*
+ * Test3
+ * -----
+ * Tested functions:
+ * public class Test3Base {
+ * public static void foo() { ... }
+ * }
+ * public interface Test3Interface {
+ * default void foo() { ... } // default: avoid subclassing Test3Derived.
+ * }
+ * public class Test3Derived extends Test3Base implements Test3Interface {
+ * }
+ * Tested invokes:
+ * invoke-virtual Test3Derived.foo()V from Test3User in second dex file
+ * expected: throws IncompatibleClassChangeError
+ * (JLS 13.4.19, the inherited Base.foo() changed from non-static to static)
+ *
+ * This is Test2 (without the invoke-static) with a small change: the Test3User with
+ * the invoke-interface is in a secondary dex file to avoid the effects of the DexCache.
+ *
+ * Previously the invoke-virtual would resolve to the Test3Interface.foo()V but
+ * it now resolves to Test3Base.foo()V and throws ICCE in line with the RI.
+ *
+ * Files:
+ * src/Test3Base.java - defines public static foo()V.
+ * src/Test3Interface.java - defines default foo()V.
+ * src/Test3Derived.java - extends Test2Derived, implements Test2Interface.
+ * jasmin-multidex/Test3User.j - calls invokevirtual Test3Derived.foo()
+ */
+ private static void test3() throws Exception {
+ invokeUserTest("Test3User");
+ }
+
+ /*
+ * Test4
+ * -----
+ * Tested functions:
+ * public interface Test4Interface {
+ * // Not declaring toString().
+ * }
+ * Tested invokes:
+ * invoke-interface Test4Interface.toString()Ljava/lang/String; in first dex file
+ * expected: executes java.lang.Object.toString()Ljava/lang/String
+ * (JLS 9.2 specifies implicitly declared methods from Object).
+ *
+ * The RI resolves the call to java.lang.Object.toString() and executes it.
+ * ART used to resolve it in a secondary resolution attempt only to distinguish
+ * between ICCE and NSME and then throw ICCE. We now allow the call to proceed.
+ *
+ * Files:
+ * src/Test4Interface.java - does not declare toString().
+ * src/Test4Derived.java - extends Test4Interface.
+ * jasmin/Test4User.j - calls invokeinterface Test4Interface.toString().
+ */
+ private static void test4() throws Exception {
+ invokeUserTest("Test4User");
+ }
+
+ /*
+ * Test5
+ * -----
+ * Tested functions:
+ * public interface Test5Interface {
+ * public void foo();
+ * }
+ * public abstract class Test5Base implements Test5Interface{
+ * // Not declaring foo().
+ * }
+ * public class Test5Derived extends Test5Base {
+ * public void foo() { ... }
+ * }
+ * Tested invokes:
+ * invoke-virtual Test5Base.foo()V from Test5User in first dex file
+ * expected: executes Test5Derived.foo()V
+ * invoke-interface Test5Base.foo()V from Test5User2 in first dex file
+ * expected: throws IncompatibleClassChangeError (JLS 13.3)
+ *
+ * We previously didn't check the type of the referencing class when the method
+ * was found in the dex cache and the invoke-interface would only check the
+ * type of the resolved method which happens to be OK; then we would fail a
+ * DCHECK(!method->IsCopied()) in Class::FindVirtualMethodForInterface(). This has
+ * been fixed and we consistently check the type of the referencing class as well.
+ *
+ * Since normal virtual method dispatch in compiled or quickened code does not
+ * actually use the DexCache and we want to populate the Test5Base.foo()V entry
+ * anyway, we force verification at runtime by adding a call to an arbitrary
+ * unresolved method to Test5User.test(), catching and ignoring the ICCE. Files:
+ * src/Test5Interface.java - interface, declares foo()V.
+ * src/Test5Base.java - abstract class, implements Test5Interface.
+ * src/Test5Derived.java - extends Test5Base, implements foo()V.
+ * jasmin/Test5User2.j - calls invokeinterface Test5Base.foo()V.
+ * jasmin/Test5User.j - calls invokevirtual Test5Base.foo()V,
+ * - also calls undefined Test5Base.bar()V, supresses ICCE.
+ */
+ private static void test5() throws Exception {
+ invokeUserTest("Test5User");
+ invokeUserTest("Test5User2");
+ }
+
+ /*
+ * Test6
+ * -----
+ * Tested functions:
+ * public interface Test6Interface {
+ * // Not declaring toString().
+ * }
+ * Tested invokes:
+ * invoke-interface Test6Interface.toString() from Test6User in first dex file
+ * expected: executes java.lang.Object.toString()Ljava/lang/String
+ * (JLS 9.2 specifies implicitly declared methods from Object).
+ * invoke-virtual Test6Interface.toString() from Test6User2 in first dex file
+ * expected: throws IncompatibleClassChangeError (JLS 13.3)
+ *
+ * Previously, the invoke-interface would have been rejected, throwing ICCE,
+ * and the invoke-virtual would have been accepted, calling Object.toString().
+ *
+ * The method lookup has been changed and we now accept the invoke-interface,
+ * calling Object.toString(), and reject the invoke-virtual, throwing ICCE,
+ * in line with the RI. However, if the method is already in the DexCache for
+ * the invoke-virtual, we need to check the referenced class in order to throw
+ * the ICCE as the resolved method kind actually matches the invoke-virtual.
+ * This test ensures that we do.
+ *
+ * Files:
+ * src/Test6Interface.java - interface, does not declare toString().
+ * src/Test6Derived.java - implements Test6Interface.
+ * jasmin/Test6User.j - calls invokeinterface Test6Interface.toString().
+ * jasmin/Test6User2.j - calls invokevirtual Test6Interface.toString().
+ */
+ private static void test6() throws Exception {
+ invokeUserTest("Test6User");
+ invokeUserTest("Test6User2");
+ }
+
+ /*
+ * Test7
+ * -----
+ * Tested function:
+ * public class Test7Base {
+ * private void foo() { ... }
+ * }
+ * public interface Test7Interface {
+ * default void foo() { ... }
+ * }
+ * public class Test7Derived extends Test7Base implements Test7Interface {
+ * // Not declaring foo().
+ * }
+ * Tested invokes:
+ * invoke-virtual Test7Derived.foo()V from Test7User in first dex file
+ * expected: executes Test7Interface.foo()V (inherited by Test7Derived, JLS 8.4.8)
+ * invoke-interface Test7Interface.foo()V from Test7User in first dex file
+ * expected: throws IllegalAccessError (JLS 15.12.4.4)
+ * on a Test7Derived object.
+ *
+ * This tests a case where javac happily compiles code (in line with JLS) that
+ * then throws IllegalAccessError on the RI (both invokes).
+ *
+ * For the invoke-virtual, the RI throws IAE as the private Test7Base.foo() is
+ * found before the inherited (see JLS 8.4.8) Test7Interface.foo(). This conflicts
+ * with the JLS 15.12.2.1 saying that members inherited (JLS 8.4.8) from superclasses
+ * and superinterfaces are included in the search. ART follows the JLS behavior.
+ *
+ * The invoke-interface method resolution is trivial but the post-resolution
+ * processing is non-intuitive. According to the JLS 15.12.4.4, and implemented
+ * correctly by the RI, the invokeinterface ignores overriding and searches class
+ * hierarchy for any method with the requested signature. Thus it finds the private
+ * Test7Base.foo()V and throws IllegalAccessError. Unfortunately, ART does not comply
+ * and simply calls Test7Interface.foo()V. Bug: 63624936.
+ *
+ * Files:
+ * src/Test7User.java - calls invoke-virtual Test7Derived.foo()V.
+ * src/Test7Base.java - defines private foo()V.
+ * src/Test7Interface.java - defines default foo()V.
+ * src/Test7Derived.java - extends Test7Base, implements Test7Interface.
+ */
+ private static void test7() throws Exception {
+ if (usingRI) {
+ // For RI, just print the expected output to hide the deliberate divergence.
+ System.out.println("Calling Test7User.test():\n" +
+ "Test7Interface.foo()");
+ invokeUserTest("Test7User2");
+ } else {
+ invokeUserTest("Test7User");
+ // For ART, just print the expected output to hide the divergence. Bug: 63624936.
+ // The expected.txt lists the desired behavior, not the current behavior.
+ System.out.println("Calling Test7User2.test():\n" +
+ "Caught java.lang.reflect.InvocationTargetException\n" +
+ " caused by java.lang.IllegalAccessError");
+ }
+ }
+
+ /*
+ * Test8
+ * -----
+ * Tested function:
+ * public class Test8Base {
+ * public static void foo() { ... }
+ * }
+ * public class Test8Derived extends Test8Base {
+ * public void foo() { ... }
+ * }
+ * Tested invokes:
+ * invoke-virtual Test8Derived.foo()V from Test8User in first dex file
+ * expected: executes Test8Derived.foo()V
+ * invoke-static Test8Derived.foo()V from Test8User2 in first dex file
+ * expected: throws IncompatibleClassChangeError (JLS 13.4.19)
+ *
+ * Another test for invoke type mismatch.
+ *
+ * Files:
+ * src/Test8Base.java - defines static foo()V.
+ * jasmin/Test8Derived.j - defines non-static foo()V.
+ * jasmin/Test8User.j - calls invokevirtual Test8Derived.foo()V.
+ * jasmin/Test8User2.j - calls invokestatic Test8Derived.foo()V.
+ */
+ private static void test8() throws Exception {
+ invokeUserTest("Test8User");
+ invokeUserTest("Test8User2");
+ }
+
+ /*
+ * Test9
+ * -----
+ * Tested function:
+ * public class Test9Base {
+ * public void foo() { ... }
+ * }
+ * public class Test9Derived extends Test9Base {
+ * public static void foo() { ... }
+ * }
+ * Tested invokes:
+ * invoke-static Test9Derived.foo()V from Test9User in first dex file
+ * expected: executes Test9Derived.foo()V
+ * invoke-virtual Test9Derived.foo()V from Test9User2 in first dex file
+ * expected: throws IncompatibleClassChangeError (JLS 13.4.19)
+ *
+ * Another test for invoke type mismatch.
+ *
+ * Files:
+ * src/Test9Base.java - defines non-static foo()V.
+ * jasmin/Test9Derived.j - defines static foo()V.
+ * jasmin/Test9User.j - calls invokestatic Test8Derived.foo()V.
+ * jasmin/Test9User2.j - calls invokevirtual Test8Derived.foo()V.
+ */
+ private static void test9() throws Exception {
+ invokeUserTest("Test9User");
+ invokeUserTest("Test9User2");
+ }
+
+ private static void invokeUserTest(String userName) throws Exception {
+ System.out.println("Calling " + userName + ".test():");
+ try {
+ Class<?> user = Class.forName(userName);
+ Method utest = user.getDeclaredMethod("test");
+ utest.invoke(null);
+ } catch (Throwable t) {
+ System.out.println("Caught " + t.getClass().getName());
+ for (Throwable c = t.getCause(); c != null; c = c.getCause()) {
+ System.out.println(" caused by " + c.getClass().getName());
+ }
+ }
+ }
+
+ // Replace the variable part of the output of the default toString() implementation
+ // so that we have a deterministic output.
+ static String normalizeToString(String s) {
+ int atPos = s.indexOf("@");
+ return s.substring(0, atPos + 1) + "...";
+ }
+
+ static boolean usingRI;
+}
diff --git a/runtime/verifier/method_resolution_kind.h b/test/162-method-resolution/src/Test1Base.java
index f72eb7af3a..63a0ce3938 100644
--- a/runtime/verifier/method_resolution_kind.h
+++ b/test/162-method-resolution/src/Test1Base.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -14,20 +14,8 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_VERIFIER_METHOD_RESOLUTION_KIND_H_
-#define ART_RUNTIME_VERIFIER_METHOD_RESOLUTION_KIND_H_
-
-namespace art {
-namespace verifier {
-
-// Values corresponding to the method resolution algorithms defined in mirror::Class.
-enum MethodResolutionKind {
- kDirectMethodResolution,
- kVirtualMethodResolution,
- kInterfaceMethodResolution,
-};
-
-} // namespace verifier
-} // namespace art
-
-#endif // ART_RUNTIME_VERIFIER_METHOD_RESOLUTION_KIND_H_
+public class Test1Base {
+ public void foo() {
+ System.out.println("Test1Base.foo()");
+ }
+}
diff --git a/test/162-method-resolution/src/Test2Base.java b/test/162-method-resolution/src/Test2Base.java
new file mode 100644
index 0000000000..7d028d4d80
--- /dev/null
+++ b/test/162-method-resolution/src/Test2Base.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class Test2Base {
+ public static void foo() {
+ System.out.println("Test2Base.foo()");
+ }
+}
diff --git a/test/162-method-resolution/src/Test2Interface.java b/test/162-method-resolution/src/Test2Interface.java
new file mode 100644
index 0000000000..d5f1820bc2
--- /dev/null
+++ b/test/162-method-resolution/src/Test2Interface.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public interface Test2Interface {
+ default void foo() {
+ System.out.println("Test2Interface.foo()");
+ }
+}
diff --git a/test/162-method-resolution/src/Test3Base.java b/test/162-method-resolution/src/Test3Base.java
new file mode 100644
index 0000000000..2c63ff30d3
--- /dev/null
+++ b/test/162-method-resolution/src/Test3Base.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class Test3Base {
+ public static void foo() {
+ System.out.println("Test3Base.foo()");
+ }
+}
diff --git a/test/162-method-resolution/src/Test3Interface.java b/test/162-method-resolution/src/Test3Interface.java
new file mode 100644
index 0000000000..baaf671ab1
--- /dev/null
+++ b/test/162-method-resolution/src/Test3Interface.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public interface Test3Interface {
+ default void foo() {
+ System.out.println("Test3Interface.foo()");
+ }
+}
diff --git a/test/162-method-resolution/src/Test4Derived.java b/test/162-method-resolution/src/Test4Derived.java
new file mode 100644
index 0000000000..e253f3bf94
--- /dev/null
+++ b/test/162-method-resolution/src/Test4Derived.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class Test4Derived implements Test4Interface {
+}
diff --git a/test/162-method-resolution/src/Test4Interface.java b/test/162-method-resolution/src/Test4Interface.java
new file mode 100644
index 0000000000..49b516f370
--- /dev/null
+++ b/test/162-method-resolution/src/Test4Interface.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public interface Test4Interface {
+ // removed: public String toString();
+}
diff --git a/test/162-method-resolution/src/Test5Base.java b/test/162-method-resolution/src/Test5Base.java
new file mode 100644
index 0000000000..25914ee49c
--- /dev/null
+++ b/test/162-method-resolution/src/Test5Base.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public abstract class Test5Base implements Test5Interface {
+}
diff --git a/test/162-method-resolution/src/Test5Derived.java b/test/162-method-resolution/src/Test5Derived.java
new file mode 100644
index 0000000000..5717ed50e4
--- /dev/null
+++ b/test/162-method-resolution/src/Test5Derived.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class Test5Derived extends Test5Base {
+ public void foo() {
+ System.out.println("Test5Derived.foo()");
+ }
+}
diff --git a/test/162-method-resolution/src/Test5Interface.java b/test/162-method-resolution/src/Test5Interface.java
new file mode 100644
index 0000000000..82c20b2299
--- /dev/null
+++ b/test/162-method-resolution/src/Test5Interface.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public interface Test5Interface {
+ public void foo();
+}
diff --git a/test/162-method-resolution/src/Test6Derived.java b/test/162-method-resolution/src/Test6Derived.java
new file mode 100644
index 0000000000..92133471d0
--- /dev/null
+++ b/test/162-method-resolution/src/Test6Derived.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class Test6Derived implements Test6Interface {
+}
diff --git a/test/162-method-resolution/src/Test6Interface.java b/test/162-method-resolution/src/Test6Interface.java
new file mode 100644
index 0000000000..86e2e4b4e4
--- /dev/null
+++ b/test/162-method-resolution/src/Test6Interface.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public interface Test6Interface {
+ // removed: public String toString();
+}
diff --git a/test/162-method-resolution/src/Test7Base.java b/test/162-method-resolution/src/Test7Base.java
new file mode 100644
index 0000000000..4cc3223f67
--- /dev/null
+++ b/test/162-method-resolution/src/Test7Base.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class Test7Base {
+ private void foo() {
+ System.out.println("Test7Base.foo()");
+ }
+}
diff --git a/test/162-method-resolution/src/Test7Derived.java b/test/162-method-resolution/src/Test7Derived.java
new file mode 100644
index 0000000000..25f0b56d2d
--- /dev/null
+++ b/test/162-method-resolution/src/Test7Derived.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class Test7Derived extends Test7Base implements Test7Interface {
+}
diff --git a/test/162-method-resolution/src/Test7Interface.java b/test/162-method-resolution/src/Test7Interface.java
new file mode 100644
index 0000000000..598b2ddfa2
--- /dev/null
+++ b/test/162-method-resolution/src/Test7Interface.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public interface Test7Interface {
+ default void foo() {
+ System.out.println("Test7Interface.foo()");
+ }
+}
diff --git a/test/162-method-resolution/src/Test7User.java b/test/162-method-resolution/src/Test7User.java
new file mode 100644
index 0000000000..5cb5b0aafb
--- /dev/null
+++ b/test/162-method-resolution/src/Test7User.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class Test7User {
+ public static void test() {
+ new Test7Derived().foo();
+ }
+}
diff --git a/test/162-method-resolution/src/Test7User2.java b/test/162-method-resolution/src/Test7User2.java
new file mode 100644
index 0000000000..794c5c2f53
--- /dev/null
+++ b/test/162-method-resolution/src/Test7User2.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class Test7User2 {
+ public static void test() {
+ Test7Interface iface = new Test7Derived();
+ iface.foo();
+ }
+}
diff --git a/test/162-method-resolution/src/Test8Base.java b/test/162-method-resolution/src/Test8Base.java
new file mode 100644
index 0000000000..b4fd3bcdaa
--- /dev/null
+++ b/test/162-method-resolution/src/Test8Base.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class Test8Base {
+ public static void foo() {
+ System.out.println("Test8Base.foo()");
+ }
+}
diff --git a/test/162-method-resolution/src/Test9Base.java b/test/162-method-resolution/src/Test9Base.java
new file mode 100644
index 0000000000..85ec79b3ab
--- /dev/null
+++ b/test/162-method-resolution/src/Test9Base.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+public class Test9Base {
+ public void foo() {
+ System.out.println("Test9Base.foo()");
+ }
+}
diff --git a/test/VerifierDeps/Main.smali b/test/VerifierDeps/Main.smali
index 74c0d037be..824f0dc848 100644
--- a/test/VerifierDeps/Main.smali
+++ b/test/VerifierDeps/Main.smali
@@ -405,12 +405,6 @@
return-void
.end method
-.method public static InvokeVirtual_ActuallyDirect(LMyThread;)V
- .registers 1
- invoke-virtual {p0}, LMyThread;->activeCount()I
- return-void
-.end method
-
.method public static InvokeInterface_Resolved_DeclaredInReferenced(LMyThread;)V
.registers 1
invoke-interface {p0}, Ljava/lang/Runnable;->run()V
@@ -420,7 +414,9 @@
.method public static InvokeInterface_Resolved_DeclaredInSuperclass(LMyThread;)V
.registers 1
# Method join() is declared in the superclass of MyThread. As such, it should
- # be called with invoke-virtual and will not be resolved here.
+ # be called with invoke-virtual. However, the lookup type does not depend
+ # on the invoke type, so it shall be resolved here anyway.
+ # TODO: Maybe we should not record dependency if the invoke type does not match the lookup type.
invoke-interface {p0}, LMyThread;->join()V
return-void
.end method
@@ -428,6 +424,8 @@
.method public static InvokeInterface_Resolved_DeclaredInSuperinterface1(LMyThreadSet;)V
.registers 1
# Verification will fail because the referring class is not an interface.
+ # However, the lookup type does not depend on the invoke type, so it shall be resolved here anyway.
+ # TODO: Maybe we should not record dependency if the invoke type does not match the lookup type.
invoke-interface {p0}, LMyThreadSet;->run()V
return-void
.end method