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