diff options
| -rw-r--r-- | dex2oat/dex2oat.cc | 12 | ||||
| -rw-r--r-- | dex2oat/dex2oat_test.cc | 12 | ||||
| -rw-r--r-- | runtime/class_loader_context.cc | 61 | ||||
| -rw-r--r-- | runtime/class_loader_context.h | 25 | ||||
| -rw-r--r-- | runtime/class_loader_context_test.cc | 135 | ||||
| -rw-r--r-- | runtime/common_runtime_test.cc | 43 | ||||
| -rw-r--r-- | runtime/common_runtime_test.h | 17 |
7 files changed, 240 insertions, 65 deletions
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 113bdb58d0..c9cd17142e 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -414,16 +414,20 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(" "); UsageError(" The chain is interpreted in the natural 'parent order', meaning that class"); UsageError(" loader 'i+1' will be the parent of class loader 'i'."); - UsageError(" The compilation sources will be added to the classpath of the last class"); - UsageError(" loader. This allows the compiled dex files to be loaded at runtime in"); - UsageError(" a class loader that contains other dex files as well (e.g. shared libraries)."); + UsageError(" The compilation sources will be appended to the classpath of the first class"); + UsageError(" loader."); + UsageError(" "); + UsageError(" E.g. if the context is 'PCL[lib1.dex];DLC[lib2.dex]' and "); + UsageError(" --dex-file=src.dex then dex2oat will setup a PathClassLoader with classpath "); + UsageError(" 'lib1.dex:src.dex' and set its parent to a DelegateLastClassLoader with "); + UsageError(" classpath 'lib2.dex'."); UsageError(" "); UsageError(" Note that the compiler will be tolerant if the source dex files specified"); UsageError(" with --dex-file are found in the classpath. The source dex files will be"); UsageError(" removed from any class loader's classpath possibly resulting in empty"); UsageError(" class loaders."); UsageError(" "); - UsageError(" Example: --classloader-spec=PCL[lib1.dex:lib2.dex];DLC[lib3.dex]"); + UsageError(" Example: --class-loader-context=PCL[lib1.dex:lib2.dex];DLC[lib3.dex]"); UsageError(""); std::cerr << "See log for usage error information\n"; exit(EXIT_FAILURE); diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index ed1aee66ee..68ec0b5ab6 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -1102,4 +1102,16 @@ TEST_F(Dex2oatClassLoaderContextTest, ContextWithNotExistentDexFiles) { RunTest(context.c_str(), kEmptyClassPathKey, /*expected_success*/ true); } +TEST_F(Dex2oatClassLoaderContextTest, ChainContext) { + std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Nested"); + std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); + + std::string context = "PCL[" + GetTestDexFileName("Nested") + "];" + + "DLC[" + GetTestDexFileName("MultiDex") + "]"; + std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "];" + + "DLC[" + CreateClassPathWithChecksums(dex_files2) + "]"; + + RunTest(context.c_str(), expected_classpath_key.c_str(), true); +} + } // namespace art diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 678ae8f288..90346f0686 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -305,24 +305,41 @@ jobject ClassLoaderContext::CreateClassLoader( Thread* self = Thread::Current(); ScopedObjectAccess soa(self); - std::vector<const DexFile*> class_path_files; - - // TODO(calin): Transition period: assume we only have a classloader until - // the oat file assistant implements the full class loader check. - if (!class_loader_chain_.empty()) { - CHECK_EQ(1u, class_loader_chain_.size()); - CHECK_EQ(kPathClassLoader, class_loader_chain_[0].type); - class_path_files = MakeNonOwningPointerVector(class_loader_chain_[0].opened_dex_files); - } - - // Classpath: first the class-path given; then the dex files we'll compile. - // Thus we'll resolve the class-path first. - class_path_files.insert(class_path_files.end(), - compilation_sources.begin(), - compilation_sources.end()); - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - return class_linker->CreatePathClassLoader(self, class_path_files); + + if (class_loader_chain_.empty()) { + return class_linker->CreatePathClassLoader(self, compilation_sources); + } + + // Create the class loaders starting from the top most parent (the one on the last position + // in the chain) but omit the first class loader which will contain the compilation_sources and + // needs special handling. + jobject current_parent = nullptr; // the starting parent is the BootClassLoader. + for (size_t i = class_loader_chain_.size() - 1; i > 0; i--) { + std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector( + class_loader_chain_[i].opened_dex_files); + current_parent = class_linker->CreateWellKnownClassLoader( + self, + class_path_files, + GetClassLoaderClass(class_loader_chain_[i].type), + current_parent); + } + + // We set up all the parents. Move on to create the first class loader. + // Its classpath comes first, followed by compilation sources. This ensures that whenever + // we need to resolve classes from it the classpath elements come first. + + std::vector<const DexFile*> first_class_loader_classpath = MakeNonOwningPointerVector( + class_loader_chain_[0].opened_dex_files); + first_class_loader_classpath.insert(first_class_loader_classpath.end(), + compilation_sources.begin(), + compilation_sources.end()); + + return class_linker->CreateWellKnownClassLoader( + self, + first_class_loader_classpath, + GetClassLoaderClass(class_loader_chain_[0].type), + current_parent); } std::vector<const DexFile*> ClassLoaderContext::FlattenOpenedDexFiles() const { @@ -623,5 +640,15 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex return true; } +jclass ClassLoaderContext::GetClassLoaderClass(ClassLoaderType type) { + switch (type) { + case kPathClassLoader: return WellKnownClasses::dalvik_system_PathClassLoader; + case kDelegateLastClassLoader: return WellKnownClasses::dalvik_system_DelegateLastClassLoader; + case kInvalidClassLoader: break; // will fail after the switch. + } + LOG(FATAL) << "Invalid class loader type " << type; + UNREACHABLE(); +} + } // namespace art diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h index 8c65e2eb32..37dd02b07f 100644 --- a/runtime/class_loader_context.h +++ b/runtime/class_loader_context.h @@ -60,11 +60,22 @@ class ClassLoaderContext { bool RemoveLocationsFromClassPaths(const dchecked_vector<std::string>& compilation_sources); // Creates the entire class loader hierarchy according to the current context. - // The compilation sources are appended to the classpath of the top class loader - // (i.e the class loader whose parent is the BootClassLoader). - // Should only be called if OpenDexFiles() returned true. + // Returns the first class loader from the chain. + // + // For example: if the context was built from the spec + // "ClassLoaderType1[ClasspathElem1:ClasspathElem2...];ClassLoaderType2[...]..." + // the method returns the class loader correponding to ClassLoader1. The parent chain will be + // ClassLoader1 --> ClassLoader2 --> ... --> BootClassLoader. + // + // The compilation sources are appended to the classpath of the first class loader (in the above + // example ClassLoader1). + // // If the context is empty, this method only creates a single PathClassLoader with the // given compilation_sources. + // + // Notes: + // 1) the objects are not completely set up. Do not use this outside of tests and the compiler. + // 2) should only be called before the first call to OpenDexFiles(). jobject CreateClassLoader(const std::vector<const DexFile*>& compilation_sources) const; // Encodes the context as a string suitable to be added in oat files. @@ -89,6 +100,11 @@ class ClassLoaderContext { // The format: ClassLoaderType1[ClasspathElem1:ClasspathElem2...];ClassLoaderType2[...]... // ClassLoaderType is either "PCL" (PathClassLoader) or "DLC" (DelegateLastClassLoader). // ClasspathElem is the path of dex/jar/apk file. + // + // The spec represents a class loader chain with the natural interpretation: + // ClassLoader1 has ClassLoader2 as parent which has ClassLoader3 as a parent and so on. + // The last class loader is assumed to have the BootClassLoader as a parent. + // // Note that we allowed class loaders with an empty class path in order to support a custom // class loader for the source dex files. static std::unique_ptr<ClassLoaderContext> Create(const std::string& spec); @@ -168,6 +184,9 @@ class ClassLoaderContext { // The returned format can be used when parsing a context spec. static const char* GetClassLoaderTypeName(ClassLoaderType type); + // Returns the WellKnownClass for the given class loader type. + static jclass GetClassLoaderClass(ClassLoaderType type); + // The class loader chain represented as a vector. // The parent of class_loader_chain_[i] is class_loader_chain_[i++]. // The parent of the last element is assumed to be the boot class loader. diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index 659db4be64..1b46f67215 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -117,6 +117,24 @@ class ClassLoaderContextTest : public CommonRuntimeTest { ASSERT_FALSE(context->special_shared_library_); } + void VerifyClassLoaderDexFiles(ScopedObjectAccess& soa, + Handle<mirror::ClassLoader> class_loader, + jclass type, + std::vector<const DexFile*>& expected_dex_files) + REQUIRES_SHARED(Locks::mutator_lock_) { + ASSERT_TRUE(class_loader->GetClass() == soa.Decode<mirror::Class>(type)); + + std::vector<const DexFile*> class_loader_dex_files = GetDexFiles(soa, class_loader); + ASSERT_EQ(expected_dex_files.size(), class_loader_dex_files.size()); + + for (size_t i = 0; i < expected_dex_files.size(); i++) { + ASSERT_EQ(expected_dex_files[i]->GetLocation(), + class_loader_dex_files[i]->GetLocation()); + ASSERT_EQ(expected_dex_files[i]->GetLocationChecksum(), + class_loader_dex_files[i]->GetLocationChecksum()); + } + } + private: void VerifyClassLoaderInfo(ClassLoaderContext* context, size_t index, @@ -246,7 +264,7 @@ TEST_F(ClassLoaderContextTest, CreateClassLoader) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<1> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader = hs.NewHandle( soa.Decode<mirror::ClassLoader>(jclass_loader)); @@ -255,25 +273,17 @@ TEST_F(ClassLoaderContextTest, CreateClassLoader) { ASSERT_TRUE(class_loader->GetParent()->GetClass() == soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); - - std::vector<const DexFile*> class_loader_dex_files = GetDexFiles(jclass_loader); - ASSERT_EQ(classpath_dex.size() + compilation_sources.size(), class_loader_dex_files.size()); - - // The classpath dex files must come first. - for (size_t i = 0; i < classpath_dex.size(); i++) { - ASSERT_EQ(classpath_dex[i]->GetLocation(), - class_loader_dex_files[i]->GetLocation()); - ASSERT_EQ(classpath_dex[i]->GetLocationChecksum(), - class_loader_dex_files[i]->GetLocationChecksum()); + // For the first class loader the class path dex files must come first and then the + // compilation sources. + std::vector<const DexFile*> expected_classpath = MakeNonOwningPointerVector(classpath_dex); + for (auto& dex : compilation_sources_raw) { + expected_classpath.push_back(dex); } - // The compilation dex files must come second. - for (size_t i = 0, k = classpath_dex.size(); i < compilation_sources.size(); i++, k++) { - ASSERT_EQ(compilation_sources[i]->GetLocation(), - class_loader_dex_files[k]->GetLocation()); - ASSERT_EQ(compilation_sources[i]->GetLocationChecksum(), - class_loader_dex_files[k]->GetLocationChecksum()); - } + VerifyClassLoaderDexFiles(soa, + class_loader, + WellKnownClasses::dalvik_system_PathClassLoader, + expected_classpath); } TEST_F(ClassLoaderContextTest, CreateClassLoaderWithEmptyContext) { @@ -290,28 +300,90 @@ TEST_F(ClassLoaderContextTest, CreateClassLoaderWithEmptyContext) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<1> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader = hs.NewHandle( soa.Decode<mirror::ClassLoader>(jclass_loader)); - ASSERT_TRUE(class_loader->GetClass() == - soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)); + // An empty context should create a single PathClassLoader with only the compilation sources. + VerifyClassLoaderDexFiles(soa, + class_loader, + WellKnownClasses::dalvik_system_PathClassLoader, + compilation_sources_raw); ASSERT_TRUE(class_loader->GetParent()->GetClass() == soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); +} + +TEST_F(ClassLoaderContextTest, CreateClassLoaderWithComplexChain) { + // Setup the context. + std::vector<std::unique_ptr<const DexFile>> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_d = OpenTestDexFiles("ForClassLoaderD"); + + std::string context_spec = + "PCL[" + CreateClassPath(classpath_dex_a) + ":" + CreateClassPath(classpath_dex_b) + "];" + + "DLC[" + CreateClassPath(classpath_dex_c) + "];" + + "PCL[" + CreateClassPath(classpath_dex_d) + "]"; + + std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_spec); + ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, "")); + // Setup the compilation sources. + std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex"); + std::vector<const DexFile*> compilation_sources_raw = + MakeNonOwningPointerVector(compilation_sources); - std::vector<const DexFile*> class_loader_dex_files = GetDexFiles(jclass_loader); + // Create the class loader. + jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw); + ASSERT_TRUE(jclass_loader != nullptr); + + // Verify the class loader. + ScopedObjectAccess soa(Thread::Current()); - // The compilation sources should be the only files present in the class loader - ASSERT_EQ(compilation_sources.size(), class_loader_dex_files.size()); - for (size_t i = 0; i < compilation_sources.size(); i++) { - ASSERT_EQ(compilation_sources[i]->GetLocation(), - class_loader_dex_files[i]->GetLocation()); - ASSERT_EQ(compilation_sources[i]->GetLocationChecksum(), - class_loader_dex_files[i]->GetLocationChecksum()); + StackHandleScope<3> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle( + soa.Decode<mirror::ClassLoader>(jclass_loader)); + + // Verify the first class loader + + // For the first class loader the class path dex files must come first and then the + // compilation sources. + std::vector<const DexFile*> class_loader_1_dex_files = + MakeNonOwningPointerVector(classpath_dex_a); + for (auto& dex : classpath_dex_b) { + class_loader_1_dex_files.push_back(dex.get()); } + for (auto& dex : compilation_sources_raw) { + class_loader_1_dex_files.push_back(dex); + } + VerifyClassLoaderDexFiles(soa, + class_loader_1, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_1_dex_files); + + // Verify the second class loader + Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(class_loader_1->GetParent()); + std::vector<const DexFile*> class_loader_2_dex_files = + MakeNonOwningPointerVector(classpath_dex_c); + VerifyClassLoaderDexFiles(soa, + class_loader_2, + WellKnownClasses::dalvik_system_DelegateLastClassLoader, + class_loader_2_dex_files); + + // Verify the third class loader + Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_2->GetParent()); + std::vector<const DexFile*> class_loader_3_dex_files = + MakeNonOwningPointerVector(classpath_dex_d); + VerifyClassLoaderDexFiles(soa, + class_loader_3, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_3_dex_files); + // The last class loader should have the BootClassLoader as a parent. + ASSERT_TRUE(class_loader_3->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); } + TEST_F(ClassLoaderContextTest, RemoveSourceLocations) { std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("PCL[a.dex]"); @@ -338,9 +410,8 @@ TEST_F(ClassLoaderContextTest, EncodeInOatFile) { std::vector<std::unique_ptr<const DexFile>> dex1 = OpenTestDexFiles("Main"); std::vector<std::unique_ptr<const DexFile>> dex2 = OpenTestDexFiles("MyClass"); std::string encoding = context->EncodeContextForOatFile(""); - std::string expected_encoding = "PCL[" + - dex1[0]->GetLocation() + "*" + std::to_string(dex1[0]->GetLocationChecksum()) + ":" + - dex2[0]->GetLocation() + "*" + std::to_string(dex2[0]->GetLocationChecksum()) + "]"; + std::string expected_encoding = "PCL[" + CreateClassPathWithChecksums(dex1) + ":" + + CreateClassPathWithChecksums(dex2) + "]"; ASSERT_EQ(expected_encoding, context->EncodeContextForOatFile("")); } diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 659c7e4950..aae997327c 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -589,18 +589,24 @@ std::unique_ptr<const DexFile> CommonRuntimeTestImpl::OpenTestDexFile(const char } std::vector<const DexFile*> CommonRuntimeTestImpl::GetDexFiles(jobject jclass_loader) { - std::vector<const DexFile*> ret; - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<1> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader = hs.NewHandle( soa.Decode<mirror::ClassLoader>(jclass_loader)); + return GetDexFiles(soa, class_loader); +} - DCHECK_EQ(class_loader->GetClass(), - soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)); - DCHECK_EQ(class_loader->GetParent()->GetClass(), - soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); +std::vector<const DexFile*> CommonRuntimeTestImpl::GetDexFiles( + ScopedObjectAccess& soa, + Handle<mirror::ClassLoader> class_loader) { + std::vector<const DexFile*> ret; + + DCHECK( + (class_loader->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) || + (class_loader->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader))); // The class loader is a PathClassLoader which inherits from BaseDexClassLoader. // We need to get the DexPathList and loop through it. @@ -618,6 +624,7 @@ std::vector<const DexFile*> CommonRuntimeTestImpl::GetDexFiles(jobject jclass_lo // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look // at the mCookie which is a DexFile vector. if (dex_elements_obj != nullptr) { + StackHandleScope<1> hs(soa.Self()); Handle<mirror::ObjectArray<mirror::Object>> dex_elements = hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>()); for (int32_t i = 0; i < dex_elements->GetLength(); ++i) { @@ -757,6 +764,28 @@ std::string CommonRuntimeTestImpl::GetCoreFileLocation(const char* suffix) { return location; } +std::string CommonRuntimeTestImpl::CreateClassPath( + const std::vector<std::unique_ptr<const DexFile>>& dex_files) { + CHECK(!dex_files.empty()); + std::string classpath = dex_files[0]->GetLocation(); + for (size_t i = 1; i < dex_files.size(); i++) { + classpath += ":" + dex_files[i]->GetLocation(); + } + return classpath; +} + +std::string CommonRuntimeTestImpl::CreateClassPathWithChecksums( + const std::vector<std::unique_ptr<const DexFile>>& dex_files) { + CHECK(!dex_files.empty()); + std::string classpath = dex_files[0]->GetLocation() + "*" + + std::to_string(dex_files[0]->GetLocationChecksum()); + for (size_t i = 1; i < dex_files.size(); i++) { + classpath += ":" + dex_files[i]->GetLocation() + "*" + + std::to_string(dex_files[i]->GetLocationChecksum()); + } + return classpath; +} + CheckJniAbortCatcher::CheckJniAbortCatcher() : vm_(Runtime::Current()->GetJavaVM()) { vm_->SetCheckJniAbortHook(Hook, &actual_); } diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index fcf3a31fbc..daf9ac344e 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -28,6 +28,7 @@ // TODO: Add inl file and avoid including inl. #include "obj_ptr-inl.h" #include "os.h" +#include "scoped_thread_state_change-inl.h" namespace art { @@ -159,9 +160,12 @@ class CommonRuntimeTestImpl { const DexFile* java_lang_dex_file_; std::vector<const DexFile*> boot_class_path_; - // Get the dex files from a PathClassLoader. This in order of the dex elements and their dex - // arrays. + // Get the dex files from a PathClassLoader or DelegateLastClassLoader. + // This only looks into the current class loader and does not recurse into the parents. std::vector<const DexFile*> GetDexFiles(jobject jclass_loader); + std::vector<const DexFile*> GetDexFiles(ScopedObjectAccess& soa, + Handle<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_); // Get the first dex file from a PathClassLoader. Will abort if it is null. const DexFile* GetFirstDexFile(jobject jclass_loader); @@ -176,6 +180,15 @@ class CommonRuntimeTestImpl { // initializers, initialize well-known classes, and creates the heap thread pool. virtual void FinalizeSetup(); + // Creates the class path string for the given dex files (the list of dex file locations + // separated by ':'). + std::string CreateClassPath( + const std::vector<std::unique_ptr<const DexFile>>& dex_files); + // Same as CreateClassPath but add the dex file checksum after each location. The separator + // is '*'. + std::string CreateClassPathWithChecksums( + const std::vector<std::unique_ptr<const DexFile>>& dex_files); + private: static std::string GetCoreFileLocation(const char* suffix); |