ART: Change method lookup to be more consistent to JLS and the RI.
The method lookup for different invoke types was previously
widely different and didn't work well with the dex cache
method array where we have only a single slot for each
MethodId. The new behavior is to perform the same lookup for
all cases, distinguishing only between interface and
non-interface referencing class, and to further align the
behavior with the JLS and the RI. Where the JLS conflicts
with the RI, we follow the JLS semantics.
The new lookup for class methods first searches the methods
declared in the superclass chain (ignoring "copied" methods)
and only then looks in the "copied" methods. If the search
in the superclass chain finds a method that has not been
inherited (i.e. either a private method or a package-access
method where one of the classes in the chain does not belong
to the same package, see JLS 8.4.8), we still search the
"copied" methods as there may actually be a method inherited
from an interface. This follows the JLS semantics where
inherited methods are included in the search (JLS 15.12.2.1)
but conflicts with the RI where the private or
package-access method takes precedence over methods
inherited from interfaces.
Note that this search can find an accessible method that is
not inherited by the qualifying type, either for a package
access method when the referrer is in the same package but
the qualifying type is in another package, or for a private
method where the referrer is in the same class but the
qualifying type is actually a subclass. For the moment we
allow such calls and we shall consider whether to throw
an IncompatibleClassChangeError in this situation in future
to comply with JLS 15.12.4.3.
The new lookup for interface methods searches the interface
class, then all the superinterfaces and then the
java.lang.Object class, see implicitly declared methods in
interfaces, JLS 9.2. The search for the maximally-specific
non-abstract superinterface method is not yet implemented,
but the difference should be difficult to observe as the
usual subsequent call to FindVirtualMethodForInterface()
should yield the same result for any matching method.
The new test 162-method-idx-clash exposes several cases
where we previously completely messed up due to the effects
of the DexCache, or where we were out of line with the RI.
It also tests a case where the JLS and the RI disagree and
we follow the JLS.
Test: art/test/run-test --host --jvm 162-method-resolution
Test: m test-art-host-gtest
Test: testrunner.py --host
Test: testrunner.py --host --interp-ac
Test: Nexus 6P boots.
Test: testrunner.py --target
Bug: 62855082
Bug: 30627598
Change-Id: If450c8cff2751369011d649c25d28a482a2c61a3
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 3683695..08145e2 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -265,8 +265,8 @@
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 @@
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 fba1136..9d57b96 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -291,13 +291,14 @@
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 db95bd6..b043929 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -111,7 +111,7 @@
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 83d7a3d..24b146d 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -374,14 +374,12 @@
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;
}
@@ -544,7 +542,7 @@
// 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,
@@ -1756,7 +1754,7 @@
}
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) {
@@ -1765,7 +1763,7 @@
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 b4777df..0b3ca69 100644
--- a/compiler/exception_test.cc
+++ b/compiler/exception_test.cc
@@ -102,12 +102,14 @@
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 9d7aff7..252fdd6 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -113,9 +113,9 @@
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 @@
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 6ce7d75..40e5645 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -247,9 +247,9 @@
// 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 6120ed0..f8bb417 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1143,11 +1143,13 @@
// 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 @@
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 839f328..8054140 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -664,10 +664,7 @@
// 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 @@
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 @@
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 @@
// 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 @@
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 d147166..f2a8cc0 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -1867,33 +1867,35 @@
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 98332d3..ecbf52b 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -526,7 +526,7 @@
// 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 686da21..72e2a6c 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -155,13 +155,14 @@
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 @@
// 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 @@
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 @@
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 @@
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, 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_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_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_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_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_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_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_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 @@
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 @@
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 @@
}
// 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;
- }
+ {
+ 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) {
- 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;
- }
+ {
+ 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()));
+ }
- {
- 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(),
+ 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()));
+ }
- {
- 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) {
+ 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()));
}
-
- // 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;
- }
- }
- 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;
- }
- }
- 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 45dd596..ef9c457 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -67,9 +67,10 @@
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 3c51f52..d29db15 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -90,33 +90,105 @@
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 @@
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 @@
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 41adae4..07a40c4 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -149,8 +149,8 @@
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;
}
@@ -4658,10 +4658,8 @@
// 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";
@@ -4673,8 +4671,9 @@
// 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);
@@ -7383,10 +7382,8 @@
// 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();
@@ -7967,201 +7964,95 @@
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 (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;
+
+ 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);
}
}
+
+ // 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;
@@ -8180,21 +8071,16 @@
}
// 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;
@@ -8509,19 +8395,19 @@
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: {
@@ -8529,10 +8415,10 @@
// 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: {
@@ -8555,16 +8441,16 @@
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;
}
@@ -8580,10 +8466,10 @@
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;
}
}
@@ -9200,14 +9086,14 @@
}
// 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 864d37f..3cf59f0 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -281,10 +281,10 @@
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 @@
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 @@
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 03cc6c5..98d7c7c 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -1121,7 +1121,7 @@
// 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 @@
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 @@
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 @@
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 @@
// 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 41db4d8..b163cdb 100644
--- a/runtime/dex_file-inl.h
+++ b/runtime/dex_file-inl.h
@@ -181,19 +181,18 @@
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 6547299..828148a 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -50,6 +50,8 @@
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 @@
}
// 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 @@
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 @@
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 @@
}
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 36885d8..6abf7c5 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1182,7 +1182,7 @@
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 @@
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 @@
// 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 2a601c9..9e9fa71 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 @@
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 @@
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 @@
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 @@
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 0687b75..be2d34d 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -593,10 +593,8 @@
}
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 152cce4..7f9363f 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -265,7 +265,7 @@
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 @@
}
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 @@
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 c2ef724..3461a65 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -387,8 +387,9 @@
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 @@
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 @@
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 @@
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 @@
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 dbad614..e6c140b 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -233,17 +233,10 @@
}
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 e1e4f9c..e0afbb6 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -626,9 +626,9 @@
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 003cd4e..121c259 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -533,7 +533,11 @@
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 b0e5b6a..6f70b19 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -396,95 +396,44 @@
}
}
-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;
+ }
}
}
- return nullptr;
-}
-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;
+ // 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;
+ }
}
}
- return nullptr;
-}
-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;
-}
-
-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;
- }
- }
- return nullptr;
-}
-
-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()) {
- 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) {
+ // 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;
}
}
@@ -492,37 +441,220 @@
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;
- }
- }
- return nullptr;
+ArtMethod* Class::FindInterfaceMethod(const StringPiece& name,
+ const StringPiece& signature,
+ PointerSize pointer_size) {
+ return FindInterfaceMethodWithSignature(this, name, signature, pointer_size);
}
-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::FindInterfaceMethod(const StringPiece& name,
+ const Signature& signature,
+ PointerSize pointer_size) {
+ return FindInterfaceMethodWithSignature(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::FindInterfaceMethod(ObjPtr<DexCache> dex_cache,
+ uint32_t dex_method_idx,
+ PointerSize pointer_size) {
+ // 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);
+}
+
+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 true;
+}
+
+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;
+ }
+ }
+
+ // 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;
+ }
+ }
+
+ // 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 uninherited_method; // Return the `uninherited_method` if any.
+}
+
+
+ArtMethod* Class::FindClassMethod(const StringPiece& name,
+ const StringPiece& signature,
+ PointerSize pointer_size) {
+ return FindClassMethodWithSignature(this, name, signature, pointer_size);
+}
+
+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;
+ }
+ }
+ }
+
+ // 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;
+ }
+ }
+ }
+
+ // 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 uninherited_method; // Return the `uninherited_method` if any.
+}
+
+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;
@@ -539,47 +671,6 @@
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;
- }
- }
- return nullptr;
-}
-
-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()) {
- 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;
- }
- }
- }
- return nullptr;
-}
-
ArtMethod* Class::FindDeclaredVirtualMethodByName(const StringPiece& name,
PointerSize pointer_size) {
for (auto& method : GetVirtualMethods(pointer_size)) {
@@ -591,42 +682,6 @@
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 e516a06..c626897 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -915,6 +915,13 @@
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 @@
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- ArtMethod* FindDeclaredDirectMethod(const StringPiece& name,
- const StringPiece& signature,
- PointerSize pointer_size)
+ // 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* FindClassMethod(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)
+ ArtMethod* FindClassMethod(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)
+ ArtMethod* FindClassMethod(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_);
-
- ArtMethod* FindDirectMethod(ObjPtr<DexCache> dex_cache,
- uint32_t dex_method_idx,
- PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- ArtMethod* FindDeclaredVirtualMethod(const StringPiece& name,
- const StringPiece& signature,
- PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- ArtMethod* FindDeclaredVirtualMethod(const StringPiece& name,
- const Signature& signature,
- 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 @@
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 5b1ba8d..194d9bc 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -128,14 +128,18 @@
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 fed9c1c..4d66e2b 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -372,8 +372,7 @@
}
// 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 @@
}
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 @@
}
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 debee91..31694c4 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -40,6 +40,7 @@
#include "art_jvmti.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 @@
// 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 6e0196e..fa58295 100644
--- a/runtime/openjdkjvmti/ti_search.cc
+++ b/runtime/openjdkjvmti/ti_search.cc
@@ -105,17 +105,21 @@
}
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 4e95b01..b055bf9 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 @@
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 260be8f..d830387 100644
--- a/runtime/reference_table_test.cc
+++ b/runtime/reference_table_test.cc
@@ -56,8 +56,8 @@
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 1ba4b7b..d00f08b 100644
--- a/runtime/reflection_test.cc
+++ b/runtime/reflection_test.cc
@@ -108,9 +108,9 @@
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 @@
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 bf9e405..74b9810 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -633,9 +633,10 @@
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 004b68e..5ee4487 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2780,7 +2780,7 @@
}
}
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 634bd47..48b703a 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -192,18 +192,21 @@
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 ea480f4..0351fd3 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -72,8 +72,8 @@
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_resolution_kind.h b/runtime/verifier/method_resolution_kind.h
deleted file mode 100644
index f72eb7a..0000000
--- a/runtime/verifier/method_resolution_kind.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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_
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index efb02f6..6dc7953 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 @@
}
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 @@
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 @@
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 @@
}
}
- // 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 122e05f..112eec8 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -54,9 +54,7 @@
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::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 @@
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::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 @@
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 @@
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 @@
(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 @@
}
}
- 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 @@
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 @@
}
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 @@
<< ")";
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 @@
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 @@
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 43eb948..b883a9e 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 @@
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 @@
// 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 @@
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 @@
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 0000000..1bf39c9
--- /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 0000000..ff57a9a
--- /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 0000000..09ba77b
--- /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 0000000..90f3a4e
--- /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 0000000..d754c64
--- /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 0000000..8af9aab
--- /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 0000000..bb4525d
--- /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 0000000..2cce074
--- /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 0000000..eb80f32
--- /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 0000000..2bf4bf1
--- /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 0000000..5b65368
--- /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 0000000..036e366
--- /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 0000000..9484a69
--- /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 0000000..55b43f1
--- /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 0000000..ab9ac0e
--- /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 0000000..73f8b28
--- /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 0000000..af60c6e
--- /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 0000000..5cdb95c
--- /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 0000000..789f0f2
--- /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 0000000..81f9a7d
--- /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 0000000..ae53905
--- /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 0000000..22e3aee
--- /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 0000000..fa95aa7
--- /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/test/162-method-resolution/src/Test1Base.java b/test/162-method-resolution/src/Test1Base.java
new file mode 100644
index 0000000..63a0ce3
--- /dev/null
+++ b/test/162-method-resolution/src/Test1Base.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 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 0000000..7d028d4
--- /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 0000000..d5f1820
--- /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 0000000..2c63ff3
--- /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 0000000..baaf671
--- /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 0000000..e253f3b
--- /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 0000000..49b516f
--- /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 0000000..25914ee
--- /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 0000000..5717ed5
--- /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 0000000..82c20b2
--- /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 0000000..9213347
--- /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 0000000..86e2e4b
--- /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 0000000..4cc3223
--- /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 0000000..25f0b56
--- /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 0000000..598b2dd
--- /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 0000000..5cb5b0a
--- /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 0000000..794c5c2
--- /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 0000000..b4fd3bc
--- /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 0000000..85ec79b
--- /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 74c0d03..824f0dc 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