Move descriptor hashing to `mirror::Class`.

We may want to reuse the hashing for image classes which are
not closely related with the `ClassTable` where the hashing
was previously.

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 181943478
Change-Id: Icf1b51085829509f58e7685dc1e9cf2b0583d107
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index d4cd867..56fbe90 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -1115,7 +1115,7 @@
     DCHECK(!class_table->classes_.empty());
     ClassTable::ClassSet& last_class_set = class_table->classes_.back();
     for (mirror::Class* klass : classes_to_prune_) {
-      uint32_t hash = ClassTable::TableSlot::HashDescriptor(klass);
+      uint32_t hash = klass->DescriptorHash();
       auto it = last_class_set.FindWithHash(ClassTable::TableSlot(klass, hash), hash);
       DCHECK(it != last_class_set.end());
       last_class_set.erase(it);
@@ -1687,7 +1687,7 @@
       size_t oat_index = pair.second;
       DCHECK(image_writer->image_infos_[oat_index].class_table_.has_value());
       ClassTable::ClassSet& class_table = *image_writer->image_infos_[oat_index].class_table_;
-      uint32_t hash = ClassTable::TableSlot::HashDescriptor(klass);
+      uint32_t hash = klass->DescriptorHash();
       bool inserted = class_table.InsertWithHash(ClassTable::TableSlot(klass, hash), hash).second;
       DCHECK(inserted) << "Class " << klass->PrettyDescriptor()
           << " (" << klass.Ptr() << ") already inserted";
@@ -1703,7 +1703,7 @@
         DCHECK(image_info.class_table_.has_value());
         ClassTable::ClassSet& table = *image_info.class_table_;
         for (mirror::Class* klass : boot_image_classes) {
-          uint32_t hash = ClassTable::TableSlot::HashDescriptor(klass);
+          uint32_t hash = klass->DescriptorHash();
           bool inserted = table.InsertWithHash(ClassTable::TableSlot(klass, hash), hash).second;
           DCHECK(inserted) << "Boot image class " << klass->PrettyDescriptor()
               << " (" << klass << ") already inserted";
diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h
index ff775d7..071376c 100644
--- a/runtime/class_table-inl.h
+++ b/runtime/class_table-inl.h
@@ -29,41 +29,12 @@
 namespace art {
 
 inline ClassTable::TableSlot::TableSlot(ObjPtr<mirror::Class> klass)
-    : TableSlot(klass, HashDescriptor(klass)) {}
-
-inline uint32_t ClassTable::TableSlot::HashDescriptor(ObjPtr<mirror::Class> klass) {
-  // No read barriers needed, we're reading a chain of constant references for comparison with null
-  // and retrieval of constant primitive data. See `ReadBarrierOption` and `Class::GetDescriptor()`.
-  DCHECK(klass != nullptr);
-  ObjPtr<mirror::Class> orig_klass = klass;  // For debug check.
-  uint32_t hash = StartModifiedUtf8Hash();
-  while (klass->IsArrayClass()) {
-    klass = klass->GetComponentType<kDefaultVerifyFlags, kWithoutReadBarrier>();
-    hash = UpdateModifiedUtf8Hash(hash, '[');
-  }
-  if (UNLIKELY(klass->IsProxyClass())) {
-    hash = UpdateHashForProxyClass(hash, klass);
-  } else if (klass->IsPrimitive()) {
-    hash = UpdateModifiedUtf8Hash(hash, Primitive::Descriptor(klass->GetPrimitiveType())[0]);
-  } else {
-    const DexFile& dex_file = klass->GetDexFile();
-    const dex::TypeId& type_id = dex_file.GetTypeId(klass->GetDexTypeIndex());
-    std::string_view descriptor = dex_file.GetTypeDescriptorView(type_id);
-    hash = UpdateModifiedUtf8Hash(hash, descriptor);
-  }
-
-  if (kIsDebugBuild) {
-    std::string temp;
-    CHECK_EQ(hash, ComputeModifiedUtf8Hash(orig_klass->GetDescriptor(&temp)));
-  }
-
-  return hash;
-}
+    : TableSlot(klass, klass->DescriptorHash()) {}
 
 inline uint32_t ClassTable::ClassDescriptorHash::operator()(const TableSlot& slot) const {
   // No read barriers needed, we're reading a chain of constant references for comparison with null
-  // and retrieval of constant primitive data. See `ReadBarrierOption` and `Class::GetDescriptor()`.
-  return TableSlot::HashDescriptor(slot.Read<kWithoutReadBarrier>());
+  // and retrieval of constant primitive data. See `ReadBarrierOption` and `Class::DescriptorHash()`.
+  return slot.Read<kWithoutReadBarrier>()->DescriptorHash();
 }
 
 inline uint32_t ClassTable::ClassDescriptorHash::operator()(const DescriptorHashPair& pair) const {
@@ -202,7 +173,7 @@
 
 inline ClassTable::TableSlot::TableSlot(ObjPtr<mirror::Class> klass, uint32_t descriptor_hash)
     : data_(Encode(klass, MaskHash(descriptor_hash))) {
-  DCHECK_EQ(descriptor_hash, HashDescriptor(klass));
+  DCHECK_EQ(descriptor_hash, klass->DescriptorHash());
 }
 
 template <typename Filter>
@@ -213,9 +184,9 @@
 }
 
 inline ObjPtr<mirror::Class> ClassTable::LookupByDescriptor(ObjPtr<mirror::Class> klass) {
+  uint32_t hash = klass->DescriptorHash();
   std::string temp;
   const char* descriptor = klass->GetDescriptor(&temp);
-  uint32_t hash = TableSlot::HashDescriptor(klass);
   return Lookup(descriptor, hash);
 }
 
diff --git a/runtime/class_table.cc b/runtime/class_table.cc
index 10f60ab..429f926 100644
--- a/runtime/class_table.cc
+++ b/runtime/class_table.cc
@@ -23,34 +23,6 @@
 
 namespace art {
 
-uint32_t ClassTable::TableSlot::UpdateHashForProxyClass(
-    uint32_t hash, ObjPtr<mirror::Class> proxy_class) {
-  // No read barrier needed, the `name` field is constant for proxy classes and
-  // the contents of the String are also constant. See ReadBarrierOption.
-  // Note: The `proxy_class` can be a from-space reference.
-  DCHECK(proxy_class->IsProxyClass());
-  ObjPtr<mirror::String> name = proxy_class->GetName<kVerifyNone, kWithoutReadBarrier>();
-  DCHECK(name != nullptr);
-  // Update hash for characters we would get from `DotToDescriptor(name->ToModifiedUtf8())`.
-  DCHECK_NE(name->GetLength(), 0);
-  DCHECK_NE(name->CharAt(0), '[');
-  hash = UpdateModifiedUtf8Hash(hash, 'L');
-  if (name->IsCompressed()) {
-    std::string_view dot_name(reinterpret_cast<const char*>(name->GetValueCompressed()),
-                              name->GetLength());
-    for (char c : dot_name) {
-      hash = UpdateModifiedUtf8Hash(hash, (c != '.') ? c : '/');
-    }
-  } else {
-    std::string dot_name = name->ToModifiedUtf8();
-    for (char c : dot_name) {
-      hash = UpdateModifiedUtf8Hash(hash, (c != '.') ? c : '/');
-    }
-  }
-  hash = UpdateModifiedUtf8Hash(hash, ';');
-  return hash;
-}
-
 ClassTable::ClassTable() : lock_("Class loader classes", kClassLoaderClassesLock) {
   Runtime* const runtime = Runtime::Current();
   classes_.push_back(ClassSet(runtime->GetHashTableMinLoadFactor(),
@@ -150,7 +122,7 @@
 }
 
 void ClassTable::Insert(ObjPtr<mirror::Class> klass) {
-  InsertWithHash(klass, TableSlot::HashDescriptor(klass));
+  InsertWithHash(klass, klass->DescriptorHash());
 }
 
 void ClassTable::InsertWithHash(ObjPtr<mirror::Class> klass, size_t hash) {
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 3377f14..212a7d6 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -78,9 +78,6 @@
       return MaskHash(other) == Hash();
     }
 
-    static uint32_t HashDescriptor(ObjPtr<mirror::Class> klass)
-        REQUIRES_SHARED(Locks::mutator_lock_);
-
     template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
     ObjPtr<mirror::Class> Read() const REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -96,9 +93,6 @@
     static uint32_t Encode(ObjPtr<mirror::Class> klass, uint32_t hash_bits)
         REQUIRES_SHARED(Locks::mutator_lock_);
 
-    static uint32_t UpdateHashForProxyClass(uint32_t hash, ObjPtr<mirror::Class> proxy_class)
-        REQUIRES_SHARED(Locks::mutator_lock_);
-
     // Data contains the class pointer GcRoot as well as the low bits of the descriptor hash.
     mutable Atomic<uint32_t> data_;
     static constexpr uint32_t kHashMask = kObjectAlignment - 1;
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 84f117f..b6bd22e 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -898,6 +898,34 @@
   }
 }
 
+inline uint32_t Class::DescriptorHash() {
+  // No read barriers needed, we're reading a chain of constant references for comparison with null
+  // and retrieval of constant primitive data. See `ReadBarrierOption` and `Class::GetDescriptor()`.
+  ObjPtr<mirror::Class> klass = this;
+  uint32_t hash = StartModifiedUtf8Hash();
+  while (klass->IsArrayClass()) {
+    klass = klass->GetComponentType<kDefaultVerifyFlags, kWithoutReadBarrier>();
+    hash = UpdateModifiedUtf8Hash(hash, '[');
+  }
+  if (UNLIKELY(klass->IsProxyClass())) {
+    hash = UpdateHashForProxyClass(hash, klass);
+  } else if (klass->IsPrimitive()) {
+    hash = UpdateModifiedUtf8Hash(hash, Primitive::Descriptor(klass->GetPrimitiveType())[0]);
+  } else {
+    const DexFile& dex_file = klass->GetDexFile();
+    const dex::TypeId& type_id = dex_file.GetTypeId(klass->GetDexTypeIndex());
+    std::string_view descriptor = dex_file.GetTypeDescriptorView(type_id);
+    hash = UpdateModifiedUtf8Hash(hash, descriptor);
+  }
+
+  if (kIsDebugBuild) {
+    std::string temp;
+    CHECK_EQ(hash, ComputeModifiedUtf8Hash(GetDescriptor(&temp)));
+  }
+
+  return hash;
+}
+
 inline void Class::AssertInitializedOrInitializingInThread(Thread* self) {
   if (kIsDebugBuild && !IsInitialized()) {
     CHECK(IsInitializing()) << PrettyClass() << " is not initializing: " << GetStatus();
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index a07a15b..37a4197 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -1702,6 +1702,33 @@
   return storage == match;
 }
 
+uint32_t Class::UpdateHashForProxyClass(uint32_t hash, ObjPtr<mirror::Class> proxy_class) {
+  // No read barrier needed, the `name` field is constant for proxy classes and
+  // the contents of the String are also constant. See ReadBarrierOption.
+  // Note: The `proxy_class` can be a from-space reference.
+  DCHECK(proxy_class->IsProxyClass());
+  ObjPtr<mirror::String> name = proxy_class->GetName<kVerifyNone, kWithoutReadBarrier>();
+  DCHECK(name != nullptr);
+  // Update hash for characters we would get from `DotToDescriptor(name->ToModifiedUtf8())`.
+  DCHECK_NE(name->GetLength(), 0);
+  DCHECK_NE(name->CharAt(0), '[');
+  hash = UpdateModifiedUtf8Hash(hash, 'L');
+  if (name->IsCompressed()) {
+    std::string_view dot_name(reinterpret_cast<const char*>(name->GetValueCompressed()),
+                              name->GetLength());
+    for (char c : dot_name) {
+      hash = UpdateModifiedUtf8Hash(hash, (c != '.') ? c : '/');
+    }
+  } else {
+    std::string dot_name = name->ToModifiedUtf8();
+    for (char c : dot_name) {
+      hash = UpdateModifiedUtf8Hash(hash, (c != '.') ? c : '/');
+    }
+  }
+  hash = UpdateModifiedUtf8Hash(hash, ';');
+  return hash;
+}
+
 // TODO: Move this to java_lang_Class.cc?
 ArtMethod* Class::GetDeclaredConstructor(
     Thread* self, Handle<ObjectArray<Class>> args, PointerSize pointer_size) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 7b967ac..90efce5 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1208,6 +1208,8 @@
 
   bool DescriptorEquals(const char* match) REQUIRES_SHARED(Locks::mutator_lock_);
 
+  uint32_t DescriptorHash() REQUIRES_SHARED(Locks::mutator_lock_);
+
   const dex::ClassDef* GetClassDef() REQUIRES_SHARED(Locks::mutator_lock_);
 
   ALWAYS_INLINE uint32_t NumDirectInterfaces() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -1393,6 +1395,9 @@
   ALWAYS_INLINE uint32_t GetDirectMethodsStartOffset() REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool ProxyDescriptorEquals(const char* match) REQUIRES_SHARED(Locks::mutator_lock_);
+  static uint32_t UpdateHashForProxyClass(uint32_t hash, ObjPtr<mirror::Class> proxy_class)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
 
   template<VerifyObjectFlags kVerifyFlags>
   void GetAccessFlagsDCheck() REQUIRES_SHARED(Locks::mutator_lock_);