Support InMemoryDexClassLoader in ClassLoaderContext
Add new class loader tag IMC to class loader context spec which
represents InMemoryDexClassLoader. A special case is required to not
attempt to open its dex files as the dex location does not correspond
to a real file path. This is achieved by setting load-attempted variable
to 'true' when encountering IMC whilst parsing a spec. Context with IMC
can still have opened dex files if it was created from an existing class
loader.
Bug: 72131483
Test: m test-art-host-gtest-class_loader_context_text
Change-Id: Ic64065819018a1e56dee0f65405d26beb8fd7bbd
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index 3c5f1ef..7fba830 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -61,6 +61,13 @@
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,
@@ -70,6 +77,15 @@
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) {
@@ -102,6 +118,13 @@
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
@@ -249,19 +272,24 @@
}
TEST_F(ClassLoaderContextTest, ParseValidContextPCL) {
- std::unique_ptr<ClassLoaderContext> context =
- ClassLoaderContext::Create("PCL[a.dex]");
+ 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]");
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("DLC[a.dex]");
VerifyContextSize(context.get(), 1);
VerifyClassLoaderDLC(context.get(), 0, "a.dex");
}
+TEST_F(ClassLoaderContextTest, ParseInvalidContextIMC) {
+ // 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[a.dex]");
+ 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]");
@@ -408,7 +436,6 @@
return;
}
-
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create(
"PCL[" + multidex_name + ":" + myclass_dex_name + "];" +
@@ -469,6 +496,19 @@
ASSERT_FALSE(context->OpenDexFiles(InstructionSet::kArm, ""));
}
+TEST_F(ClassLoaderContextTest, OpenDexFilesForIMCFails) {
+ std::unique_ptr<ClassLoaderContext> context;
+ std::string dex_name = GetTestDexFileName("Main");
+
+ context = ParseContextWithChecksums("PCL[" + dex_name + "*111]");
+ VerifyContextSize(context.get(), 1);
+ ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, "."));
+
+ context = ParseContextWithChecksums("IMC[" + dex_name + "*111]");
+ VerifyContextSize(context.get(), 1);
+ ASSERT_FALSE(context->OpenDexFiles(InstructionSet::kArm, "."));
+}
+
TEST_F(ClassLoaderContextTest, CreateClassLoader) {
std::string dex_name = GetTestDexFileName("Main");
std::unique_ptr<ClassLoaderContext> context =
@@ -1079,6 +1119,34 @@
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, VerifyClassLoaderContextFirstElement) {
std::string context_spec = "PCL[]";
@@ -1133,6 +1201,22 @@
ClassLoaderContext::VerificationResult::kMismatch);
}
+TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextWithIMCMatch) {
+ std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890];IMC[d.dex*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, "d.dex");
+
+ ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec),
+ ClassLoaderContext::VerificationResult::kVerifies);
+}
+
TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchSpecial) {
std::string context_spec = "&";
std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
@@ -1200,6 +1284,25 @@
ClassLoaderContext::VerificationResult::kMismatch);
}
+TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchWithIMCSL) {
+ std::string context_spec =
+ "IMC[a.dex*123:b.dex*456]{IMC[d.dex*321];IMC[e.dex*654]#IMC[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);
+ VerifyClassLoaderIMC(context.get(), 0, "a.dex:b.dex");
+ VerifyClassLoaderDLC(context.get(), 1, "c.dex");
+ VerifyClassLoaderSharedLibraryIMC(context.get(), 0, 0, "d.dex");
+ VerifyClassLoaderSharedLibraryIMC(context.get(), 0, 1, "f.dex:g.dex");
+
+ 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);
@@ -1223,6 +1326,29 @@
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);