Enum unboxing: Support hashCode

Bug: 157112269
Change-Id: I1d299cba94f6fef5837a29db5848553b71da29d2
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 1a318c0..0d0d1bd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -164,6 +164,7 @@
   public final DexString startsWithMethodName = createString("startsWith");
   public final DexString endsWithMethodName = createString("endsWith");
   public final DexString equalsMethodName = createString("equals");
+  public final DexString hashCodeMethodName = createString("hashCode");
   public final DexString equalsIgnoreCaseMethodName = createString("equalsIgnoreCase");
   public final DexString contentEqualsMethodName = createString("contentEquals");
   public final DexString indexOfMethodName = createString("indexOf");
@@ -1269,6 +1270,7 @@
     public final DexMethod toString;
     public final DexMethod compareTo;
     public final DexMethod equals;
+    public final DexMethod hashCode;
 
     public final DexMethod constructor =
         createMethod(enumType, createProto(voidType, stringType, intType), constructorMethodName);
@@ -1309,6 +1311,8 @@
               equalsMethodName,
               booleanDescriptor,
               new DexString[] {objectDescriptor});
+      hashCode =
+          createMethod(enumDescriptor, hashCodeMethodName, intDescriptor, DexString.EMPTY_ARRAY);
     }
 
     public boolean isValuesMethod(DexMethod method, DexClass enumClass) {
@@ -1495,7 +1499,7 @@
           createMethod(stringDescriptor, compareToIgnoreCaseMethodName, intDescriptor,
               needsOneString);
 
-      hashCode = createMethod(stringType, createProto(intType), "hashCode");
+      hashCode = createMethod(stringType, createProto(intType), hashCodeMethodName);
       valueOf = createMethod(
           stringDescriptor, valueOfMethodName, stringDescriptor, needsOneObject);
       toString = createMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 379fc28..3239c8c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -457,6 +457,8 @@
         return Reason.ELIGIBLE;
       } else if (singleTarget == factory.enumMethods.ordinal) {
         return Reason.ELIGIBLE;
+      } else if (singleTarget == factory.enumMethods.hashCode) {
+        return Reason.ELIGIBLE;
       } else if (singleTarget == factory.enumMethods.constructor) {
         // Enum constructor call is allowed only if first call of an enum initializer.
         if (code.method().isInstanceInitializer()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 541cd92..1832a2a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -129,7 +129,8 @@
         DexMethod invokedMethod = invokeMethod.getInvokedMethod();
         DexType enumType = getEnumTypeOrNull(invokeMethod.getReceiver(), convertedEnums);
         if (enumType != null) {
-          if (invokedMethod == factory.enumMethods.ordinal) {
+          if (invokedMethod == factory.enumMethods.ordinal
+              || invokedMethod == factory.enumMethods.hashCode) {
             replaceEnumInvoke(
                 iterator, invokeMethod, ordinalUtilityMethod, m -> synthesizeOrdinalMethod());
             continue;
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/OrdinalEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/OrdinalHashCodeEnumUnboxingTest.java
similarity index 88%
rename from src/test/java/com/android/tools/r8/enumunboxing/OrdinalEnumUnboxingTest.java
rename to src/test/java/com/android/tools/r8/enumunboxing/OrdinalHashCodeEnumUnboxingTest.java
index b388ed7..8bd41c3 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/OrdinalEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/OrdinalHashCodeEnumUnboxingTest.java
@@ -14,7 +14,7 @@
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class OrdinalEnumUnboxingTest extends EnumUnboxingTestBase {
+public class OrdinalHashCodeEnumUnboxingTest extends EnumUnboxingTestBase {
 
   private static final Class<?> ENUM_CLASS = MyEnum.class;
 
@@ -27,7 +27,7 @@
     return enumUnboxingTestParameters();
   }
 
-  public OrdinalEnumUnboxingTest(
+  public OrdinalHashCodeEnumUnboxingTest(
       TestParameters parameters, boolean enumValueOptimization, KeepRule enumKeepRules) {
     this.parameters = parameters;
     this.enumValueOptimization = enumValueOptimization;
@@ -36,7 +36,7 @@
 
   @Test
   public void testEnumUnboxing() throws Exception {
-    Class<Ordinal> classToTest = Ordinal.class;
+    Class<?> classToTest = OrdinalHashCode.class;
     R8TestRunResult run =
         testForR8(parameters.getBackend())
             .addProgramClasses(classToTest, ENUM_CLASS)
@@ -61,11 +61,13 @@
     C
   }
 
-  static class Ordinal {
+  static class OrdinalHashCode {
 
     public static void main(String[] args) {
       System.out.println(MyEnum.A.ordinal());
       System.out.println(0);
+      System.out.println(MyEnum.A.hashCode());
+      System.out.println(0);
     }
   }
 }