Fix `ClassLinker::AppendToBootClassPath` usages. am: 28451dc192 am: 678b965707

Original change: https://android-review.googlesource.com/c/platform/art/+/2196996

Change-Id: I8b82cbaa5f02351d1f4d558ec8008e0fe6dc63ce
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index b570d99..52e53e6 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -63,6 +63,7 @@
 struct CompilationHelper {
   std::vector<std::string> dex_file_locations;
   std::vector<ScratchFile> image_locations;
+  std::string extra_dex;
   std::vector<std::unique_ptr<const DexFile>> extra_dex_files;
   std::vector<ScratchFile> image_files;
   std::vector<ScratchFile> oat_files;
@@ -150,18 +151,11 @@
 inline void ImageTest::DoCompile(ImageHeader::StorageMode storage_mode,
                                  /*out*/ CompilationHelper& out_helper) {
   CompilerDriver* driver = compiler_driver_.get();
+  Runtime::Current()->AppendToBootClassPath(
+      out_helper.extra_dex, out_helper.extra_dex, out_helper.extra_dex_files);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   std::vector<const DexFile*> class_path = class_linker->GetBootClassPath();
 
-  for (const std::unique_ptr<const DexFile>& dex_file : out_helper.extra_dex_files) {
-    {
-      ScopedObjectAccess soa(Thread::Current());
-      // Inject in boot class path so that the compiler driver can see it.
-      class_linker->AppendToBootClassPath(soa.Self(), dex_file.get());
-    }
-    class_path.push_back(dex_file.get());
-  }
-
   // Enable write for dex2dex.
   for (const DexFile* dex_file : class_path) {
     out_helper.dex_file_locations.push_back(dex_file->GetLocation());
@@ -368,6 +362,7 @@
   compiler_options_->SetMaxImageBlockSize(max_image_block_size);
   image_classes_.clear();
   if (!extra_dex.empty()) {
+    helper.extra_dex = extra_dex;
     helper.extra_dex_files = OpenTestDexFiles(extra_dex.c_str());
   }
   DoCompile(storage_mode, helper);
diff --git a/openjdkjvmti/ti_class_loader.cc b/openjdkjvmti/ti_class_loader.cc
index d0a6634..cf82534 100644
--- a/openjdkjvmti/ti_class_loader.cc
+++ b/openjdkjvmti/ti_class_loader.cc
@@ -66,7 +66,8 @@
   art::ScopedObjectAccessUnchecked soa(self);
   art::StackHandleScope<3> hs(self);
   if (art::ClassLinker::IsBootClassLoader(soa, loader.Get())) {
-    art::Runtime::Current()->GetClassLinker()->AppendToBootClassPath(self, dex_file);
+    art::Runtime::Current()->AppendToBootClassPath(
+        dex_file->GetLocation(), dex_file->GetLocation(), {dex_file});
     return true;
   }
   art::Handle<art::mirror::Object> java_dex_file_obj(
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 59f93c5..2521ff3 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -512,8 +512,15 @@
 art::MemMap Redefiner::MoveDataToMemMap(const std::string& original_location,
                                         art::ArrayRef<const unsigned char> data,
                                         std::string* error_msg) {
+  std::string modified_location = StringPrintf("%s-transformed", original_location.c_str());
+  // A dangling multi-dex location appended to bootclasspath can cause inaccuracy in oat file
+  // validation. For simplicity, just convert it to a normal location.
+  size_t pos = modified_location.find(art::DexFileLoader::kMultiDexSeparator);
+  if (pos != std::string::npos) {
+    modified_location[pos] = '-';
+  }
   art::MemMap map = art::MemMap::MapAnonymous(
-      StringPrintf("%s-transformed", original_location.c_str()).c_str(),
+      modified_location.c_str(),
       data.size(),
       PROT_READ|PROT_WRITE,
       /*low_4gb=*/ false,
@@ -2481,7 +2488,9 @@
     art::ClassLinker* cl = runtime_->GetClassLinker();
     if (data.GetSourceClassLoader() == nullptr) {
       // AppendToBootClassPath includes dex file registration.
-      cl->AppendToBootClassPath(&data.GetRedefinition().GetDexFile(), data.GetNewDexCache());
+      const art::DexFile& dex_file = data.GetRedefinition().GetDexFile();
+      runtime_->AppendToBootClassPath(
+          dex_file.GetLocation(), dex_file.GetLocation(), {{&dex_file, data.GetNewDexCache()}});
     } else {
       cl->RegisterExistingDexCache(data.GetNewDexCache(), data.GetSourceClassLoader());
     }
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 1ac4756..eb97140 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -176,6 +176,7 @@
 
   // Add boot class path dex files that were not included in the boot image.
   // ClassLinker takes ownership of these dex files.
+  // DO NOT use directly. Use `Runtime::AddExtraBootDexFiles`.
   void AddExtraBootDexFiles(Thread* self,
                             std::vector<std::unique_ptr<const DexFile>>&& additional_dex_files)
       REQUIRES_SHARED(Locks::mutator_lock_);
@@ -780,10 +781,12 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       NO_THREAD_SAFETY_ANALYSIS;
 
+  // DO NOT use directly. Use `Runtime::AppendToBootClassPath`.
   void AppendToBootClassPath(Thread* self, const DexFile* dex_file)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_);
 
+  // DO NOT use directly. Use `Runtime::AppendToBootClassPath`.
   void AppendToBootClassPath(const DexFile* dex_file, ObjPtr<mirror::DexCache> dex_cache)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 390fcf0..6e583af 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -16,6 +16,8 @@
 
 #include "runtime.h"
 
+#include <utility>
+
 #ifdef __linux__
 #include <sys/prctl.h>
 #endif
@@ -3477,28 +3479,69 @@
   return false;
 }
 
+void Runtime::AppendToBootClassPath(const std::string& filename, const std::string& location) {
+  DCHECK(!DexFileLoader::IsMultiDexLocation(filename.c_str()));
+  boot_class_path_.push_back(filename);
+  if (!boot_class_path_locations_.empty()) {
+    DCHECK(!DexFileLoader::IsMultiDexLocation(location.c_str()));
+    boot_class_path_locations_.push_back(location);
+  }
+}
+
 void Runtime::AppendToBootClassPath(
     const std::string& filename,
     const std::string& location,
     const std::vector<std::unique_ptr<const art::DexFile>>& dex_files) {
-  boot_class_path_.push_back(filename);
-  if (!boot_class_path_locations_.empty()) {
-    boot_class_path_locations_.push_back(location);
-  }
+  AppendToBootClassPath(filename, location);
   ScopedObjectAccess soa(Thread::Current());
   for (const std::unique_ptr<const art::DexFile>& dex_file : dex_files) {
+    // The first element must not be at a multi-dex location, while other elements must be.
+    DCHECK_NE(DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str()),
+              dex_file.get() == dex_files.begin()->get());
     GetClassLinker()->AppendToBootClassPath(Thread::Current(), dex_file.get());
   }
 }
 
+void Runtime::AppendToBootClassPath(const std::string& filename,
+                                    const std::string& location,
+                                    const std::vector<const art::DexFile*>& dex_files) {
+  AppendToBootClassPath(filename, location);
+  ScopedObjectAccess soa(Thread::Current());
+  for (const art::DexFile* dex_file : dex_files) {
+    // The first element must not be at a multi-dex location, while other elements must be.
+    DCHECK_NE(DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str()),
+              dex_file == *dex_files.begin());
+    GetClassLinker()->AppendToBootClassPath(Thread::Current(), dex_file);
+  }
+}
+
+void Runtime::AppendToBootClassPath(
+    const std::string& filename,
+    const std::string& location,
+    const std::vector<std::pair<const art::DexFile*, ObjPtr<mirror::DexCache>>>&
+        dex_files_and_cache) {
+  AppendToBootClassPath(filename, location);
+  ScopedObjectAccess soa(Thread::Current());
+  for (const auto& [dex_file, dex_cache] : dex_files_and_cache) {
+    // The first element must not be at a multi-dex location, while other elements must be.
+    DCHECK_NE(DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str()),
+              dex_file == dex_files_and_cache.begin()->first);
+    GetClassLinker()->AppendToBootClassPath(dex_file, dex_cache);
+  }
+}
+
 void Runtime::AddExtraBootDexFiles(const std::string& filename,
                                    const std::string& location,
                                    std::vector<std::unique_ptr<const art::DexFile>>&& dex_files) {
-  boot_class_path_.push_back(filename);
-  if (!boot_class_path_locations_.empty()) {
-    boot_class_path_locations_.push_back(location);
-  }
+  AppendToBootClassPath(filename, location);
   ScopedObjectAccess soa(Thread::Current());
+  if (kIsDebugBuild) {
+    for (const std::unique_ptr<const art::DexFile>& dex_file : dex_files) {
+      // The first element must not be at a multi-dex location, while other elements must be.
+      DCHECK_NE(DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str()),
+                dex_file.get() == dex_files.begin()->get());
+    }
+  }
   GetClassLinker()->AddExtraBootDexFiles(Thread::Current(), std::move(dex_files));
 }
 
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 395128d..a932718 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -302,11 +302,24 @@
     return boot_class_path_locations_.empty() ? boot_class_path_ : boot_class_path_locations_;
   }
 
