| /* | |
| * 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. | |
| */ | |
| #include "class_loader_context.h" | |
| #include <gtest/gtest.h> | |
| #include <filesystem> | |
| #include <fstream> | |
| #include <optional> | |
| #include <vector> | |
| #include "android-base/stringprintf.h" | |
| #include "android-base/strings.h" | |
| #include "art_field-inl.h" | |
| #include "art_method-alloc-inl.h" | |
| #include "base/dchecked_vector.h" | |
| #include "base/stl_util.h" | |
| #include "class_linker.h" | |
| #include "class_root-inl.h" | |
| #include "common_runtime_test.h" | |
| #include "dex/dex_file.h" | |
| #include "handle_scope-inl.h" | |
| #include "jni/jni_internal.h" | |
| #include "mirror/class.h" | |
| #include "mirror/class_loader-inl.h" | |
| #include "mirror/object-inl.h" | |
| #include "mirror/object_array-alloc-inl.h" | |
| #include "oat/oat_file_assistant.h" | |
| #include "runtime.h" | |
| #include "scoped_thread_state_change-inl.h" | |
| #include "thread.h" | |
| #include "well_known_classes-inl.h" | |
| namespace art HIDDEN { | |
| class ClassLoaderContextTest : public CommonRuntimeTest { | |
| public: | |
| ClassLoaderContextTest() { | |
| use_boot_image_ = true; // Make the Runtime creation cheaper. | |
| } | |
| void SetUp() override { | |
| CommonRuntimeTest::SetUp(); | |
| scratch_dir_ = std::make_unique<ScratchDir>(); | |
| scratch_path_ = scratch_dir_->GetPath(); | |
| // Remove the trailing '/'; | |
| scratch_path_.resize(scratch_path_.length() - 1); | |
| } | |
| void TearDown() override { | |
| scratch_dir_.reset(); | |
| CommonRuntimeTest::TearDown(); | |
| } | |
| void VerifyContextSize(ClassLoaderContext* context, size_t expected_size) { | |
| ASSERT_TRUE(context != nullptr); | |
| ASSERT_EQ(expected_size, context->GetParentChainSize()); | |
| } | |
| void VerifyClassLoaderPCL(ClassLoaderContext* context, | |
| size_t index, | |
| const std::string& classpath) { | |
| VerifyClassLoaderInfo( | |
| context, index, ClassLoaderContext::kPathClassLoader, classpath); | |
| } | |
| void VerifyClassLoaderDLC(ClassLoaderContext* context, | |
| size_t index, | |
| const std::string& classpath) { | |
| VerifyClassLoaderInfo( | |
| context, index, ClassLoaderContext::kDelegateLastClassLoader, classpath); | |
| } | |
| void VerifyClassLoaderIMC(ClassLoaderContext* context, | |
| size_t index, | |
| const std::string& classpath) { | |
| VerifyClassLoaderInfo( | |
| context, index, ClassLoaderContext::kInMemoryDexClassLoader, classpath); | |
| } | |
| void VerifyClassLoaderSharedLibraryPCL(ClassLoaderContext* context, | |
| size_t loader_index, | |
| size_t shared_library_index, | |
| const std::string& classpath) { | |
| VerifyClassLoaderInfoSL( | |
| context, loader_index, shared_library_index, ClassLoaderContext::kPathClassLoader, | |
| classpath); | |
| } | |
| void VerifyClassLoaderSharedLibraryPCLAfter(ClassLoaderContext* context, | |
| size_t loader_index, | |
| size_t shared_library_index, | |
| const std::string& classpath) { | |
| VerifyClassLoaderInfoSLAfter( | |
| context, loader_index, shared_library_index, ClassLoaderContext::kPathClassLoader, | |
| classpath); | |
| } | |
| void VerifyClassLoaderSharedLibraryIMC(ClassLoaderContext* context, | |
| size_t loader_index, | |
| size_t shared_library_index, | |
| const std::string& classpath) { | |
| VerifyClassLoaderInfoSL( | |
| context, loader_index, shared_library_index, ClassLoaderContext::kInMemoryDexClassLoader, | |
| classpath); | |
| } | |
| void VerifySharedLibrariesSize(ClassLoaderContext* context, | |
| size_t loader_index, | |
| size_t expected_size) { | |
| ASSERT_TRUE(context != nullptr); | |
| ASSERT_GT(context->GetParentChainSize(), loader_index); | |
| const ClassLoaderContext::ClassLoaderInfo& info = *context->GetParent(loader_index); | |
| ASSERT_EQ(info.shared_libraries.size(), expected_size); | |
| } | |
| void VerifyClassLoaderSharedLibraryDLC(ClassLoaderContext* context, | |
| size_t loader_index, | |
| size_t shared_library_index, | |
| const std::string& classpath) { | |
| VerifyClassLoaderInfoSL( | |
| context, loader_index, shared_library_index, ClassLoaderContext::kDelegateLastClassLoader, | |
| classpath); | |
| } | |
| void VerifyClassLoaderPCLFromTestDex(ClassLoaderContext* context, | |
| size_t index, | |
| const std::string& test_name) { | |
| VerifyClassLoaderFromTestDex( | |
| context, index, ClassLoaderContext::kPathClassLoader, test_name); | |
| } | |
| void VerifyClassLoaderDLCFromTestDex(ClassLoaderContext* context, | |
| size_t index, | |
| const std::string& test_name) { | |
| VerifyClassLoaderFromTestDex( | |
| context, index, ClassLoaderContext::kDelegateLastClassLoader, test_name); | |
| } | |
| void VerifyClassLoaderIMCFromTestDex(ClassLoaderContext* context, | |
| size_t index, | |
| const std::string& test_name) { | |
| VerifyClassLoaderFromTestDex( | |
| context, index, ClassLoaderContext::kInMemoryDexClassLoader, test_name, "<unknown>"); | |
| } | |
| enum class LocationCheck { | |
| kEquals, | |
| kEndsWith | |
| }; | |
| enum class BaseLocationCheck { | |
| kEquals, | |
| kEndsWith | |
| }; | |
| static bool IsAbsoluteLocation(const std::string& location) { | |
| return !location.empty() && location[0] == '/'; | |
| } | |
| void VerifyOpenDexFiles( | |
| ClassLoaderContext* context, | |
| size_t index, | |
| std::vector<std::unique_ptr<const DexFile>>* all_dex_files, | |
| bool classpath_matches_dex_location = true, | |
| bool only_read_checksums = false) { | |
| ASSERT_TRUE(context != nullptr); | |
| if (only_read_checksums) { | |
| ASSERT_EQ(context->dex_files_state_, | |
| ClassLoaderContext::ContextDexFilesState::kDexFilesChecksumsRead); | |
| } else { | |
| ASSERT_EQ(context->dex_files_state_, | |
| ClassLoaderContext::ContextDexFilesState::kDexFilesOpened); | |
| } | |
| ClassLoaderContext::ClassLoaderInfo& info = *context->GetParent(index); | |
| std::vector<const DexFile*> primary_dex_files; | |
| std::vector<std::optional<uint32_t>> primary_checksums; | |
| for (size_t i = 0; i < all_dex_files->size();) { | |
| primary_dex_files.push_back((*all_dex_files)[i].get()); | |
| primary_checksums.push_back(DexFileLoader::GetMultiDexChecksum(*all_dex_files, &i)); | |
| } | |
| ASSERT_EQ(primary_dex_files.size(), info.classpath.size()); | |
| ASSERT_EQ(primary_dex_files.size(), info.checksums.size()); | |
| if (only_read_checksums) { | |
| ASSERT_EQ(0u, info.opened_dex_files.size()); | |
| for (size_t k = 0; k < primary_dex_files.size(); k++) { | |
| const std::string& opened_location = info.classpath[k]; | |
| uint32_t opened_checksum = info.checksums[k]; | |
| const DexFile* expected_dex_file = primary_dex_files[k]; | |
| std::string expected_location = expected_dex_file->GetLocation(); | |
| if (!IsAbsoluteLocation(opened_location)) { | |
| // If the opened location is relative (it was open from a relative path without a | |
| // classpath_dir) it might not match the expected location which is absolute in tests). | |
| // So we compare the endings (the checksum will validate it's actually the same file). | |
| ASSERT_TRUE(expected_location.ends_with(opened_location)) | |
| << expected_location << " " << opened_location; | |
| } else { | |
| ASSERT_EQ(expected_location, opened_location); | |
| } | |
| ASSERT_EQ(primary_checksums[k], opened_checksum); | |
| if (classpath_matches_dex_location) { | |
| ASSERT_EQ(info.classpath[k], opened_location); | |
| } | |
| } | |
| } else { | |
| ASSERT_EQ(all_dex_files->size(), info.opened_dex_files.size()); | |
| for (size_t k = 0; k < all_dex_files->size(); k++) { | |
| const std::string& opened_location = info.opened_dex_files[k]->GetLocation(); | |
| uint32_t opened_checksum = info.opened_dex_files[k]->GetLocationChecksum(); | |
| std::unique_ptr<const DexFile>& expected_dex_file = (*all_dex_files)[k]; | |
| std::string expected_location = expected_dex_file->GetLocation(); | |
| if (!IsAbsoluteLocation(opened_location)) { | |
| // If the opened location is relative (it was open from a relative path without a | |
| // classpath_dir) it might not match the expected location which is absolute in tests). | |
| // So we compare the endings (the checksum will validate it's actually the same file). | |
| ASSERT_TRUE(expected_location.ends_with(opened_location)) | |
| << expected_location << " " << opened_location; | |
| } else { | |
| ASSERT_EQ(expected_location, opened_location); | |
| } | |
| ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_checksum); | |
| if (classpath_matches_dex_location) { | |
| ASSERT_EQ(info.classpath[k], opened_location); | |
| } | |
| } | |
| } | |
| } | |
| std::unique_ptr<ClassLoaderContext> CreateContextForClassLoader(jobject class_loader) { | |
| return ClassLoaderContext::CreateContextForClassLoader(class_loader, nullptr); | |
| } | |
| std::unique_ptr<ClassLoaderContext> ParseContextWithChecksums(const std::string& context_spec) { | |
| std::unique_ptr<ClassLoaderContext> context(new ClassLoaderContext()); | |
| if (!context->Parse(context_spec, /*parse_checksums=*/ true)) { | |
| return nullptr; | |
| } | |
| return context; | |
| } | |
| void VerifyContextForClassLoader(ClassLoaderContext* context) { | |
| ASSERT_TRUE(context != nullptr); | |
| ASSERT_EQ(context->dex_files_state_, ClassLoaderContext::ContextDexFilesState::kDexFilesOpened); | |
| ASSERT_FALSE(context->owns_the_dex_files_); | |
| } | |
| void VerifyClassLoaderDexFiles(Thread* self, | |
| Handle<mirror::ClassLoader> class_loader, | |
| ObjPtr<mirror::Class> type, | |
| std::vector<const DexFile*>& expected_dex_files) | |
| REQUIRES_SHARED(Locks::mutator_lock_) { | |
| ASSERT_TRUE(class_loader->GetClass() == type); | |
| std::vector<const DexFile*> class_loader_dex_files = GetDexFiles(self, 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()); | |
| } | |
| } | |
| void PretendContextOpenedDexFiles(ClassLoaderContext* context) { | |
| context->dex_files_state_ = ClassLoaderContext::ContextDexFilesState::kDexFilesOpened; | |
| } | |
| void PretendContextOpenedDexFilesForChecksums(ClassLoaderContext* context) { | |
| context->dex_files_state_ = ClassLoaderContext::ContextDexFilesState::kDexFilesChecksumsRead; | |
| } | |
| void TestOpenDexFiles(bool only_read_checksums) { | |
| std::string multidex_name = GetTestDexFileName("MultiDex"); | |
| std::string myclass_dex_name = GetTestDexFileName("MyClass"); | |
| std::string dex_name = GetTestDexFileName("Main"); | |
| std::unique_ptr<ClassLoaderContext> context = | |
| ClassLoaderContext::Create( | |
| "PCL[" + multidex_name + ":" + myclass_dex_name + "];" + | |
| "DLC[" + dex_name + "]"); | |
| ASSERT_TRUE(context->OpenDexFiles( | |
| /*classpath_dir=*/ "", | |
| /*context_fds=*/ std::vector<int>(), | |
| only_read_checksums)); | |
| VerifyContextSize(context.get(), 2); | |
| std::vector<std::unique_ptr<const DexFile>> all_dex_files0 = OpenTestDexFiles("MultiDex"); | |
| std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass"); | |
| for (size_t i = 0; i < myclass_dex_files.size(); i++) { | |
| all_dex_files0.emplace_back(myclass_dex_files[i].release()); | |
| } | |
| VerifyOpenDexFiles(context.get(), | |
| /*index=*/ 0, | |
| &all_dex_files0, | |
| /*classpath_matches_dex_location=*/ false, | |
| only_read_checksums); | |
| std::vector<std::unique_ptr<const DexFile>> all_dex_files1 = OpenTestDexFiles("Main"); | |
| VerifyOpenDexFiles(context.get(), | |
| /*index=*/ 1, | |
| &all_dex_files1, | |
| /*classpath_matches_dex_location=*/ false, | |
| only_read_checksums); | |
| } | |
| void TestOpenValidDexFilesRelative(bool use_classpath_dir, bool only_read_checksums) { | |
| char cwd_buf[4096]; | |
| if (getcwd(cwd_buf, arraysize(cwd_buf)) == nullptr) { | |
| PLOG(FATAL) << "Could not get working directory"; | |
| } | |
| std::string multidex_name; | |
| std::string myclass_dex_name; | |
| std::string dex_name; | |
| if (!CreateRelativeString(GetTestDexFileName("MultiDex"), cwd_buf, &multidex_name) || | |
| !CreateRelativeString(GetTestDexFileName("MyClass"), cwd_buf, &myclass_dex_name) || | |
| !CreateRelativeString(GetTestDexFileName("Main"), cwd_buf, &dex_name)) { | |
| LOG(ERROR) << "Test OpenValidDexFilesRelative cannot be run because target dex files have no " | |
| << "relative path."; | |
| SUCCEED(); | |
| return; | |
| } | |
| std::unique_ptr<ClassLoaderContext> context = | |
| ClassLoaderContext::Create( | |
| "PCL[" + multidex_name + ":" + myclass_dex_name + "];" + | |
| "DLC[" + dex_name + "]"); | |
| ASSERT_TRUE(context->OpenDexFiles( | |
| /*classpath_dir=*/ use_classpath_dir ? cwd_buf : "", | |
| /*context_fds=*/ std::vector<int>(), | |
| only_read_checksums)); | |
| VerifyContextSize(context.get(), 2); | |
| std::vector<std::unique_ptr<const DexFile>> all_dex_files0 = OpenTestDexFiles("MultiDex"); | |
| std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass"); | |
| for (size_t i = 0; i < myclass_dex_files.size(); i++) { | |
| all_dex_files0.emplace_back(myclass_dex_files[i].release()); | |
| } | |
| VerifyOpenDexFiles(context.get(), | |
| /*index=*/ 0, | |
| &all_dex_files0, | |
| /*classpath_matches_dex_location=*/ false, | |
| only_read_checksums); | |
| std::vector<std::unique_ptr<const DexFile>> all_dex_files1 = OpenTestDexFiles("Main"); | |
| VerifyOpenDexFiles(context.get(), | |
| /*index=*/ 1, | |
| &all_dex_files1, | |
| /*classpath_matches_dex_location=*/ false, | |
| only_read_checksums); | |
| } | |
| // Creates a relative path from cwd to 'in'. Returns false if it cannot be done. | |
| // TODO We should somehow support this in all situations. b/72042237. | |
| bool CreateRelativeString(const std::string& in, const char* cwd, std::string* out) { | |
| int cwd_len = strlen(cwd); | |
| if (!in.starts_with(cwd) || (cwd_len < 1)) { | |
| return false; | |
| } | |
| bool contains_trailing_slash = (cwd[cwd_len - 1] == '/'); | |
| int start_position = cwd_len + (contains_trailing_slash ? 0 : 1); | |
| *out = in.substr(start_position); | |
| return true; | |
| } | |
| std::unique_ptr<ScratchDir> scratch_dir_; | |
| std::string scratch_path_; | |
| private: | |
| void VerifyClassLoaderInfo(ClassLoaderContext* context, | |
| size_t index, | |
| ClassLoaderContext::ClassLoaderType type, | |
| const std::string& classpath) { | |
| ASSERT_TRUE(context != nullptr); | |
| ASSERT_GT(context->GetParentChainSize(), index); | |
| ClassLoaderContext::ClassLoaderInfo& info = *context->GetParent(index); | |
| ASSERT_EQ(type, info.type); | |
| std::vector<std::string> expected_classpath; | |
| Split(classpath, ':', &expected_classpath); | |
| ASSERT_EQ(expected_classpath, info.classpath); | |
| } | |
| void VerifyClassLoaderInfoSL(ClassLoaderContext* context, | |
| size_t loader_index, | |
| size_t shared_library_index, | |
| ClassLoaderContext::ClassLoaderType type, | |
| const std::string& classpath) { | |
| ASSERT_TRUE(context != nullptr); | |
| ASSERT_GT(context->GetParentChainSize(), loader_index); | |
| const ClassLoaderContext::ClassLoaderInfo& info = *context->GetParent(loader_index); | |
| ASSERT_GT(info.shared_libraries.size(), shared_library_index); | |
| const ClassLoaderContext::ClassLoaderInfo& sl = | |
| *info.shared_libraries[shared_library_index].get(); | |
| ASSERT_EQ(type, sl.type); | |
| std::vector<std::string> expected_classpath; | |
| Split(classpath, ':', &expected_classpath); | |
| ASSERT_EQ(expected_classpath, sl.classpath); | |
| } | |
| void VerifyClassLoaderInfoSLAfter(ClassLoaderContext* context, | |
| size_t loader_index, | |
| size_t shared_library_index, | |
| ClassLoaderContext::ClassLoaderType type, | |
| const std::string& classpath) { | |
| ASSERT_TRUE(context != nullptr); | |
| ASSERT_GT(context->GetParentChainSize(), loader_index); | |
| const ClassLoaderContext::ClassLoaderInfo& info = *context->GetParent(loader_index); | |
| ASSERT_GT(info.shared_libraries_after.size(), shared_library_index); | |
| const ClassLoaderContext::ClassLoaderInfo& sl = | |
| *info.shared_libraries_after[shared_library_index].get(); | |
| ASSERT_EQ(type, sl.type); | |
| std::vector<std::string> expected_classpath; | |
| Split(classpath, ':', &expected_classpath); | |
| ASSERT_EQ(expected_classpath, sl.classpath); | |
| } | |
| void VerifyClassLoaderFromTestDex(ClassLoaderContext* context, | |
| size_t index, | |
| ClassLoaderContext::ClassLoaderType type, | |
| const std::string& test_name, | |
| const std::string& classpath = "") { | |
| std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(test_name.c_str()); | |
| // If `classpath` is set, override the expected value of ClassLoaderInfo::classpath. | |
| // Otherwise assume it is equal to dex location (here test dex file name). | |
| VerifyClassLoaderInfo(context, | |
| index, | |
| type, | |
| classpath.empty() ? GetTestDexFileName(test_name.c_str()) : classpath); | |
| VerifyOpenDexFiles(context, | |
| index, | |
| &dex_files, | |
| /* classpath_matches_dex_location= */ classpath.empty()); | |
| } | |
| }; | |
| TEST_F(ClassLoaderContextTest, ParseValidEmptyContext) { | |
| std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(""); | |
| // An empty context should create a single empty PathClassLoader. | |
| VerifyContextSize(context.get(), 1); | |
| VerifyClassLoaderPCL(context.get(), 0, ""); | |
| } | |
| TEST_F(ClassLoaderContextTest, ParseInvalidSharedLibraryContext) { | |
| // '&' used to be a special context. | |
| std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("&"); | |
| ASSERT_TRUE(context == nullptr); | |
| } | |
| TEST_F(ClassLoaderContextTest, ParseValidContextPCL) { | |
| std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("PCL[a.dex]"); | |
| VerifyContextSize(context.get(), 1); | |
| VerifyClassLoaderPCL(context.get(), 0, "a.dex"); | |
| } | |
| TEST_F(ClassLoaderContextTest, ParseValidContextDLC) { | |
| std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("DLC[a.dex]"); | |
| VerifyContextSize(context.get(), 1); | |
| VerifyClassLoaderDLC(context.get(), 0, "a.dex"); | |
| } | |
| TEST_F(ClassLoaderContextTest, ParseValidContextIMC) { | |
| std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums("IMC[<unknown>*111]"); | |
| ASSERT_FALSE(context == nullptr); | |
| } | |
| TEST_F(ClassLoaderContextTest, ParseInvalidContextIMCNoChecksum) { | |
| // IMC is treated as an unknown class loader unless a checksum is provided. | |
| // This is because the dex location is always bogus. | |
| std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("IMC[<unknown>]"); | |
| ASSERT_TRUE(context == nullptr); | |
| } | |
| TEST_F(ClassLoaderContextTest, ParseInvalidContextIMCWrongClasspathMagic) { | |
| // IMC does not support arbitrary dex location. A magic marker must be used | |
| // otherwise the spec should be rejected. | |
| std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("IMC[a.dex*111]"); | |
| ASSERT_TRUE(context == nullptr); | |
| } | |
| TEST_F(ClassLoaderContextTest, ParseValidContextChain) { | |
| std::unique_ptr<ClassLoaderContext> context = | |
| ClassLoaderContext::Create("PCL[a.dex:b.dex];DLC[c.dex:d.dex];PCL[e.dex]"); | |
| VerifyContextSize(context.get(), 3); | |
| VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex"); | |
| VerifyClassLoaderDLC(context.get(), 1, "c.dex:d.dex"); | |
| VerifyClassLoaderPCL(context.get(), 2, "e.dex"); | |
| } | |
| TEST_F(ClassLoaderContextTest, ParseSharedLibraries) { | |
| std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create( | |
| "PCL[a.dex:b.dex]{PCL[s1.dex]#PCL[s2.dex:s3.dex]#~PCL[s5.dex]#~PCL[s6.dex:s7.dex]};" | |
| "DLC[c.dex:d.dex]{DLC[s4.dex]}"); | |
| VerifyContextSize(context.get(), 2); | |
| VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s1.dex"); | |
| VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 1, "s2.dex:s3.dex"); | |
| VerifyClassLoaderSharedLibraryPCLAfter(context.get(), 0, 0, "s5.dex"); | |
| VerifyClassLoaderSharedLibraryPCLAfter(context.get(), 0, 1, "s6.dex:s7.dex"); | |
| VerifyClassLoaderDLC(context.get(), 1, "c.dex:d.dex"); | |
| VerifyClassLoaderSharedLibraryDLC(context.get(), 1, 0, "s4.dex"); | |
| } | |
| TEST_F(ClassLoaderContextTest, ParseEnclosingSharedLibraries) { | |
| std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create( | |
| "PCL[a.dex:b.dex]{PCL[s1.dex]{PCL[s2.dex:s3.dex];PCL[s4.dex]}}"); | |
| VerifyContextSize(context.get(), 1); | |
| VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s1.dex"); | |
| } | |
| TEST_F(ClassLoaderContextTest, ParseComplexSharedLibraries1) { | |
| std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create( | |
| "PCL[]{PCL[s4.dex]{PCL[s5.dex]{PCL[s6.dex]}#PCL[s6.dex]}}"); | |
| VerifyContextSize(context.get(), 1); | |
| VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s4.dex"); | |
| } | |
| TEST_F(ClassLoaderContextTest, ParseComplexSharedLibraries2) { | |
| std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create( | |
| "PCL[]{PCL[s1.dex]{PCL[s2.dex]}#PCL[s2.dex]#" | |
| "PCL[s3.dex]#PCL[s4.dex]{PCL[s5.dex]{PCL[s6.dex]}#PCL[s6.dex]}#PCL[s5.dex]{PCL[s6.dex]}}"); | |
| VerifyContextSize(context.get(), 1); | |
| VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s1.dex"); | |
| VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 1, "s2.dex"); | |
| VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 2, "s3.dex"); | |
| VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 3, "s4.dex"); | |
| VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 4, "s5.dex"); | |
| } | |
| TEST_F(ClassLoaderContextTest, ParseValidEmptyContextDLC) { | |
| std::unique_ptr<ClassLoaderContext> context = | |
| ClassLoaderContext::Create("DLC[]"); | |
| VerifyContextSize(context.get(), 1); | |
| VerifyClassLoaderDLC(context.get(), 0, ""); | |
| } | |
| TEST_F(ClassLoaderContextTest, ParseValidEmptyContextSharedLibrary) { | |
| std::unique_ptr<ClassLoaderContext> context = | |
| ClassLoaderContext::Create("DLC[]{}"); | |
| VerifyContextSize(context.get(), 1); | |
| VerifySharedLibrariesSize(context.get(), 0, 0); | |
| } | |
| TEST_F(ClassLoaderContextTest, ParseInvalidValidContexts) { | |
| ASSERT_TRUE(nullptr == ClassLoaderContext::Create("ABC[a.dex]")); | |
| ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL")); | |
| ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL[a.dex")); | |
| ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCLa.dex]")); | |
| ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{a.dex}")); | |
| ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL[a.dex];DLC[b.dex")); | |
| ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL[a.dex]{ABC};DLC[b.dex")); | |
| ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL[a.dex]{};DLC[b.dex")); | |
| ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC[s4.dex]}")); | |
| ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC[s4.dex]{")); | |
| ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC{DLC[s4.dex]}")); | |
| ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{##}")); | |
| ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{PCL[s4.dex]#}")); | |
| ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{PCL[s4.dex]##}")); | |
| ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{PCL[s4.dex]{PCL[s3.dex]}#}")); | |
| } | |
| TEST_F(ClassLoaderContextTest, OpenInvalidDexFiles) { | |
| std::unique_ptr<ClassLoaderContext> context = | |
| ClassLoaderContext::Create("PCL[does_not_exist.dex]"); | |
| VerifyContextSize(context.get(), 1); | |
| ASSERT_FALSE(context->OpenDexFiles(".")); | |
| } | |
| TEST_F(ClassLoaderContextTest, ReadChecksumsInvalidDexFiles) { | |
| std::unique_ptr<ClassLoaderContext> context = | |
| ClassLoaderContext::Create("PCL[does_not_exist.dex]"); | |
| VerifyContextSize(context.get(), 1); | |
| ASSERT_FALSE(context->OpenDexFiles( | |
| /*classpath_dir=*/ ".", | |
| /*context_fds=*/ std::vector<int>(), | |
| /*only_read_checksums=*/ true)); | |
| } | |
| TEST_F(ClassLoaderContextTest, OpenValidDexFiles) { | |
| TestOpenDexFiles(/*only_read_checksums=*/ false); | |
| } | |
| TEST_F(ClassLoaderContextTest, ReadDexFileChecksums) { | |
| TestOpenDexFiles(/*only_read_checksums=*/ true); | |
| } | |
| TEST_F(ClassLoaderContextTest, OpenValidDexFilesRelative) { | |
| TestOpenValidDexFilesRelative(/*use_classpath_dir=*/ false, /*only_read_checksums=*/ false); | |
| } | |
| TEST_F(ClassLoaderContextTest, ReadChecksumsValidDexFilesRelative) { | |
| TestOpenValidDexFilesRelative(/*use_classpath_dir=*/ false, /*only_read_checksums=*/ true); | |
| } | |
| TEST_F(ClassLoaderContextTest, OpenValidDexFilesClasspathDir) { | |
| TestOpenValidDexFilesRelative(/*use_classpath_dir=*/ true, /*only_read_checksums=*/ false); | |
| } | |
| TEST_F(ClassLoaderContextTest, ReadChecksumsValidDexFilesClasspathDir) { | |
| TestOpenValidDexFilesRelative(/*use_classpath_dir=*/ true, /*only_read_checksums=*/ true); | |
| } | |
| TEST_F(ClassLoaderContextTest, OpenInvalidDexFilesMix) { | |
| std::string dex_name = GetTestDexFileName("Main"); | |
| std::unique_ptr<ClassLoaderContext> context = | |
| ClassLoaderContext::Create("PCL[does_not_exist.dex];DLC[" + dex_name + "]"); | |
| ASSERT_FALSE(context->OpenDexFiles()); | |
| } | |
| TEST_F(ClassLoaderContextTest, ReadChecksumsInvalidDexFilesMix) { | |
| std::string dex_name = GetTestDexFileName("Main"); | |
| std::unique_ptr<ClassLoaderContext> context = | |
| ClassLoaderContext::Create("PCL[does_not_exist.dex];DLC[" + dex_name + "]"); | |
| ASSERT_FALSE(context->OpenDexFiles( | |
| /*classpath_dir=*/ "", | |
| /*context_fds=*/ std::vector<int>(), | |
| /*only_read_checksums=*/ true)); | |
| } | |
| TEST_F(ClassLoaderContextTest, OpenDexFilesForIMCFails) { | |
| std::unique_ptr<ClassLoaderContext> context; | |
| std::string dex_name = GetTestDexFileName("Main"); | |
| context = ParseContextWithChecksums("IMC[<unknown>*111]"); | |
| VerifyContextSize(context.get(), 1); | |
| ASSERT_FALSE(context->OpenDexFiles(".")); | |
| } | |
| // Verify that we can fully open the dex files after only reading their checksums. | |
| TEST_F(ClassLoaderContextTest, SubsequentOpenDexFilesOperations) { | |
| std::string dex_name = GetTestDexFileName("Main"); | |
| std::unique_ptr<ClassLoaderContext> context = | |
| ClassLoaderContext::Create("PCL[" + dex_name + "]"); | |
| std::vector<std::unique_ptr<const DexFile>> all_dex_files0 = OpenTestDexFiles("Main"); | |
| ASSERT_TRUE(context->OpenDexFiles( | |
| /*classpath_dir=*/ "", | |
| /*context_fds=*/ std::vector<int>(), | |
| /*only_read_checksums=*/ true)); | |
| VerifyOpenDexFiles( | |
| context.get(), | |
| /*index=*/ 0, | |
| &all_dex_files0, | |
| /*classpath_matches_dex_location=*/ false, | |
| /*only_read_checksums=*/ true); | |
| ASSERT_TRUE(context->OpenDexFiles( | |
| /*classpath_dir=*/ "", | |
| /*context_fds=*/ std::vector<int>(), | |
| /*only_read_checksums=*/ false)); | |
| VerifyOpenDexFiles( | |
| context.get(), | |
| /*index=*/ 0, | |
| &all_dex_files0, | |
| /*classpath_matches_dex_location=*/ false, | |
| /*only_read_checksums=*/ false); | |
| } | |
| TEST_F(ClassLoaderContextTest, CreateClassLoader) { | |
| std::string dex_name = GetTestDexFileName("Main"); | |
| std::unique_ptr<ClassLoaderContext> context = | |
| ClassLoaderContext::Create("PCL[" + dex_name + "]"); | |
| ASSERT_TRUE(context->OpenDexFiles()); | |
| std::vector<std::unique_ptr<const DexFile>> classpath_dex = OpenTestDexFiles("Main"); | |
| std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex"); | |
| std::vector<const DexFile*> compilation_sources_raw = | |
| MakeNonOwningPointerVector(compilation_sources); | |
| jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw); | |
| ASSERT_TRUE(jclass_loader != nullptr); | |
| ScopedObjectAccess soa(Thread::Current()); | |
| StackHandleScope<1> hs(soa.Self()); | |
| Handle<mirror::ClassLoader> class_loader = hs.NewHandle( | |
| soa.Decode<mirror::ClassLoader>(jclass_loader)); | |
| ASSERT_TRUE(class_loader->GetClass() == WellKnownClasses::dalvik_system_PathClassLoader); | |
| ASSERT_TRUE(class_loader->GetParent()->GetClass() == | |
| WellKnownClasses::java_lang_BootClassLoader); | |
| // 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); | |
| } | |
| VerifyClassLoaderDexFiles(soa.Self(), | |
| class_loader, | |
| WellKnownClasses::dalvik_system_PathClassLoader.Get(), | |
| expected_classpath); | |
| } | |
| TEST_F(ClassLoaderContextTest, CreateClassLoaderWithEmptyContext) { | |
| std::unique_ptr<ClassLoaderContext> context = | |
| ClassLoaderContext::Create(""); | |
| ASSERT_TRUE(context->OpenDexFiles()); | |
| std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex"); | |
| std::vector<const DexFile*> compilation_sources_raw = | |
| MakeNonOwningPointerVector(compilation_sources); | |
| jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw); | |
| ASSERT_TRUE(jclass_loader != nullptr); | |
| ScopedObjectAccess soa(Thread::Current()); | |
| StackHandleScope<1> hs(soa.Self()); | |
| Handle<mirror::ClassLoader> class_loader = hs.NewHandle( | |
| soa.Decode<mirror::ClassLoader>(jclass_loader)); | |
| // An empty context should create a single PathClassLoader with only the compilation sources. | |
| VerifyClassLoaderDexFiles(soa.Self(), | |
| class_loader, | |
| WellKnownClasses::dalvik_system_PathClassLoader.Get(), | |
| compilation_sources_raw); | |
| ASSERT_TRUE(class_loader->GetParent()->GetClass() == | |
| 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()); | |
| // 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); | |
| // 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()); | |
| 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.Self(), | |
| class_loader_1, | |
| WellKnownClasses::dalvik_system_PathClassLoader.Get(), | |
| 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.Self(), | |
| class_loader_2, | |
| WellKnownClasses::dalvik_system_DelegateLastClassLoader.Get(), | |
| 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.Self(), | |
| class_loader_3, | |
| WellKnownClasses::dalvik_system_PathClassLoader.Get(), | |
| class_loader_3_dex_files); | |
| // The last class loader should have the BootClassLoader as a parent. | |
| ASSERT_TRUE(class_loader_3->GetParent()->GetClass() == | |
| WellKnownClasses::java_lang_BootClassLoader); | |
| } | |
| TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibraries) { | |
| // 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()); | |
| // 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); | |
| // 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()); | |
| StackHandleScope<4> hs(soa.Self()); | |
| Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle( | |
| soa.Decode<mirror::ClassLoader>(jclass_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.Self(), | |
| class_loader_1, | |
| WellKnownClasses::dalvik_system_PathClassLoader.Get(), | |
| class_loader_1_dex_files); | |
| // Verify the shared libraries. | |
| ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders; | |
| ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get()); | |
| ASSERT_TRUE(raw_shared_libraries != nullptr); | |
| Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries( | |
| hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); | |
| ASSERT_EQ(shared_libraries->GetLength(), 2); | |
| // Verify the first shared library. | |
| Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0)); | |
| std::vector<const DexFile*> class_loader_2_dex_files = | |
| MakeNonOwningPointerVector(classpath_dex_c); | |
| VerifyClassLoaderDexFiles(soa.Self(), | |
| class_loader_2, | |
| WellKnownClasses::dalvik_system_DelegateLastClassLoader.Get(), | |
| class_loader_2_dex_files); | |
| raw_shared_libraries = field->GetObject(class_loader_2.Get()); | |
| ASSERT_TRUE(raw_shared_libraries == nullptr); | |
| // Verify the second shared library. | |
| Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(shared_libraries->Get(1)); | |
| std::vector<const DexFile*> class_loader_3_dex_files = | |
| MakeNonOwningPointerVector(classpath_dex_d); | |
| VerifyClassLoaderDexFiles(soa.Self(), | |
| class_loader_3, | |
| WellKnownClasses::dalvik_system_PathClassLoader.Get(), | |
| class_loader_3_dex_files); | |
| raw_shared_libraries = field->GetObject(class_loader_3.Get()); | |
| ASSERT_TRUE(raw_shared_libraries == nullptr); | |
| // All class loaders should have the BootClassLoader as a parent. | |
| ASSERT_TRUE(class_loader_1->GetParent()->GetClass() == | |
| WellKnownClasses::java_lang_BootClassLoader); | |
| ASSERT_TRUE(class_loader_2->GetParent()->GetClass() == | |
| WellKnownClasses::java_lang_BootClassLoader); | |
| ASSERT_TRUE(class_loader_3->GetParent()->GetClass() == | |
| WellKnownClasses::java_lang_BootClassLoader); | |
| } | |
| TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibrariesInParentToo) { | |
| // 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) + "]{" + | |
| "PCL[" + CreateClassPath(classpath_dex_b) + "]};" + | |
| "PCL[" + CreateClassPath(classpath_dex_c) + "]{" + | |
| "PCL[" + CreateClassPath(classpath_dex_d) + "]}"; | |
| std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_spec); | |
| ASSERT_TRUE(context->OpenDexFiles()); | |
| // 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); | |
| // 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()); | |
| StackHandleScope<6> hs(soa.Self()); | |
| Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle( | |
| soa.Decode<mirror::ClassLoader>(jclass_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 : compilation_sources_raw) { | |
| class_loader_1_dex_files.push_back(dex); | |
| } | |
| VerifyClassLoaderDexFiles(soa.Self(), | |
| class_loader_1, | |
| WellKnownClasses::dalvik_system_PathClassLoader.Get(), | |
| class_loader_1_dex_files); | |
| // Verify its shared library. | |
| ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders; | |
| ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get()); | |
| ASSERT_TRUE(raw_shared_libraries != nullptr); | |
| Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries( | |
| hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); | |
| ASSERT_EQ(shared_libraries->GetLength(), 1); | |
| Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0)); | |
| std::vector<const DexFile*> class_loader_2_dex_files = | |
| MakeNonOwningPointerVector(classpath_dex_b); | |
| VerifyClassLoaderDexFiles(soa.Self(), | |
| class_loader_2, | |
| WellKnownClasses::dalvik_system_PathClassLoader.Get(), | |
| class_loader_2_dex_files); | |
| raw_shared_libraries = field->GetObject(class_loader_2.Get()); | |
| ASSERT_TRUE(raw_shared_libraries == nullptr); | |
| // Verify the parent. | |
| Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_1->GetParent()); | |
| std::vector<const DexFile*> class_loader_3_dex_files = | |
| MakeNonOwningPointerVector(classpath_dex_c); | |
| VerifyClassLoaderDexFiles(soa.Self(), | |
| class_loader_3, | |
| WellKnownClasses::dalvik_system_PathClassLoader.Get(), | |
| class_loader_3_dex_files); | |
| // Verify its shared library. | |
| raw_shared_libraries = field->GetObject(class_loader_3.Get()); | |
| ASSERT_TRUE(raw_shared_libraries != nullptr); | |
| Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_2( | |
| hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); | |
| ASSERT_EQ(shared_libraries->GetLength(), 1); | |
| Handle<mirror::ClassLoader> class_loader_4 = hs.NewHandle(shared_libraries_2->Get(0)); | |
| std::vector<const DexFile*> class_loader_4_dex_files = | |
| MakeNonOwningPointerVector(classpath_dex_d); | |
| VerifyClassLoaderDexFiles(soa.Self(), | |
| class_loader_4, | |
| WellKnownClasses::dalvik_system_PathClassLoader.Get(), | |
| class_loader_4_dex_files); | |
| raw_shared_libraries = field->GetObject(class_loader_4.Get()); | |
| ASSERT_TRUE(raw_shared_libraries == nullptr); | |
| // Class loaders should have the BootClassLoader as a parent. | |
| ASSERT_TRUE(class_loader_2->GetParent()->GetClass() == | |
| WellKnownClasses::java_lang_BootClassLoader); | |
| ASSERT_TRUE(class_loader_3->GetParent()->GetClass() == | |
| WellKnownClasses::java_lang_BootClassLoader); | |
| ASSERT_TRUE(class_loader_4->GetParent()->GetClass() == | |
| WellKnownClasses::java_lang_BootClassLoader); | |
| } | |
| TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibrariesDependencies) { | |
| // 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) + "]{" + | |
| "PCL[" + CreateClassPath(classpath_dex_b) + "]{" + | |
| "PCL[" + CreateClassPath(classpath_dex_c) + "]}};" + | |
| "PCL[" + CreateClassPath(classpath_dex_d) + "]"; | |
| std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_spec); | |
| ASSERT_TRUE(context->OpenDexFiles()); | |
| // 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); | |
| // 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()); | |
| StackHandleScope<6> hs(soa.Self()); | |
| Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle( | |
| soa.Decode<mirror::ClassLoader>(jclass_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 : compilation_sources_raw) { | |
| class_loader_1_dex_files.push_back(dex); | |
| } | |
| VerifyClassLoaderDexFiles(soa.Self(), | |
| class_loader_1, | |
| WellKnownClasses::dalvik_system_PathClassLoader.Get(), | |
| class_loader_1_dex_files); | |
| // Verify its shared library. | |
| ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders; | |
| ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get()); | |
| ASSERT_TRUE(raw_shared_libraries != nullptr); | |
| Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries( | |
| hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); | |
| ASSERT_EQ(shared_libraries->GetLength(), 1); | |
| Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0)); | |
| std::vector<const DexFile*> class_loader_2_dex_files = | |
| MakeNonOwningPointerVector(classpath_dex_b); | |
| VerifyClassLoaderDexFiles(soa.Self(), | |
| class_loader_2, | |
| WellKnownClasses::dalvik_system_PathClassLoader.Get(), | |
| class_loader_2_dex_files); | |
| // Verify the shared library dependency of the shared library. | |
| raw_shared_libraries = field->GetObject(class_loader_2.Get()); | |
| ASSERT_TRUE(raw_shared_libraries != nullptr); | |
| Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_2( | |
| hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); | |
| ASSERT_EQ(shared_libraries_2->GetLength(), 1); | |
| Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(shared_libraries_2->Get(0)); | |
| std::vector<const DexFile*> class_loader_3_dex_files = | |
| MakeNonOwningPointerVector(classpath_dex_c); | |
| VerifyClassLoaderDexFiles(soa.Self(), | |
| class_loader_3, | |
| WellKnownClasses::dalvik_system_PathClassLoader.Get(), | |
| class_loader_3_dex_files); | |
| raw_shared_libraries = field->GetObject(class_loader_3.Get()); | |
| ASSERT_TRUE(raw_shared_libraries == nullptr); | |
| // Verify the parent. | |
| Handle<mirror::ClassLoader> class_loader_4 = hs.NewHandle(class_loader_1->GetParent()); | |
| std::vector<const DexFile*> class_loader_4_dex_files = | |
| MakeNonOwningPointerVector(classpath_dex_d); | |
| VerifyClassLoaderDexFiles(soa.Self(), | |
| class_loader_4, | |
| WellKnownClasses::dalvik_system_PathClassLoader.Get(), | |
| class_loader_4_dex_files); | |
| raw_shared_libraries = field->GetObject(class_loader_4.Get()); | |
| ASSERT_TRUE(raw_shared_libraries == nullptr); | |
| // Class loaders should have the BootClassLoader as a parent. | |
| ASSERT_TRUE(class_loader_2->GetParent()->GetClass() == | |
| WellKnownClasses::java_lang_BootClassLoader); | |
| ASSERT_TRUE(class_loader_3->GetParent()->GetClass() == | |
| WellKnownClasses::java_lang_BootClassLoader); | |
| ASSERT_TRUE(class_loader_4->GetParent()->GetClass() == | |
| WellKnownClasses::java_lang_BootClassLoader); | |
| } | |
| TEST_F(ClassLoaderContextTest, RemoveSourceLocations) { | |
| std::unique_ptr<ClassLoaderContext> context = | |
| ClassLoaderContext::Create("PCL[a.dex]"); | |
| dchecked_vector<std::string> classpath_dex; | |
| classpath_dex.push_back("a.dex"); | |
| dchecked_vector<std::string> compilation_sources; | |
| compilation_sources.push_back("src.dex"); | |
| // Nothing should be removed. | |
| ASSERT_FALSE(context->RemoveLocationsFromClassPaths(compilation_sources)); | |
| VerifyClassLoaderPCL(context.get(), 0, "a.dex"); | |
| // Classes should be removed. | |
| ASSERT_TRUE(context->RemoveLocationsFromClassPaths(classpath_dex)); | |
| VerifyClassLoaderPCL(context.get(), 0, ""); | |
| } | |
| TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSameSharedLibraries) { | |
| // 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::string context_spec = | |
| "PCL[" + CreateClassPath(classpath_dex_a) + "]{" + | |
| "PCL[" + CreateClassPath(classpath_dex_b) + "]};" + | |
| "PCL[" + CreateClassPath(classpath_dex_c) + "]{" + | |
| "PCL[" + CreateClassPath(classpath_dex_b) + "]}"; | |
| std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_spec); | |
| ASSERT_TRUE(context->OpenDexFiles()); | |
| // 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); | |
| // 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()); | |
| StackHandleScope<6> hs(soa.Self()); | |
| Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle( | |
| soa.Decode<mirror::ClassLoader>(jclass_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 : compilation_sources_raw) { | |
| class_loader_1_dex_files.push_back(dex); | |
| } | |
| VerifyClassLoaderDexFiles(soa.Self(), | |
| class_loader_1, | |
| WellKnownClasses::dalvik_system_PathClassLoader.Get(), | |
| class_loader_1_dex_files); | |
| // Verify its shared library. | |
| ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders; | |
| ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get()); | |
| ASSERT_TRUE(raw_shared_libraries != nullptr); | |
| Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries( | |
| hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); | |
| ASSERT_EQ(shared_libraries->GetLength(), 1); | |
| Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0)); | |
| std::vector<const DexFile*> class_loader_2_dex_files = | |
| MakeNonOwningPointerVector(classpath_dex_b); | |
| VerifyClassLoaderDexFiles(soa.Self(), | |
| class_loader_2, | |
| WellKnownClasses::dalvik_system_PathClassLoader.Get(), | |
| class_loader_2_dex_files); | |
| // Verify the parent. | |
| Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_1->GetParent()); | |
| std::vector<const DexFile*> class_loader_3_dex_files = | |
| MakeNonOwningPointerVector(classpath_dex_c); | |
| VerifyClassLoaderDexFiles(soa.Self(), | |
| class_loader_3, | |
| WellKnownClasses::dalvik_system_PathClassLoader.Get(), | |
| class_loader_3_dex_files); | |
| // Verify its shared library is the same as the child. | |
| raw_shared_libraries = field->GetObject(class_loader_3.Get()); | |
| ASSERT_TRUE(raw_shared_libraries != nullptr); | |
| Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_2( | |
| hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); | |
| ASSERT_EQ(shared_libraries_2->GetLength(), 1); | |
| ASSERT_OBJ_PTR_EQ(shared_libraries_2->Get(0), class_loader_2.Get()); | |
| // Class loaders should have the BootClassLoader as a parent. | |
| ASSERT_TRUE(class_loader_2->GetParent()->GetClass() == | |
| WellKnownClasses::java_lang_BootClassLoader); | |
| ASSERT_TRUE(class_loader_3->GetParent()->GetClass() == | |
| WellKnownClasses::java_lang_BootClassLoader); | |
| } | |
| TEST_F(ClassLoaderContextTest, EncodeInOatFile) { | |
| std::string dex1_name = GetTestDexFileName("Main"); | |
| std::string dex2_name = GetTestDexFileName("MyClass"); | |
| std::unique_ptr<ClassLoaderContext> context = | |
| ClassLoaderContext::Create("PCL[" + dex1_name + ":" + dex2_name + "]"); | |
| ASSERT_TRUE(context->OpenDexFiles()); | |
| 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[" + CreateClassPathWithChecksums(dex1) + ":" + | |
| CreateClassPathWithChecksums(dex2) + "]"; | |
| ASSERT_EQ(expected_encoding, context->EncodeContextForOatFile("")); | |
| } | |
| // Same as above, but passes `only_read_checksums=true` to `OpenDexFiles`. | |
| TEST_F(ClassLoaderContextTest, EncodeInOatFileOnlyReadChecksums) { | |
| std::string dex1_name = GetTestDexFileName("Main"); | |
| std::string dex2_name = GetTestDexFileName("MyClass"); | |
| std::unique_ptr<ClassLoaderContext> context = | |
| ClassLoaderContext::Create("PCL[" + dex1_name + ":" + dex2_name + "]"); | |
| ASSERT_TRUE(context->OpenDexFiles( | |
| /*classpath_dir=*/"", /*context_fds=*/{}, /*only_read_checksums=*/true)); | |
| 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[" + CreateClassPathWithChecksums(dex1) + ":" + CreateClassPathWithChecksums(dex2) + "]"; | |
| ASSERT_EQ(expected_encoding, context->EncodeContextForOatFile("")); | |
| } | |
| TEST_F(ClassLoaderContextTest, EncodeInOatFileIMC) { | |
| jobject class_loader_a = LoadDexInPathClassLoader("Main", nullptr); | |
| jobject class_loader_b = LoadDexInInMemoryDexClassLoader("MyClass", class_loader_a); | |
| std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_b); | |
| ASSERT_TRUE(context->OpenDexFiles()); | |
| std::vector<std::unique_ptr<const DexFile>> dex1 = OpenTestDexFiles("Main"); | |
| std::vector<std::unique_ptr<const DexFile>> dex2 = OpenTestDexFiles("MyClass"); | |
| ASSERT_EQ(dex2.size(), 1u); | |
| uint32_t expected_checksum = DexFileLoader::GetMultiDexChecksum(dex2); | |
| std::string encoding = context->EncodeContextForOatFile(""); | |
| std::string expected_encoding = "IMC[<unknown>*" + std::to_string(expected_checksum) + "];PCL[" + | |
| CreateClassPathWithChecksums(dex1) + "]"; | |
| ASSERT_EQ(expected_encoding, context->EncodeContextForOatFile("")); | |
| } | |
| TEST_F(ClassLoaderContextTest, EncodeForDex2oat) { | |
| std::string dex1_name = GetTestDexFileName("Main"); | |
| std::string dex2_name = GetTestDexFileName("MultiDex"); | |
| std::unique_ptr<ClassLoaderContext> context = | |
| ClassLoaderContext::Create("PCL[" + dex1_name + ":" + dex2_name + "]"); | |
| ASSERT_TRUE(context->OpenDexFiles()); | |
| std::string encoding = context->EncodeContextForDex2oat(""); | |
| std::string expected_encoding = "PCL[" + dex1_name + ":" + dex2_name + "]"; | |
| ASSERT_EQ(expected_encoding, context->EncodeContextForDex2oat("")); | |
| } | |
| TEST_F(ClassLoaderContextTest, EncodeForDex2oatIMC) { | |
| jobject class_loader_a = LoadDexInPathClassLoader("Main", nullptr); | |
| jobject class_loader_b = LoadDexInInMemoryDexClassLoader("MyClass", class_loader_a); | |
| std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_b); | |
| ASSERT_TRUE(context->OpenDexFiles()); | |
| std::string encoding = context->EncodeContextForDex2oat(""); | |
| std::string expected_encoding = "IMC[<unknown>];PCL[" + GetTestDexFileName("Main") + "]"; | |
| ASSERT_EQ(expected_encoding, context->EncodeContextForDex2oat("")); | |
| } | |
| TEST_F(ClassLoaderContextTest, EncodeForDex2oatDuplicates) { | |
| std::string dex_name = GetTestDexFileName("Main"); | |
| std::unique_ptr<ClassLoaderContext> context = | |
| ClassLoaderContext::Create("PCL[" + dex_name + ":" + dex_name + "]"); | |
| ASSERT_TRUE(context->OpenDexFiles()); | |
| std::string encoding = context->EncodeContextForDex2oat(""); | |
| std::string expected_encoding = "PCL[" + dex_name + "]"; | |
| ASSERT_EQ(expected_encoding, context->EncodeContextForDex2oat("")); | |
| } | |
| TEST_F(ClassLoaderContextTest, EncodeContextsSinglePath) { | |
| jobject class_loader = LoadDexInPathClassLoader("Main", nullptr); | |
| std::unique_ptr<ClassLoaderContext> context = | |
| CreateContextForClassLoader(class_loader); | |
| std::map<std::string, std::string> encodings = context->EncodeClassPathContexts(""); | |
| ASSERT_EQ(1u, encodings.size()); | |
| ASSERT_EQ("PCL[]", encodings.at(GetTestDexFileName("Main"))); | |
| } | |
| TEST_F(ClassLoaderContextTest, EncodeContextsMultiDex) { | |
| jobject class_loader = LoadDexInPathClassLoader("MultiDex", nullptr); | |
| std::unique_ptr<ClassLoaderContext> context = | |
| CreateContextForClassLoader(class_loader); | |
| std::map<std::string, std::string> encodings = context->EncodeClassPathContexts(""); | |
| ASSERT_EQ(1u, encodings.size()); | |
| ASSERT_EQ("PCL[]", encodings.at(GetTestDexFileName("MultiDex"))); | |
| } | |
| TEST_F(ClassLoaderContextTest, EncodeContextsRepeatedMultiDex) { | |
| jobject top_class_loader = LoadDexInPathClassLoader("MultiDex", nullptr); | |
| jobject middle_class_loader = | |
| LoadDexInPathClassLoader("Main", top_class_loader); | |
| jobject bottom_class_loader = | |
| LoadDexInPathClassLoader("MultiDex", middle_class_loader); | |
| std::unique_ptr<ClassLoaderContext> context = | |
| CreateContextForClassLoader(bottom_class_loader); | |
| std::map<std::string, std::string> encodings = context->EncodeClassPathContexts(""); | |
| ASSERT_EQ(1u, encodings.size()); | |
| std::string main_dex_name = GetTestDexFileName("Main"); | |
| std::string multidex_dex_name = GetTestDexFileName("MultiDex"); | |
| ASSERT_EQ( | |
| "PCL[];PCL[" + main_dex_name + "];PCL[" + multidex_dex_name + "]", | |
| encodings.at(multidex_dex_name)); | |
| } | |
| TEST_F(ClassLoaderContextTest, EncodeContextsSinglePathWithShared) { | |
| jobject class_loader_a = LoadDexInPathClassLoader("MyClass", nullptr); | |
| ScopedObjectAccess soa(Thread::Current()); | |
| StackHandleScope<1> hs(soa.Self()); | |
| Handle<mirror::ObjectArray<mirror::ClassLoader>> libraries = hs.NewHandle( | |
| mirror::ObjectArray<mirror::ClassLoader>::Alloc( | |
| soa.Self(), | |
| GetClassRoot<mirror::ObjectArray<mirror::ClassLoader>>(), | |
| 1)); | |
| libraries->Set(0, soa.Decode<mirror::ClassLoader>(class_loader_a)); | |
| jobject class_loader_b = LoadDexInPathClassLoader( | |
| "Main", nullptr, soa.AddLocalReference<jobject>(libraries.Get())); | |
| std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_b); | |
| std::map<std::string, std::string> encodings = context->EncodeClassPathContexts(""); | |
| ASSERT_EQ(1u, encodings.size()); | |
| ASSERT_EQ( | |
| "PCL[]{PCL[" + GetTestDexFileName("MyClass") + "]}", | |
| encodings.at(GetTestDexFileName("Main"))); | |
| } | |
| TEST_F(ClassLoaderContextTest, EncodeContextsMultiplePaths) { | |
| jobject class_loader = LoadDexInPathClassLoader( | |
| std::vector<std::string>{ "Main", "MultiDex"}, nullptr); | |
| std::unique_ptr<ClassLoaderContext> context = | |
| CreateContextForClassLoader(class_loader); | |
| std::map<std::string, std::string> encodings = context->EncodeClassPathContexts(""); | |
| ASSERT_EQ(2u, encodings.size()); | |
| ASSERT_EQ("PCL[]", encodings.at(GetTestDexFileName("Main"))); | |
| ASSERT_EQ( | |
| "PCL[" + GetTestDexFileName("Main") + "]", encodings.at(GetTestDexFileName("MultiDex"))); | |
| } | |
| TEST_F(ClassLoaderContextTest, EncodeContextsMultiplePathsWithShared) { | |
| jobject class_loader_a = LoadDexInPathClassLoader("MyClass", nullptr); | |
| ScopedObjectAccess soa(Thread::Current()); | |
| StackHandleScope<1> hs(soa.Self()); | |
| Handle<mirror::ObjectArray<mirror::ClassLoader>> libraries = hs.NewHandle( | |
| mirror::ObjectArray<mirror::ClassLoader>::Alloc( | |
| soa.Self(), | |
| GetClassRoot<mirror::ObjectArray<mirror::ClassLoader>>(), | |
| 1)); | |
| libraries->Set(0, soa.Decode<mirror::ClassLoader>(class_loader_a)); | |
| jobject class_loader_b = LoadDexInPathClassLoader( | |
| std::vector<std::string> { "Main", "MultiDex" }, | |
| nullptr, soa.AddLocalReference<jobject>(libraries.Get())); | |
| std::unique_ptr<ClassLoaderContext> context = | |
| CreateContextForClassLoader(class_loader_b); | |
| std::map<std::string, std::string> encodings = context->EncodeClassPathContexts(""); | |
| ASSERT_EQ(2u, encodings.size()); | |
| const std::string context_suffix = | |
| "{PCL[" + GetTestDexFileName("MyClass") + "]}"; | |
| ASSERT_EQ("PCL[]" + context_suffix, encodings.at(GetTestDexFileName("Main"))); | |
| ASSERT_EQ( | |
| "PCL[" + GetTestDexFileName("Main") + "]" + context_suffix, | |
| encodings.at(GetTestDexFileName("MultiDex"))); | |
| } | |
| TEST_F(ClassLoaderContextTest, EncodeContextsIMC) { | |
| jobject class_loader_a = LoadDexInPathClassLoader("Main", nullptr); | |
| jobject class_loader_b = | |
| LoadDexInInMemoryDexClassLoader("MyClass", class_loader_a); | |
| std::unique_ptr<ClassLoaderContext> context = | |
| CreateContextForClassLoader(class_loader_b); | |
| std::map<std::string, std::string> encodings = context->EncodeClassPathContexts(""); | |
| ASSERT_EQ(1u, encodings.size()); | |
| ASSERT_EQ( | |
| "IMC[];PCL[" + GetTestDexFileName("Main") + "]", | |
| encodings.at("<unknown>")); | |
| } | |
| TEST_F(ClassLoaderContextTest, EncodeContextsForSingleDex) { | |
| jobject class_loader = LoadDexInPathClassLoader("Main", nullptr); | |
| std::map<std::string, std::string> encodings = | |
| ClassLoaderContext::EncodeClassPathContextsForClassLoader(class_loader); | |
| ASSERT_EQ(1u, encodings.size()); | |
| ASSERT_EQ("PCL[]", encodings.at(GetTestDexFileName("Main"))); | |
| } | |
| static jobject CreateForeignClassLoader() { | |
| ScopedObjectAccess soa(Thread::Current()); | |
| // We cannot instantiate a ClassLoader directly, so instead we allocate an Object to represent | |
| // our foreign ClassLoader (this works because the runtime does proper instanceof checks before | |
| // operating on this object. | |
| ArtMethod* ctor = | |
| GetClassRoot<mirror::Object>()->FindClassMethod("<init>", "()V", kRuntimePointerSize); | |
| CHECK(ctor != nullptr); | |
| return soa.AddLocalReference<jobject>(ctor->NewObject<>(soa.Self())); | |
| } | |
| TEST_F(ClassLoaderContextTest, EncodeContextsForUnsupportedBase) { | |
| std::map<std::string, std::string> empty; | |
| ASSERT_EQ( | |
| empty, ClassLoaderContext::EncodeClassPathContextsForClassLoader(CreateForeignClassLoader())); | |
| } | |
| TEST_F(ClassLoaderContextTest, EncodeContextsForUnsupportedChain) { | |
| jobject class_loader = LoadDexInPathClassLoader("Main", CreateForeignClassLoader()); | |
| std::map<std::string, std::string> encodings = | |
| ClassLoaderContext::EncodeClassPathContextsForClassLoader(class_loader); | |
| ASSERT_EQ(1u, encodings.size()); | |
| ASSERT_EQ( | |
| ClassLoaderContext::kUnsupportedClassLoaderContextEncoding, | |
| encodings.at(GetTestDexFileName("Main"))); | |
| } | |
| TEST_F(ClassLoaderContextTest, EncodeContextsForUnsupportedChainMultiPath) { | |
| jobject class_loader = LoadDexInPathClassLoader(std::vector<std::string> { "Main", "MyClass" }, | |
| CreateForeignClassLoader()); | |
| std::map<std::string, std::string> encodings = | |
| ClassLoaderContext::EncodeClassPathContextsForClassLoader(class_loader); | |
| ASSERT_EQ(2u, encodings.size()); | |
| ASSERT_EQ( | |
| ClassLoaderContext::kUnsupportedClassLoaderContextEncoding, | |
| encodings.at(GetTestDexFileName("Main"))); | |
| ASSERT_EQ( | |
| ClassLoaderContext::kUnsupportedClassLoaderContextEncoding, | |
| encodings.at(GetTestDexFileName("MyClass"))); | |
| } | |
| TEST_F(ClassLoaderContextTest, EncodeContextsForUnsupportedChainMultiDex) { | |
| jobject class_loader = LoadDexInPathClassLoader("MultiDex", CreateForeignClassLoader()); | |
| std::map<std::string, std::string> encodings = | |
| ClassLoaderContext::EncodeClassPathContextsForClassLoader(class_loader); | |
| ASSERT_EQ(1u, encodings.size()); | |
| ASSERT_EQ( | |
| ClassLoaderContext::kUnsupportedClassLoaderContextEncoding, | |
| encodings.at(GetTestDexFileName("MultiDex"))); | |
| } | |
| TEST_F(ClassLoaderContextTest, IsValidEncoding) { | |
| ASSERT_TRUE(ClassLoaderContext::IsValidEncoding("PCL[]")); | |
| ASSERT_TRUE(ClassLoaderContext::IsValidEncoding("PCL[foo.dex]")); | |
| ASSERT_TRUE(ClassLoaderContext::IsValidEncoding("PCL[foo.dex];PCL[bar.dex]")); | |
| ASSERT_TRUE(ClassLoaderContext::IsValidEncoding("DLC[];PCL[bar.dex]")); | |
| ASSERT_TRUE( | |
| ClassLoaderContext::IsValidEncoding( | |
| ClassLoaderContext::kUnsupportedClassLoaderContextEncoding)); | |
| ASSERT_FALSE(ClassLoaderContext::IsValidEncoding("not_valid")); | |
| ASSERT_FALSE(ClassLoaderContext::IsValidEncoding("[]")); | |
| ASSERT_FALSE(ClassLoaderContext::IsValidEncoding("FCL[]")); | |
| ASSERT_FALSE(ClassLoaderContext::IsValidEncoding("foo.dex:bar.dex")); | |
| } | |
| // TODO(calin) add a test which creates the context for a class loader together with dex_elements. | |
| TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) { | |
| // The chain is | |
| // ClassLoaderA (PathClassLoader) | |
| // ^ | |
| // | | |
| // ClassLoaderB (DelegateLastClassLoader) | |
| // ^ | |
| // | | |
| // ClassLoaderC (PathClassLoader) | |
| // ^ | |
| // | | |
| // ClassLoaderD (DelegateLastClassLoader) | |
| jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr); | |
| jobject class_loader_b = LoadDexInDelegateLastClassLoader("ForClassLoaderB", class_loader_a); | |
| jobject class_loader_c = LoadDexInPathClassLoader("ForClassLoaderC", class_loader_b); | |
| jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c); | |
| std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d); | |
| VerifyContextForClassLoader(context.get()); | |
| VerifyContextSize(context.get(), 4); | |
| VerifyClassLoaderDLCFromTestDex(context.get(), 0, "ForClassLoaderD"); | |
| VerifyClassLoaderPCLFromTestDex(context.get(), 1, "ForClassLoaderC"); | |
| VerifyClassLoaderDLCFromTestDex(context.get(), 2, "ForClassLoaderB"); | |
| VerifyClassLoaderPCLFromTestDex(context.get(), 3, "ForClassLoaderA"); | |
| } | |
| TEST_F(ClassLoaderContextTest, CreateContextForClassLoaderIMC) { | |
| // The chain is | |
| // ClassLoaderA (PathClassLoader) | |
| // ^ | |
| // | | |
| // ClassLoaderB (InMemoryDexClassLoader) | |
| // ^ | |
| // | | |
| // ClassLoaderC (InMemoryDexClassLoader) | |
| // ^ | |
| // | | |
| // ClassLoaderD (DelegateLastClassLoader) | |
| jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr); | |
| jobject class_loader_b = LoadDexInInMemoryDexClassLoader("ForClassLoaderB", class_loader_a); | |
| jobject class_loader_c = LoadDexInInMemoryDexClassLoader("ForClassLoaderC", class_loader_b); | |
| jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c); | |
| std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d); | |
| VerifyContextForClassLoader(context.get()); | |
| VerifyContextSize(context.get(), 4); | |
| VerifyClassLoaderDLCFromTestDex(context.get(), 0, "ForClassLoaderD"); | |
| VerifyClassLoaderIMCFromTestDex(context.get(), 1, "ForClassLoaderC"); | |
| VerifyClassLoaderIMCFromTestDex(context.get(), 2, "ForClassLoaderB"); | |
| VerifyClassLoaderPCLFromTestDex(context.get(), 3, "ForClassLoaderA"); | |
| } | |
| TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) { | |
| std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890]"; | |
| std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec); | |
| // Pretend that we successfully open the dex files to pass the DCHECKS. | |
| // (as it's much easier to test all the corner cases without relying on actual dex files). | |
| PretendContextOpenedDexFilesForChecksums(context.get()); | |
| VerifyContextSize(context.get(), 2); | |
| VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex"); | |
| VerifyClassLoaderDLC(context.get(), 1, "c.dex"); | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec), | |
| ClassLoaderContext::VerificationResult::kVerifies); | |
| std::string wrong_class_loader_type = "PCL[a.dex*123:b.dex*456];PCL[c.dex*890]"; | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_type), | |
| ClassLoaderContext::VerificationResult::kMismatch); | |
| std::string wrong_class_loader_order = "DLC[c.dex*890];PCL[a.dex*123:b.dex*456]"; | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_order), | |
| ClassLoaderContext::VerificationResult::kMismatch); | |
| std::string wrong_classpath_order = "PCL[b.dex*456:a.dex*123];DLC[c.dex*890]"; | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_classpath_order), | |
| ClassLoaderContext::VerificationResult::kMismatch); | |
| std::string wrong_checksum = "PCL[a.dex*999:b.dex*456];DLC[c.dex*890]"; | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_checksum), | |
| ClassLoaderContext::VerificationResult::kMismatch); | |
| std::string wrong_extra_class_loader = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890];PCL[d.dex*321]"; | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader), | |
| ClassLoaderContext::VerificationResult::kMismatch); | |
| std::string wrong_extra_classpath = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890:d.dex*321]"; | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_classpath), | |
| ClassLoaderContext::VerificationResult::kMismatch); | |
| std::string wrong_spec = "PCL[a.dex*999:b.dex*456];DLC["; | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_spec), | |
| ClassLoaderContext::VerificationResult::kMismatch); | |
| } | |
| TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextWithIMCMatch) { | |
| std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890];IMC[<unknown>*111]"; | |
| std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec); | |
| // Pretend that we successfully open the dex files to pass the DCHECKS. | |
| // (as it's much easier to test all the corner cases without relying on actual dex files). | |
| PretendContextOpenedDexFiles(context.get()); | |
| VerifyContextSize(context.get(), 3); | |
| VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex"); | |
| VerifyClassLoaderDLC(context.get(), 1, "c.dex"); | |
| VerifyClassLoaderIMC(context.get(), 2, "<unknown>"); | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec), | |
| ClassLoaderContext::VerificationResult::kVerifies); | |
| } | |
| TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchWithSL) { | |
| std::string context_spec = | |
| "PCL[a.dex*123:b.dex*456]{PCL[d.dex*321];PCL[e.dex*654]#PCL[f.dex*098:g.dex*999]}" | |
| ";DLC[c.dex*890]"; | |
| std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec); | |
| // Pretend that we successfully open the dex files to pass the DCHECKS. | |
| // (as it's much easier to test all the corner cases without relying on actual dex files). | |
| PretendContextOpenedDexFiles(context.get()); | |
| VerifyContextSize(context.get(), 2); | |
| VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex"); | |
| VerifyClassLoaderDLC(context.get(), 1, "c.dex"); | |
| VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "d.dex"); | |
| VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 1, "f.dex:g.dex"); | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec), | |
| ClassLoaderContext::VerificationResult::kVerifies); | |
| std::string wrong_class_loader_type = | |
| "PCL[a.dex*123:b.dex*456]{DLC[d.dex*321];PCL[e.dex*654]#PCL[f.dex*098:g.dex*999]}" | |
| ";DLC[c.dex*890]"; | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_type), | |
| ClassLoaderContext::VerificationResult::kMismatch); | |
| std::string wrong_class_loader_order = | |
| "PCL[a.dex*123:b.dex*456]{PCL[f.dex#098:g.dex#999}#PCL[d.dex*321];PCL[e.dex*654]}" | |
| ";DLC[c.dex*890]"; | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_order), | |
| ClassLoaderContext::VerificationResult::kMismatch); | |
| std::string wrong_classpath_order = | |
| "PCL[a.dex*123:b.dex*456]{PCL[d.dex*321];PCL[e.dex*654]#PCL[g.dex*999:f.dex*098]}" | |
| ";DLC[c.dex*890]"; | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_classpath_order), | |
| ClassLoaderContext::VerificationResult::kMismatch); | |
| std::string wrong_checksum = | |
| "PCL[a.dex*123:b.dex*456]{PCL[d.dex*333];PCL[e.dex*654]#PCL[g.dex*999:f.dex*098]}" | |
| ";DLC[c.dex*890]"; | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_checksum), | |
| ClassLoaderContext::VerificationResult::kMismatch); | |
| std::string wrong_extra_class_loader = | |
| "PCL[a.dex*123:b.dex*456]" | |
| "{PCL[d.dex*321];PCL[e.dex*654]#PCL[f.dex*098:g.dex*999];PCL[i.dex#444]}" | |
| ";DLC[c.dex*890]"; | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader), | |
| ClassLoaderContext::VerificationResult::kMismatch); | |
| std::string wrong_extra_classpath = | |
| "PCL[a.dex*123:b.dex*456]{PCL[d.dex*321:i.dex#444];PCL[e.dex*654]#PCL[f.dex*098:g.dex*999]}" | |
| ";DLC[c.dex*890]"; | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_classpath), | |
| ClassLoaderContext::VerificationResult::kMismatch); | |
| } | |
| TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchWithIMCSL) { | |
| std::string context_spec = | |
| "IMC[<unknown>*123:<unknown>*456]" | |
| "{IMC[<unknown>*321];IMC[<unknown>*654]#IMC[<unknown>*098:<unknown>*999]};" | |
| "DLC[c.dex*890]"; | |
| std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec); | |
| // Pretend that we successfully open the dex files to pass the DCHECKS. | |
| // (as it's much easier to test all the corner cases without relying on actual dex files). | |
| PretendContextOpenedDexFiles(context.get()); | |
| VerifyContextSize(context.get(), 2); | |
| VerifyClassLoaderIMC(context.get(), 0, "<unknown>:<unknown>"); | |
| VerifyClassLoaderDLC(context.get(), 1, "c.dex"); | |
| VerifyClassLoaderSharedLibraryIMC(context.get(), 0, 0, "<unknown>"); | |
| VerifyClassLoaderSharedLibraryIMC(context.get(), 0, 1, "<unknown>:<unknown>"); | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec), | |
| ClassLoaderContext::VerificationResult::kVerifies); | |
| } | |
| TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterResolvingSymlinks) { | |
| { | |
| std::ofstream ofs(scratch_path_ + "/foo.jar"); | |
| ASSERT_TRUE(ofs); | |
| } | |
| std::filesystem::create_directory_symlink(scratch_path_, scratch_path_ + "/bar"); | |
| std::string context_spec = | |
| android::base::StringPrintf("PCL[%s/foo.jar*123:%s/foo.jar!classes2.dex*456]", | |
| scratch_path_.c_str(), | |
| scratch_path_.c_str()); | |
| std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec); | |
| PretendContextOpenedDexFilesForChecksums(context.get()); | |
| std::string context_spec_with_symlinks = | |
| android::base::StringPrintf("PCL[%s/bar/foo.jar*123:%s/bar/foo.jar!classes2.dex*456]", | |
| scratch_path_.c_str(), | |
| scratch_path_.c_str()); | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec_with_symlinks), | |
| ClassLoaderContext::VerificationResult::kVerifies); | |
| } | |
| TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) { | |
| jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr); | |
| jobject class_loader_b = LoadDexInDelegateLastClassLoader("ForClassLoaderB", class_loader_a); | |
| jobject class_loader_c = LoadDexInPathClassLoader("ForClassLoaderC", class_loader_b); | |
| jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c); | |
| std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d); | |
| std::string context_with_no_base_dir = context->EncodeContextForOatFile(""); | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_no_base_dir), | |
| ClassLoaderContext::VerificationResult::kVerifies); | |
| std::string dex_location = GetTestDexFileName("ForClassLoaderA"); | |
| size_t pos = dex_location.rfind('/'); | |
| ASSERT_NE(std::string::npos, pos); | |
| std::string parent = dex_location.substr(0, pos); | |
| std::string context_with_base_dir = context->EncodeContextForOatFile(parent); | |
| ASSERT_NE(context_with_base_dir, context_with_no_base_dir); | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_base_dir), | |
| ClassLoaderContext::VerificationResult::kVerifies); | |
| } | |
| TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingIMC) { | |
| jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr); | |
| jobject class_loader_b = LoadDexInInMemoryDexClassLoader("ForClassLoaderB", class_loader_a); | |
| jobject class_loader_c = LoadDexInInMemoryDexClassLoader("ForClassLoaderC", class_loader_b); | |
| jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c); | |
| std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d); | |
| std::string context_with_no_base_dir = context->EncodeContextForOatFile(""); | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_no_base_dir), | |
| ClassLoaderContext::VerificationResult::kVerifies); | |
| std::string dex_location = GetTestDexFileName("ForClassLoaderA"); | |
| size_t pos = dex_location.rfind('/'); | |
| ASSERT_NE(std::string::npos, pos); | |
| std::string parent = dex_location.substr(0, pos); | |
| std::string context_with_base_dir = context->EncodeContextForOatFile(parent); | |
| ASSERT_NE(context_with_base_dir, context_with_no_base_dir); | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_base_dir), | |
| ClassLoaderContext::VerificationResult::kVerifies); | |
| } | |
| TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) { | |
| jobject class_loader = LoadDexInPathClassLoader("MultiDex", nullptr); | |
| std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader); | |
| std::string context_with_no_base_dir = context->EncodeContextForOatFile(""); | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_no_base_dir), | |
| ClassLoaderContext::VerificationResult::kVerifies); | |
| std::string dex_location = GetTestDexFileName("MultiDex"); | |
| size_t pos = dex_location.rfind('/'); | |
| ASSERT_NE(std::string::npos, pos); | |
| std::string parent = dex_location.substr(0, pos); | |
| std::string context_with_base_dir = context->EncodeContextForOatFile(parent); | |
| ASSERT_NE(context_with_base_dir, context_with_no_base_dir); | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_base_dir), | |
| ClassLoaderContext::VerificationResult::kVerifies); | |
| } | |
| TEST_F(ClassLoaderContextTest, CreateContextForClassLoaderWithSharedLibraries) { | |
| jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr); | |
| ScopedObjectAccess soa(Thread::Current()); | |
| StackHandleScope<1> hs(soa.Self()); | |
| Handle<mirror::ObjectArray<mirror::ClassLoader>> libraries = hs.NewHandle( | |
| mirror::ObjectArray<mirror::ClassLoader>::Alloc( | |
| soa.Self(), | |
| GetClassRoot<mirror::ObjectArray<mirror::ClassLoader>>(), | |
| 1)); | |
| libraries->Set(0, soa.Decode<mirror::ClassLoader>(class_loader_a)); | |
| jobject class_loader_b = LoadDexInPathClassLoader( | |
| "ForClassLoaderB", nullptr, soa.AddLocalReference<jobject>(libraries.Get())); | |
| std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_b); | |
| ASSERT_TRUE(context != nullptr); | |
| std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ForClassLoaderB"); | |
| VerifyClassLoaderPCL(context.get(), 0, dex_files[0]->GetLocation()); | |
| dex_files = OpenTestDexFiles("ForClassLoaderA"); | |
| VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, dex_files[0]->GetLocation()); | |
| ASSERT_EQ(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")), | |
| ClassLoaderContext::VerificationResult::kVerifies); | |
| } | |
| TEST_F(ClassLoaderContextTest, CheckForDuplicateDexFilesNotFoundSingleCL) { | |
| jobject class_loader = LoadDexInPathClassLoader("Main", nullptr); | |
| std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader); | |
| std::set<const DexFile*> result = context->CheckForDuplicateDexFiles( | |
| std::vector<const DexFile*>()); | |
| ASSERT_EQ(0u, result.size()); | |
| std::vector<std::unique_ptr<const DexFile>> dex1 = OpenTestDexFiles("ForClassLoaderA"); | |
| std::vector<const DexFile*> dex1_raw = MakeNonOwningPointerVector(dex1); | |
| result = context->CheckForDuplicateDexFiles(dex1_raw); | |
| ASSERT_EQ(0u, result.size()); | |
| } | |
| TEST_F(ClassLoaderContextTest, CheckForDuplicateDexFilesFound) { | |
| jobject class_loader = LoadDexInPathClassLoader(std::vector<std::string> { "Main", "Main" }, nullptr); | |
| std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader); | |
| std::vector<std::unique_ptr<const DexFile>> dex1 = OpenTestDexFiles("Main"); | |
| std::vector<const DexFile*> dex1_raw = MakeNonOwningPointerVector(dex1); | |
| std::set<const DexFile*> result = context->CheckForDuplicateDexFiles(dex1_raw); | |
| ASSERT_EQ(1u, result.size()) << context->EncodeContextForOatFile(""); | |
| ASSERT_EQ(dex1_raw[0], *(result.begin())); | |
| } | |
| TEST_F(ClassLoaderContextTest, CheckForDuplicateCrossCLNotFound) { | |
| jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr); | |
| jobject class_loader_b = LoadDexInInMemoryDexClassLoader("ForClassLoaderB", class_loader_a); | |
| std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_b); | |
| std::vector<std::unique_ptr<const DexFile>> dex1 = OpenTestDexFiles("ForClassLoaderA"); | |
| std::vector<const DexFile*> dex1_raw = MakeNonOwningPointerVector(dex1); | |
| std::set<const DexFile*> result = context->CheckForDuplicateDexFiles(dex1_raw); | |
| ASSERT_EQ(0u, result.size()); | |
| } | |
| } // namespace art |