Change ClassLinker::dex_caches_ from list to unordered_map.

Move the DexFile* pointer from struct field out to map key.
This makes it easy and fast to find DexCache from DexFile.

Add check that given DexFile* is registered only once.

Test: test.py -b -r --host --64
Test: generated images are identical as before
Change-Id: I84a6d6cbf963af2408abe5bb5e4c99d0ca11df78
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index d7c2d65..b047387 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -1205,7 +1205,8 @@
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   ReaderMutexLock mu2(self, *Locks::dex_lock_);
   dex_caches.reserve(class_linker->GetDexCachesData().size());
-  for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
+  for (const auto& entry : class_linker->GetDexCachesData()) {
+    const ClassLinker::DexCacheData& data = entry.second;
     if (self->IsJWeakCleared(data.weak_root)) {
       continue;
     }
@@ -1256,7 +1257,8 @@
   {
     ReaderMutexLock mu(self, *Locks::dex_lock_);
     // Count number of dex caches not in the boot image.
-    for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
+    for (const auto& entry : class_linker->GetDexCachesData()) {
+      const ClassLinker::DexCacheData& data = entry.second;
       ObjPtr<mirror::DexCache> dex_cache =
           ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root));
       if (dex_cache == nullptr) {
@@ -1273,23 +1275,10 @@
   CHECK(dex_caches != nullptr) << "Failed to allocate a dex cache array.";
   {
     ReaderMutexLock mu(self, *Locks::dex_lock_);
-    size_t non_image_dex_caches = 0;
-    // Re-count number of non image dex caches.
-    for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
-      ObjPtr<mirror::DexCache> dex_cache =
-          ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root));
-      if (dex_cache == nullptr) {
-        continue;
-      }
-      const DexFile* dex_file = dex_cache->GetDexFile();
-      if (IsImageDexCache(dex_cache)) {
-        non_image_dex_caches += image_dex_files.find(dex_file) != image_dex_files.end() ? 1u : 0u;
-      }
-    }
-    CHECK_EQ(dex_cache_count, non_image_dex_caches)
-        << "The number of non-image dex caches changed.";
-    size_t i = 0;
-    for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
+    // Collect all dex caches (and sort them by registration index to make output deterministic).
+    std::map<uint64_t, ObjPtr<mirror::DexCache>> non_image_dex_caches;
+    for (const auto& entry : class_linker->GetDexCachesData()) {
+      const ClassLinker::DexCacheData& data = entry.second;
       ObjPtr<mirror::DexCache> dex_cache =
           ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root));
       if (dex_cache == nullptr) {
@@ -1298,10 +1287,17 @@
       const DexFile* dex_file = dex_cache->GetDexFile();
       if (IsImageDexCache(dex_cache) &&
           image_dex_files.find(dex_file) != image_dex_files.end()) {
-        dex_caches->Set<false>(i, dex_cache.Ptr());
-        ++i;
+        bool inserted = non_image_dex_caches.emplace(data.registration_index, dex_cache).second;
+        CHECK(inserted);
       }
     }
+    CHECK_EQ(dex_cache_count, non_image_dex_caches.size())
+        << "The number of non-image dex caches changed.";
+    // Write the dex caches to the output array (in registration order).
+    size_t i = 0;
+    for (const auto& entry : non_image_dex_caches) {
+      dex_caches->Set<false>(i++, entry.second.Ptr());
+    }
   }
   return dex_caches;
 }
@@ -2102,7 +2098,8 @@
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
     Thread* self = Thread::Current();
     ReaderMutexLock mu(self, *Locks::dex_lock_);