-  // Dynamically add an element to boot class path.
+  // Dynamically adds an element to boot class path.
   void AppendToBootClassPath(const std::string& filename,
                              const std::string& location,
                              const std::vector<std::unique_ptr<const art::DexFile>>& dex_files);
 
+  // Same as above, but takes raw pointers.
+  void AppendToBootClassPath(const std::string& filename,
+                             const std::string& location,
+                             const std::vector<const art::DexFile*>& dex_files);
+
+  // Same as above, but also takes a dex cache for each dex file.
+  void AppendToBootClassPath(
+      const std::string& filename,
+      const std::string& location,
+      const std::vector<std::pair<const art::DexFile*, ObjPtr<mirror::DexCache>>>&
+          dex_files_and_cache);
+
+  // Dynamically adds an element to boot class path and takes ownership of the dex files.
   void AddExtraBootDexFiles(const std::string& filename,
                             const std::string& location,
                             std::vector<std::unique_ptr<const art::DexFile>>&& dex_files);
@@ -1156,6 +1169,8 @@
   // Caches the apex versions produced by `GetApexVersions`.
   void InitializeApexVersions();
 
+  void AppendToBootClassPath(const std::string& filename, const std::string& location);
+
   // A pointer to the active runtime or null.
   static Runtime* instance_;