/* * 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 #include "android-base/strings.h" #include "art_field-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_file_assistant.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "thread.h" #include "well_known_classes.h" namespace art { class ClassLoaderContextTest : public CommonRuntimeTest { public: 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 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, ""); } 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>* 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); ASSERT_EQ(all_dex_files->size(), info.classpath.size()); ASSERT_EQ(all_dex_files->size(), info.checksums.size()); if (only_read_checksums) { ASSERT_EQ(0u, info.opened_dex_files.size()); } else { ASSERT_EQ(all_dex_files->size(), info.opened_dex_files.size()); } for (size_t k = 0, cur_open_dex_index = 0; k < all_dex_files->size(); k++, cur_open_dex_index++) { const std::string& opened_location = only_read_checksums ? info.classpath[cur_open_dex_index] : info.opened_dex_files[cur_open_dex_index]->GetLocation(); uint32_t opened_checksum = only_read_checksums ? info.checksums[cur_open_dex_index] : info.opened_dex_files[cur_open_dex_index]->GetLocationChecksum(); std::unique_ptr& 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_EQ(0, expected_location.compare( expected_location.length() - opened_location.length(), opened_location.length(), 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 CreateContextForClassLoader(jobject class_loader) { return ClassLoaderContext::CreateContextForClassLoader(class_loader, nullptr); } std::unique_ptr ParseContextWithChecksums(const std::string& context_spec) { std::unique_ptr 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(ScopedObjectAccess& soa, Handle class_loader, jclass type, std::vector& expected_dex_files) REQUIRES_SHARED(Locks::mutator_lock_) { ASSERT_TRUE(class_loader->GetClass() == soa.Decode(type)); std::vector 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()); } } 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 context = ClassLoaderContext::Create( "PCL[" + multidex_name + ":" + myclass_dex_name + "];" + "DLC[" + dex_name + "]"); ASSERT_TRUE(context->OpenDexFiles( /*classpath_dir=*/ "", /*context_fds=*/ std::vector(), only_read_checksums)); VerifyContextSize(context.get(), 2); std::vector> all_dex_files0 = OpenTestDexFiles("MultiDex"); std::vector> 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> 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 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(), only_read_checksums)); VerifyContextSize(context.get(), 2); std::vector> all_dex_files0 = OpenTestDexFiles("MultiDex"); std::vector> 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> 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 (!android::base::StartsWith(in, 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; } 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 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, info.type); std::vector 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> 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 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 context = ClassLoaderContext::Create("&"); ASSERT_TRUE(context == nullptr); } TEST_F(ClassLoaderContextTest, ParseValidContextPCL) { std::unique_ptr context = ClassLoaderContext::Create("PCL[a.dex]"); VerifyContextSize(context.get(), 1); VerifyClassLoaderPCL(context.get(), 0, "a.dex"); } TEST_F(ClassLoaderContextTest, ParseValidContextDLC) { std::unique_ptr context = ClassLoaderContext::Create("DLC[a.dex]"); VerifyContextSize(context.get(), 1); VerifyClassLoaderDLC(context.get(), 0, "a.dex"); } TEST_F(ClassLoaderContextTest, ParseValidContextIMC) { std::unique_ptr context = ParseContextWithChecksums("IMC[*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 context = ClassLoaderContext::Create("IMC[]"); 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 context = ClassLoaderContext::Create("IMC[a.dex*111]"); ASSERT_TRUE(context == nullptr); } TEST_F(ClassLoaderContextTest, ParseValidContextChain) { std::unique_ptr 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 context = ClassLoaderContext::Create( "PCL[a.dex:b.dex]{PCL[s1.dex]#PCL[s2.dex:s3.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"); VerifyClassLoaderDLC(context.get(), 1, "c.dex:d.dex"); VerifyClassLoaderSharedLibraryDLC(context.get(), 1, 0, "s4.dex"); } TEST_F(ClassLoaderContextTest, ParseEnclosingSharedLibraries) { std::unique_ptr 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 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 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 context = ClassLoaderContext::Create("DLC[]"); VerifyContextSize(context.get(), 1); VerifyClassLoaderDLC(context.get(), 0, ""); } TEST_F(ClassLoaderContextTest, ParseValidEmptyContextSharedLibrary) { std::unique_ptr 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 context = ClassLoaderContext::Create("PCL[does_not_exist.dex]"); VerifyContextSize(context.get(), 1); ASSERT_FALSE(context->OpenDexFiles(".")); } TEST_F(ClassLoaderContextTest, ReadChecksumsInvalidDexFiles) { std::unique_ptr context = ClassLoaderContext::Create("PCL[does_not_exist.dex]"); VerifyContextSize(context.get(), 1); ASSERT_FALSE(context->OpenDexFiles( /*classpath_dir=*/ ".", /*context_fds=*/ std::vector(), /*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 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 context = ClassLoaderContext::Create("PCL[does_not_exist.dex];DLC[" + dex_name + "]"); ASSERT_FALSE(context->OpenDexFiles( /*classpath_dir=*/ "", /*context_fds=*/ std::vector(), /*only_read_checksums=*/ true)); } TEST_F(ClassLoaderContextTest, OpenDexFilesForIMCFails) { std::unique_ptr context; std::string dex_name = GetTestDexFileName("Main"); context = ParseContextWithChecksums("IMC[*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 context = ClassLoaderContext::Create("PCL[" + dex_name + "]"); std::vector> all_dex_files0 = OpenTestDexFiles("Main"); ASSERT_TRUE(context->OpenDexFiles( /*classpath_dir=*/ "", /*context_fds=*/ std::vector(), /*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(), /*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 context = ClassLoaderContext::Create("PCL[" + dex_name + "]"); ASSERT_TRUE(context->OpenDexFiles()); std::vector> classpath_dex = OpenTestDexFiles("Main"); std::vector> compilation_sources = OpenTestDexFiles("MultiDex"); std::vector 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 class_loader = hs.NewHandle( soa.Decode(jclass_loader)); ASSERT_TRUE(class_loader->GetClass() == soa.Decode(WellKnownClasses::dalvik_system_PathClassLoader)); ASSERT_TRUE(class_loader->GetParent()->GetClass() == soa.Decode(WellKnownClasses::java_lang_BootClassLoader)); // For the first class loader the class path dex files must come first and then the // compilation sources. std::vector expected_classpath = MakeNonOwningPointerVector(classpath_dex); for (auto& dex : compilation_sources_raw) { expected_classpath.push_back(dex); } VerifyClassLoaderDexFiles(soa, class_loader, WellKnownClasses::dalvik_system_PathClassLoader, expected_classpath); } TEST_F(ClassLoaderContextTest, CreateClassLoaderWithEmptyContext) { std::unique_ptr context = ClassLoaderContext::Create(""); ASSERT_TRUE(context->OpenDexFiles()); std::vector> compilation_sources = OpenTestDexFiles("MultiDex"); std::vector 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 class_loader = hs.NewHandle( soa.Decode(jclass_loader)); // 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(WellKnownClasses::java_lang_BootClassLoader)); } TEST_F(ClassLoaderContextTest, CreateClassLoaderWithComplexChain) { // Setup the context. std::vector> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA"); std::vector> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB"); std::vector> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC"); std::vector> 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 context = ClassLoaderContext::Create(context_spec); ASSERT_TRUE(context->OpenDexFiles()); // Setup the compilation sources. std::vector> compilation_sources = OpenTestDexFiles("MultiDex"); std::vector 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 class_loader_1 = hs.NewHandle( soa.Decode(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 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 class_loader_2 = hs.NewHandle(class_loader_1->GetParent()); std::vector 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 class_loader_3 = hs.NewHandle(class_loader_2->GetParent()); std::vector 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(WellKnownClasses::java_lang_BootClassLoader)); } TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibraries) { // Setup the context. std::vector> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA"); std::vector> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB"); std::vector> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC"); std::vector> 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 context = ClassLoaderContext::Create(context_spec); ASSERT_TRUE(context->OpenDexFiles()); // Setup the compilation sources. std::vector> compilation_sources = OpenTestDexFiles("MultiDex"); std::vector 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 class_loader_1 = hs.NewHandle( soa.Decode(jclass_loader)); // For the first class loader the class path dex files must come first and then the // compilation sources. std::vector 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 shared libraries. ArtField* field = jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); ObjPtr raw_shared_libraries = field->GetObject(class_loader_1.Get()); ASSERT_TRUE(raw_shared_libraries != nullptr); Handle> shared_libraries( hs.NewHandle(raw_shared_libraries->AsObjectArray())); ASSERT_EQ(shared_libraries->GetLength(), 2); // Verify the first shared library. Handle class_loader_2 = hs.NewHandle(shared_libraries->Get(0)); std::vector class_loader_2_dex_files = MakeNonOwningPointerVector(classpath_dex_c); VerifyClassLoaderDexFiles(soa, class_loader_2, WellKnownClasses::dalvik_system_DelegateLastClassLoader, 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 class_loader_3 = hs.NewHandle(shared_libraries->Get(1)); std::vector class_loader_3_dex_files = MakeNonOwningPointerVector(classpath_dex_d); VerifyClassLoaderDexFiles(soa, class_loader_3, WellKnownClasses::dalvik_system_PathClassLoader, 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() == soa.Decode(WellKnownClasses::java_lang_BootClassLoader)); ASSERT_TRUE(class_loader_2->GetParent()->GetClass() == soa.Decode(WellKnownClasses::java_lang_BootClassLoader)); ASSERT_TRUE(class_loader_3->GetParent()->GetClass() == soa.Decode(WellKnownClasses::java_lang_BootClassLoader)); } TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibrariesInParentToo) { // Setup the context. std::vector> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA"); std::vector> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB"); std::vector> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC"); std::vector> 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 context = ClassLoaderContext::Create(context_spec); ASSERT_TRUE(context->OpenDexFiles()); // Setup the compilation sources. std::vector> compilation_sources = OpenTestDexFiles("MultiDex"); std::vector 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 class_loader_1 = hs.NewHandle( soa.Decode(jclass_loader)); // For the first class loader the class path dex files must come first and then the // compilation sources. std::vector 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, class_loader_1, WellKnownClasses::dalvik_system_PathClassLoader, class_loader_1_dex_files); // Verify its shared library. ArtField* field = jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); ObjPtr raw_shared_libraries = field->GetObject(class_loader_1.Get()); ASSERT_TRUE(raw_shared_libraries != nullptr); Handle> shared_libraries( hs.NewHandle(raw_shared_libraries->AsObjectArray())); ASSERT_EQ(shared_libraries->GetLength(), 1); Handle class_loader_2 = hs.NewHandle(shared_libraries->Get(0)); std::vector class_loader_2_dex_files = MakeNonOwningPointerVector(classpath_dex_b); VerifyClassLoaderDexFiles(soa, class_loader_2, WellKnownClasses::dalvik_system_PathClassLoader, class_loader_2_dex_files); raw_shared_libraries = field->GetObject(class_loader_2.Get()); ASSERT_TRUE(raw_shared_libraries == nullptr); // Verify the parent. Handle class_loader_3 = hs.NewHandle(class_loader_1->GetParent()); std::vector class_loader_3_dex_files = MakeNonOwningPointerVector(classpath_dex_c); VerifyClassLoaderDexFiles(soa, class_loader_3, WellKnownClasses::dalvik_system_PathClassLoader, 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> shared_libraries_2( hs.NewHandle(raw_shared_libraries->AsObjectArray())); ASSERT_EQ(shared_libraries->GetLength(), 1); Handle class_loader_4 = hs.NewHandle(shared_libraries_2->Get(0)); std::vector class_loader_4_dex_files = MakeNonOwningPointerVector(classpath_dex_d); VerifyClassLoaderDexFiles(soa, class_loader_4, WellKnownClasses::dalvik_system_PathClassLoader, 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() == soa.Decode(WellKnownClasses::java_lang_BootClassLoader)); ASSERT_TRUE(class_loader_3->GetParent()->GetClass() == soa.Decode(WellKnownClasses::java_lang_BootClassLoader)); ASSERT_TRUE(class_loader_4->GetParent()->GetClass() == soa.Decode(WellKnownClasses::java_lang_BootClassLoader)); } TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibrariesDependencies) { // Setup the context. std::vector> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA"); std::vector> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB"); std::vector> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC"); std::vector> 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 context = ClassLoaderContext::Create(context_spec); ASSERT_TRUE(context->OpenDexFiles()); // Setup the compilation sources. std::vector> compilation_sources = OpenTestDexFiles("MultiDex"); std::vector 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 class_loader_1 = hs.NewHandle( soa.Decode(jclass_loader)); // For the first class loader the class path dex files must come first and then the // compilation sources. std::vector 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, class_loader_1, WellKnownClasses::dalvik_system_PathClassLoader, class_loader_1_dex_files); // Verify its shared library. ArtField* field = jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); ObjPtr raw_shared_libraries = field->GetObject(class_loader_1.Get()); ASSERT_TRUE(raw_shared_libraries != nullptr); Handle> shared_libraries( hs.NewHandle(raw_shared_libraries->AsObjectArray())); ASSERT_EQ(shared_libraries->GetLength(), 1); Handle class_loader_2 = hs.NewHandle(shared_libraries->Get(0)); std::vector class_loader_2_dex_files = MakeNonOwningPointerVector(classpath_dex_b); VerifyClassLoaderDexFiles(soa, class_loader_2, WellKnownClasses::dalvik_system_PathClassLoader, 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> shared_libraries_2( hs.NewHandle(raw_shared_libraries->AsObjectArray())); ASSERT_EQ(shared_libraries_2->GetLength(), 1); Handle class_loader_3 = hs.NewHandle(shared_libraries_2->Get(0)); std::vector class_loader_3_dex_files = MakeNonOwningPointerVector(classpath_dex_c); VerifyClassLoaderDexFiles(soa, class_loader_3, WellKnownClasses::dalvik_system_PathClassLoader, class_loader_3_dex_files); raw_shared_libraries = field->GetObject(class_loader_3.Get()); ASSERT_TRUE(raw_shared_libraries == nullptr); // Verify the parent. Handle class_loader_4 = hs.NewHandle(class_loader_1->GetParent()); std::vector class_loader_4_dex_files = MakeNonOwningPointerVector(classpath_dex_d); VerifyClassLoaderDexFiles(soa, class_loader_4, WellKnownClasses::dalvik_system_PathClassLoader, 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() == soa.Decode(WellKnownClasses::java_lang_BootClassLoader)); ASSERT_TRUE(class_loader_3->GetParent()->GetClass() == soa.Decode(WellKnownClasses::java_lang_BootClassLoader)); ASSERT_TRUE(class_loader_4->GetParent()->GetClass() == soa.Decode(WellKnownClasses::java_lang_BootClassLoader)); } TEST_F(ClassLoaderContextTest, RemoveSourceLocations) { std::unique_ptr context = ClassLoaderContext::Create("PCL[a.dex]"); dchecked_vector classpath_dex; classpath_dex.push_back("a.dex"); dchecked_vector 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> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA"); std::vector> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB"); std::vector> 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 context = ClassLoaderContext::Create(context_spec); ASSERT_TRUE(context->OpenDexFiles()); // Setup the compilation sources. std::vector> compilation_sources = OpenTestDexFiles("MultiDex"); std::vector 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 class_loader_1 = hs.NewHandle( soa.Decode(jclass_loader)); // For the first class loader the class path dex files must come first and then the // compilation sources. std::vector 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, class_loader_1, WellKnownClasses::dalvik_system_PathClassLoader, class_loader_1_dex_files); // Verify its shared library. ArtField* field = jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); ObjPtr raw_shared_libraries = field->GetObject(class_loader_1.Get()); ASSERT_TRUE(raw_shared_libraries != nullptr); Handle> shared_libraries( hs.NewHandle(raw_shared_libraries->AsObjectArray())); ASSERT_EQ(shared_libraries->GetLength(), 1); Handle class_loader_2 = hs.NewHandle(shared_libraries->Get(0)); std::vector class_loader_2_dex_files = MakeNonOwningPointerVector(classpath_dex_b); VerifyClassLoaderDexFiles(soa, class_loader_2, WellKnownClasses::dalvik_system_PathClassLoader, class_loader_2_dex_files); // Verify the parent. Handle class_loader_3 = hs.NewHandle(class_loader_1->GetParent()); std::vector class_loader_3_dex_files = MakeNonOwningPointerVector(classpath_dex_c); VerifyClassLoaderDexFiles(soa, class_loader_3, WellKnownClasses::dalvik_system_PathClassLoader, 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> shared_libraries_2( hs.NewHandle(raw_shared_libraries->AsObjectArray())); 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() == soa.Decode(WellKnownClasses::java_lang_BootClassLoader)); ASSERT_TRUE(class_loader_3->GetParent()->GetClass() == soa.Decode(WellKnownClasses::java_lang_BootClassLoader)); } TEST_F(ClassLoaderContextTest, EncodeInOatFile) { std::string dex1_name = GetTestDexFileName("Main"); std::string dex2_name = GetTestDexFileName("MyClass"); std::unique_ptr context = ClassLoaderContext::Create("PCL[" + dex1_name + ":" + dex2_name + "]"); ASSERT_TRUE(context->OpenDexFiles()); std::vector> dex1 = OpenTestDexFiles("Main"); std::vector> 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 context = CreateContextForClassLoader(class_loader_b); ASSERT_TRUE(context->OpenDexFiles()); std::vector> dex1 = OpenTestDexFiles("Main"); std::vector> dex2 = OpenTestDexFiles("MyClass"); ASSERT_EQ(dex2.size(), 1u); std::string encoding = context->EncodeContextForOatFile(""); std::string expected_encoding = "IMC[*" + std::to_string(dex2[0]->GetLocationChecksum()) + "];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 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 context = CreateContextForClassLoader(class_loader_b); ASSERT_TRUE(context->OpenDexFiles()); std::string encoding = context->EncodeContextForDex2oat(""); std::string expected_encoding = "IMC[];PCL[" + GetTestDexFileName("Main") + "]"; ASSERT_EQ(expected_encoding, context->EncodeContextForDex2oat("")); } TEST_F(ClassLoaderContextTest, EncodeContextsSinglePath) { jobject class_loader = LoadDexInPathClassLoader("Main", nullptr); std::unique_ptr context = CreateContextForClassLoader(class_loader); std::map 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 context = CreateContextForClassLoader(class_loader); std::map 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 context = CreateContextForClassLoader(bottom_class_loader); std::map 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> libraries = hs.NewHandle( mirror::ObjectArray::Alloc( soa.Self(), GetClassRoot>(), 1)); libraries->Set(0, soa.Decode(class_loader_a)); jobject class_loader_b = LoadDexInPathClassLoader( "Main", nullptr, soa.AddLocalReference(libraries.Get())); std::unique_ptr context = CreateContextForClassLoader(class_loader_b); std::map 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{ "Main", "MultiDex"}, nullptr); std::unique_ptr context = CreateContextForClassLoader(class_loader); std::map 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> libraries = hs.NewHandle( mirror::ObjectArray::Alloc( soa.Self(), GetClassRoot>(), 1)); libraries->Set(0, soa.Decode(class_loader_a)); jobject class_loader_b = LoadDexInPathClassLoader( std::vector { "Main", "MultiDex" }, nullptr, soa.AddLocalReference(libraries.Get())); std::unique_ptr context = CreateContextForClassLoader(class_loader_b); std::map 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 context = CreateContextForClassLoader(class_loader_b); std::map encodings = context->EncodeClassPathContexts(""); ASSERT_EQ(1u, encodings.size()); ASSERT_EQ( "IMC[];PCL[" + GetTestDexFileName("Main") + "]", encodings.at("")); } TEST_F(ClassLoaderContextTest, EncodeContextsForSingleDex) { jobject class_loader = LoadDexInPathClassLoader("Main", nullptr); std::map encodings = ClassLoaderContext::EncodeClassPathContextsForClassLoader(class_loader); ASSERT_EQ(1u, encodings.size()); ASSERT_EQ("PCL[]", encodings.at(GetTestDexFileName("Main"))); } static jobject CreateForeignClassLoader() { ScopedObjectAccess soa(Thread::Current()); JNIEnv* env = soa.Env(); // 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. jmethodID ctor = env->GetMethodID(WellKnownClasses::java_lang_Object, "", "()V"); return env->NewObject(WellKnownClasses::java_lang_Object, ctor); } TEST_F(ClassLoaderContextTest, EncodeContextsForUnsupportedBase) { std::map empty; ASSERT_EQ( empty, ClassLoaderContext::EncodeClassPathContextsForClassLoader(CreateForeignClassLoader())); } TEST_F(ClassLoaderContextTest, EncodeContextsForUnsupportedChain) { jobject class_loader = LoadDexInPathClassLoader("Main", CreateForeignClassLoader()); std::map 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 { "Main", "MyClass" }, CreateForeignClassLoader()); std::map 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 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 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 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 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[*111]"; std::unique_ptr 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, ""); 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 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[*123:*456]" "{IMC[*321];IMC[*654]#IMC[*098:*999]};" "DLC[c.dex*890]"; std::unique_ptr 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, ":"); VerifyClassLoaderDLC(context.get(), 1, "c.dex"); VerifyClassLoaderSharedLibraryIMC(context.get(), 0, 0, ""); VerifyClassLoaderSharedLibraryIMC(context.get(), 0, 1, ":"); ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec), 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 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 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 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> libraries = hs.NewHandle( mirror::ObjectArray::Alloc( soa.Self(), GetClassRoot>(), 1)); libraries->Set(0, soa.Decode(class_loader_a)); jobject class_loader_b = LoadDexInPathClassLoader( "ForClassLoaderB", nullptr, soa.AddLocalReference(libraries.Get())); std::unique_ptr context = CreateContextForClassLoader(class_loader_b); ASSERT_TRUE(context != nullptr); std::vector> 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 context = CreateContextForClassLoader(class_loader); std::set result = context->CheckForDuplicateDexFiles( std::vector()); ASSERT_EQ(0u, result.size()); std::vector> dex1 = OpenTestDexFiles("ForClassLoaderA"); std::vector dex1_raw = MakeNonOwningPointerVector(dex1); result = context->CheckForDuplicateDexFiles(dex1_raw); ASSERT_EQ(0u, result.size()); } TEST_F(ClassLoaderContextTest, CheckForDuplicateDexFilesFound) { jobject class_loader = LoadDexInPathClassLoader(std::vector { "Main", "Main" }, nullptr); std::unique_ptr context = CreateContextForClassLoader(class_loader); std::vector> dex1 = OpenTestDexFiles("Main"); std::vector dex1_raw = MakeNonOwningPointerVector(dex1); std::set 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 context = CreateContextForClassLoader(class_loader_b); std::vector> dex1 = OpenTestDexFiles("ForClassLoaderA"); std::vector dex1_raw = MakeNonOwningPointerVector(dex1); std::set result = context->CheckForDuplicateDexFiles(dex1_raw); ASSERT_EQ(0u, result.size()); } } // namespace art