-    for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
+    for (const auto& entry : class_linker->GetDexCachesData()) {
+      const ClassLinker::DexCacheData& data = entry.second;
       ObjPtr<mirror::DexCache> dex_cache =
           ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root));
       if (dex_cache == nullptr ||
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index da066d1..4e8f8ed 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -455,10 +455,8 @@
   ReaderMutexLock rmu(self, *Locks::dex_lock_);
   std::for_each(dex_caches_.begin(),
                 dex_caches_.end(),
-                [&](DexCacheData& dcd) REQUIRES(Locks::mutator_lock_) {
-                  if (dcd.IsValid()) {
-                    visitor(dcd.dex_file);
-                  }
+                [&](const auto& entry) REQUIRES(Locks::mutator_lock_) {
+                  visitor(/*dex_file=*/entry.first);
                 });
 }
 
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 2ce0294..4d1589b 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3894,14 +3894,14 @@
   bool initialize_oat_file_data = (oat_file != nullptr) && oat_file->IsExecutable();
   JavaVMExt* const vm = self->GetJniEnv()->GetVm();
   for (auto it = dex_caches_.begin(); it != dex_caches_.end(); ) {
-    DexCacheData data = *it;
+    const DexCacheData& data = it->second;
     if (self->IsJWeakCleared(data.weak_root)) {
       vm->DeleteWeakGlobalRef(self, data.weak_root);
       it = dex_caches_.erase(it);
     } else {
       if (initialize_oat_file_data &&
-          it->dex_file->GetOatDexFile() != nullptr &&
-          it->dex_file->GetOatDexFile()->GetOatFile() == oat_file) {
+          it->first->GetOatDexFile() != nullptr &&
+          it->first->GetOatDexFile()->GetOatFile() == oat_file) {
         initialize_oat_file_data = false;  // Already initialized.
       }
       ++it;
@@ -3916,9 +3916,8 @@
   jweak dex_cache_jweak = vm->AddWeakGlobalRef(self, dex_cache);
   DexCacheData data;
   data.weak_root = dex_cache_jweak;
-  data.dex_file = dex_cache->GetDexFile();
   data.class_table = ClassTableForClassLoader(class_loader);
-  AddNativeDebugInfoForDex(self, data.dex_file);
+  AddNativeDebugInfoForDex(self, &dex_file);
   DCHECK(data.class_table != nullptr);
   // Make sure to hold the dex cache live in the class table. This case happens for the boot class
   // path dex caches without an image.
@@ -3930,7 +3929,8 @@
     // remembered sets and generational GCs.
     WriteBarrier::ForEveryFieldWrite(class_loader);
   }
-  dex_caches_.push_back(data);
+  bool inserted = dex_caches_.emplace(&dex_file, std::move(data)).second;
+  CHECK(inserted);
 }
 
 ObjPtr<mirror::DexCache> ClassLinker::DecodeDexCacheLocked(Thread* self, const DexCacheData* data) {
@@ -3944,7 +3944,7 @@
     const DexCacheData* data,
     ObjPtr<mirror::ClassLoader> class_loader) {
   CHECK(data != nullptr);
-  DCHECK_EQ(dex_cache->GetDexFile(), data->dex_file);
+  DCHECK_EQ(FindDexCacheDataLocked(*dex_cache->GetDexFile()), data);
   return data->class_table == ClassTableForClassLoader(class_loader);
 }
 
@@ -4089,13 +4089,14 @@
     return dex_cache;
   }
   // Failure, dump diagnostic and abort.
-  for (const DexCacheData& data : dex_caches_) {
+  for (const auto& entry : dex_caches_) {
+    const DexCacheData& data = entry.second;
     if (DecodeDexCacheLocked(self, &data) != nullptr) {
-      LOG(FATAL_WITHOUT_ABORT) << "Registered dex file " << data.dex_file->GetLocation();
+      LOG(FATAL_WITHOUT_ABORT) << "Registered dex file " << entry.first->GetLocation();
     }
   }
   LOG(FATAL) << "Failed to find DexCache for DexFile " << dex_file.GetLocation()
-             << " " << &dex_file << " " << dex_cache_data->dex_file;
+             << " " << &dex_file;
   UNREACHABLE();
 }
 
@@ -4103,29 +4104,21 @@
   const DexFile* dex_file = dex_cache->GetDexFile();
   DCHECK(dex_file != nullptr);
   ReaderMutexLock mu(self, *Locks::dex_lock_);
