[vm] Change Symbol CanonicalizeHash to hashCode

Aligns the CanonicalizeHash for Symbol with it's hashCode in Dart.

Motivation for aligning hashCode and CanonicalizeHash in
go/dart-vm-const-maps.
Bug: https://github.com/dart-lang/sdk/issues/45908

TEST=runtime/vm/object_test.cc

Change-Id: I228405364c930d81a269fd87cbc59cf95e97c649
Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-debug-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/206260
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Tess Strickland <sstrickl@google.com>
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 7ebeb28..1903854 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -18902,6 +18902,49 @@
   return true;
 }
 
+static ClassPtr EnsureSymbolClass(Thread* thread) {
+  ObjectStore* const store = thread->isolate_group()->object_store();
+
+  if (store->symbol_class() != Class::null()) {
+    return store->symbol_class();
+  }
+  Zone* const zone = thread->zone();
+  const auto& library = Library::Handle(zone, Library::InternalLibrary());
+  const auto& symbol_class =
+      Class::Handle(zone, library.LookupClass(Symbols::Symbol()));
+  ASSERT(!symbol_class.IsNull());
+  store->set_symbol_class(symbol_class);
+  return symbol_class.ptr();
+}
+
+bool Symbol::IsSymbolCid(classid_t class_id) {
+  Thread* const thread = Thread::Current();
+  Zone* const zone = thread->zone();
+
+  Class& symbol_class = Class::Handle(zone, EnsureSymbolClass(thread));
+
+  return class_id == symbol_class.id();
+}
+
+// Must be kept in sync with Symbol.hashCode in symbol_patch.dart
+uint32_t Symbol::CanonicalizeHash(const Instance& instance) {
+  ASSERT(IsSymbolCid(instance.GetClassId()));
+
+  Thread* const thread = Thread::Current();
+  Zone* const zone = thread->zone();
+
+  Class& symbol_class = Class::Handle(zone, EnsureSymbolClass(thread));
+  const auto& symbol_name_field = Field::Handle(
+      zone, symbol_class.LookupInstanceFieldAllowPrivate(Symbols::_name()));
+  ASSERT(!symbol_name_field.IsNull());
+
+  // Keep in sync with sdk/lib/_internal/vm/lib/symbol_patch.dart.
+  const auto& name =
+      String::Cast(Object::Handle(zone, instance.GetField(symbol_name_field)));
+  const uint32_t arbitrary_prime = 664597;
+  return 0x1fffffff & (arbitrary_prime * name.CanonicalizeHash());
+}
+
 uint32_t Instance::CanonicalizeHash() const {
   if (GetClassId() == kNullCid) {
     return 2011;  // Matches null_patch.dart.
@@ -18914,41 +18957,46 @@
   Zone* zone = thread->zone();
   const Class& cls = Class::Handle(zone, clazz());
   NoSafepointScope no_safepoint(thread);
-  const intptr_t instance_size = SizeFromClass();
-  ASSERT(instance_size != 0);
-  hash = instance_size / kCompressedWordSize;
-  uword this_addr = reinterpret_cast<uword>(this->untag());
-  Object& obj = Object::Handle(zone);
-  Instance& instance = Instance::Handle(zone);
 
-  const auto unboxed_fields_bitmap =
-      thread->isolate_group()->shared_class_table()->GetUnboxedFieldsMapAt(
-          GetClassId());
+  if (Symbol::IsSymbolCid(GetClassId())) {
+    hash = Symbol::CanonicalizeHash(*this);
+  } else {
+    const intptr_t instance_size = SizeFromClass();
+    ASSERT(instance_size != 0);
+    hash = instance_size / kCompressedWordSize;
+    uword this_addr = reinterpret_cast<uword>(this->untag());
+    Object& obj = Object::Handle(zone);
+    Instance& instance = Instance::Handle(zone);
 
-  for (intptr_t offset = Instance::NextFieldOffset();
-       offset < cls.host_next_field_offset(); offset += kCompressedWordSize) {
-    if (unboxed_fields_bitmap.Get(offset / kCompressedWordSize)) {
-      if (kCompressedWordSize == 8) {
-        hash = CombineHashes(hash,
-                             *reinterpret_cast<uint32_t*>(this_addr + offset));
-        hash = CombineHashes(
-            hash, *reinterpret_cast<uint32_t*>(this_addr + offset + 4));
+    const auto unboxed_fields_bitmap =
+        thread->isolate_group()->shared_class_table()->GetUnboxedFieldsMapAt(
+            GetClassId());
+
+    for (intptr_t offset = Instance::NextFieldOffset();
+         offset < cls.host_next_field_offset(); offset += kCompressedWordSize) {
+      if (unboxed_fields_bitmap.Get(offset / kCompressedWordSize)) {
+        if (kCompressedWordSize == 8) {
+          hash = CombineHashes(
+              hash, *reinterpret_cast<uint32_t*>(this_addr + offset));
+          hash = CombineHashes(
+              hash, *reinterpret_cast<uint32_t*>(this_addr + offset + 4));
+        } else {
+          hash = CombineHashes(
+              hash, *reinterpret_cast<uint32_t*>(this_addr + offset));
+        }
       } else {
-        hash = CombineHashes(hash,
-                             *reinterpret_cast<uint32_t*>(this_addr + offset));
-      }
-    } else {
-      obj = reinterpret_cast<CompressedObjectPtr*>(this_addr + offset)
-                ->Decompress(untag()->heap_base());
-      if (obj.IsSentinel()) {
-        hash = CombineHashes(hash, 11);
-      } else {
-        instance ^= obj.ptr();
-        hash = CombineHashes(hash, instance.CanonicalizeHash());
+        obj = reinterpret_cast<CompressedObjectPtr*>(this_addr + offset)
+                  ->Decompress(untag()->heap_base());
+        if (obj.IsSentinel()) {
+          hash = CombineHashes(hash, 11);
+        } else {
+          instance ^= obj.ptr();
+          hash = CombineHashes(hash, instance.CanonicalizeHash());
+        }
       }
     }
+    hash = FinalizeHash(hash, String::kHashBits);
   }
-  hash = FinalizeHash(hash, String::kHashBits);
   thread->heap()->SetCanonicalHash(ptr(), hash);
   return hash;
 }
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 3b80f79..718d4ca 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -9120,6 +9120,14 @@
   friend class Number;
 };
 
+// TODO(http://dartbug.com/46716): Recognize Symbol in the VM.
+class Symbol : public AllStatic {
+ public:
+  static bool IsSymbolCid(classid_t class_id);
+
+  static uint32_t CanonicalizeHash(const Instance& instance);
+};
+
 // String may not be '\0' terminated.
 class String : public Instance {
  public:
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 0bce1aa..dae798f 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -4874,8 +4874,7 @@
     const char* value_script,
     uint32_t hashcode_canonicalize_vm = kCalculateCanonizalizeHash,
     bool check_identity = true,
-    bool check_hashcode = true,
-    bool print_failure = true) {
+    bool check_hashcode = true) {
   auto kScriptChars = Utils::CStringUniquePtr(
       OS::SCreate(nullptr,
                   "%s"
@@ -4929,7 +4928,7 @@
     success &= identity_hashcode_dart == hashcode_canonicalize_vm;
   }
 
-  if (!success && print_failure) {
+  if (!success) {
     LogBlock lb;
     THR_Print(
         "Dart hashCode or Dart identityHashCode does not equal VM "
@@ -4991,6 +4990,15 @@
   EXPECT(HashCodeEqualsCanonicalizeHash(kScript));
 }
 
+TEST_CASE(HashCode_Symbol) {
+  const char* kScript =
+      "value() {\n"
+      "  return #A;\n"
+      "}\n";
+  EXPECT(HashCodeEqualsCanonicalizeHash(kScript, kCalculateCanonizalizeHash,
+                                        /*check_identity=*/false));
+}
+
 TEST_CASE(HashCode_True) {
   const char* kScript =
       "value() {\n"
diff --git a/sdk/lib/_internal/vm/lib/symbol_patch.dart b/sdk/lib/_internal/vm/lib/symbol_patch.dart
index 06eb588..eeb9e0b 100644
--- a/sdk/lib/_internal/vm/lib/symbol_patch.dart
+++ b/sdk/lib/_internal/vm/lib/symbol_patch.dart
@@ -6,6 +6,7 @@
 
 @patch
 class Symbol {
+  // TODO(http://dartbug.com/46716): Recognize Symbol in the VM.
   @patch
   const Symbol(String name) : this._name = name;
 
@@ -52,6 +53,7 @@
     return result.toString();
   }
 
+  // Must be kept in sync with Symbol::CanonicalizeHash in object.cc.
   @patch
   int get hashCode {
     const arbitraryPrime = 664597;
diff --git a/sdk/lib/internal/symbol.dart b/sdk/lib/internal/symbol.dart
index 718ac68..2ccb071 100644
--- a/sdk/lib/internal/symbol.dart
+++ b/sdk/lib/internal/symbol.dart
@@ -12,7 +12,9 @@
  * make it accessible to Dart platform code via the static method
  * [getName].
  */
+@pragma('vm:entry-point')
 class Symbol implements core.Symbol {
+  @pragma('vm:entry-point')
   final String _name;
 
   /**