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);