-  // Search assuming unique-ness of dex file.
-  for (const DexCacheData& data : dex_caches_) {
-    // Avoid decoding (and read barriers) other unrelated dex caches.
-    if (data.dex_file == dex_file) {
-      ObjPtr<mirror::DexCache> registered_dex_cache = DecodeDexCacheLocked(self, &data);
-      if (registered_dex_cache != nullptr) {
-        CHECK_EQ(registered_dex_cache, dex_cache) << dex_file->GetLocation();
-        return data.class_table;
-      }
+  auto it = dex_caches_.find(dex_file);
+  if (it != dex_caches_.end()) {
+    const DexCacheData& data = it->second;
+    ObjPtr<mirror::DexCache> registered_dex_cache = DecodeDexCacheLocked(self, &data);
+    if (registered_dex_cache != nullptr) {
+      CHECK_EQ(registered_dex_cache, dex_cache) << dex_file->GetLocation();
+      return data.class_table;
     }
   }
   return nullptr;
 }
 
 const ClassLinker::DexCacheData* ClassLinker::FindDexCacheDataLocked(const DexFile& dex_file) {
-  // Search assuming unique-ness of dex file.
-  for (const DexCacheData& data : dex_caches_) {
-    // Avoid decoding (and read barriers) other unrelated dex caches.
-    if (data.dex_file == &dex_file) {
-      return &data;
-    }
-  }
-  return nullptr;
+  auto it = dex_caches_.find(&dex_file);
+  return it != dex_caches_.end() ? &it->second : nullptr;
 }
 
 void ClassLinker::CreatePrimitiveClass(Thread* self,
@@ -9754,13 +9747,14 @@
     if (loader != nullptr) {
       os << "#" << class_loader_index++ << " " << loader->GetClass()->PrettyDescriptor() << ": [";
       bool saw_one_dex_file = false;
-      for (const DexCacheData& dex_cache : dex_caches_) {
-        if (dex_cache.IsValid() && dex_cache.class_table == class_loader.class_table) {
+      for (const auto& entry : dex_caches_) {
+        const DexCacheData& dex_cache = entry.second;
+        if (dex_cache.class_table == class_loader.class_table) {
           if (saw_one_dex_file) {
             os << ":";
           }
           saw_one_dex_file = true;
-          os << dex_cache.dex_file->GetLocation();
+          os << entry.first->GetLocation();
         }
       }
       os << "]";
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 5faf760..59797f9 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -22,6 +22,7 @@
 #include <set>
 #include <string>
 #include <type_traits>
+#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -807,27 +808,26 @@
 
   struct DexCacheData {
     // Construct an invalid data object.
-    DexCacheData()
-        : weak_root(nullptr),
-          dex_file(nullptr),
-          class_table(nullptr) { }
-
-    // Check if the data is valid.
-    bool IsValid() const {
-      return dex_file != nullptr;
+    DexCacheData() : weak_root(nullptr), class_table(nullptr) {
+      static std::atomic_uint64_t s_registration_count(0);
+      registration_index = s_registration_count.fetch_add(1, std::memory_order_seq_cst);
     }
+    DexCacheData(DexCacheData&&) = default;
 
     // Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may
     // not work properly.
     jweak weak_root;
-    // The following field caches the DexCache's field here to avoid unnecessary jweak decode that
-    // triggers read barriers (and marks them alive unnecessarily and messes with class unloading.)
-    const DexFile* dex_file;
     // Identify the associated class loader's class table. This is used to make sure that
     // the Java call to native DexCache.setResolvedType() inserts the resolved type in that
     // class table. It is also used to make sure we don't register the same dex cache with
     // multiple class loaders.
     ClassTable* class_table;
+    // Monotonically increasing integer which records the order in which DexFiles were registered.
+    // Used only to preserve determinism when creating compiled image.
+    uint64_t registration_index;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(DexCacheData);
   };
 
   // Forces a class to be marked as initialized without actually running initializers. Should only
@@ -1103,7 +1103,7 @@
       REQUIRES(Locks::dex_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
   const DexCacheData* FindDexCacheDataLocked(const DexFile& dex_file)
-      REQUIRES(Locks::dex_lock_)
+      REQUIRES_SHARED(Locks::dex_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
   static ObjPtr<mirror::DexCache> DecodeDexCacheLocked(Thread* self, const DexCacheData* data)
       REQUIRES_SHARED(Locks::dex_lock_, Locks::mutator_lock_);
@@ -1244,7 +1244,7 @@
   size_t GetDexCacheCount() REQUIRES_SHARED(Locks::mutator_lock_, Locks::dex_lock_) {
     return dex_caches_.size();
   }
-  const std::list<DexCacheData>& GetDexCachesData()
+  const std::unordered_map<const DexFile*, DexCacheData>& GetDexCachesData()
       REQUIRES_SHARED(Locks::mutator_lock_, Locks::dex_lock_) {
     return dex_caches_;
   }
@@ -1358,7 +1358,7 @@
 
   // JNI weak globals and side data to allow dex caches to get unloaded. We lazily delete weak
   // globals when we register new dex files.
-  std::list<DexCacheData> dex_caches_ GUARDED_BY(Locks::dex_lock_);
+  std::unordered_map<const DexFile*, DexCacheData> dex_caches_ GUARDED_BY(Locks::dex_lock_);
 
   // This contains the class loaders which have class tables. It is populated by
   // InsertClassTableForClassLoader.
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index e0f498d..035a9cb 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -1520,7 +1520,8 @@
   MutableHandle<mirror::DexCache> dex_cache(hs.NewHandle<mirror::DexCache>(nullptr));
   {
     ReaderMutexLock mu(soa.Self(), *Locks::dex_lock_);
-    for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
+    for (const auto& entry : class_linker->GetDexCachesData()) {
+      const ClassLinker::DexCacheData& data = entry.second;
       dex_cache.Assign(soa.Self()->DecodeJObject(data.weak_root)->AsDexCache());
       if (dex_cache != nullptr) {
         break;