Ensure deterministic output from generating keep rules in CodeToKeep

Change-Id: I17dc9ded018c6ba5716021dc71a314f1cdffc9bb
diff --git a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
index 7595a40..177e303 100644
--- a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
+++ b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
@@ -8,12 +8,15 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.CollectionUtils;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.Sets;
+import java.util.Comparator;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -155,7 +158,7 @@
       // TODO(b/134734081): Stream the consumer instead of building the String.
       StringBuilder sb = new StringBuilder();
       String cr = System.lineSeparator();
-      for (DexType type : toKeep.keySet()) {
+      for (DexType type : CollectionUtils.sort(toKeep.keySet(), getComparator())) {
         KeepStruct keepStruct = toKeep.get(type);
         sb.append("-keep class ").append(convertType(type));
         if (keepStruct.all) {
@@ -167,7 +170,7 @@
           continue;
         }
         sb.append(" {").append(cr);
-        for (DexField field : keepStruct.fields) {
+        for (DexField field : CollectionUtils.sort(keepStruct.fields, getComparator())) {
           sb.append("    ")
               .append(convertType(field.type))
               .append(" ")
@@ -175,7 +178,7 @@
               .append(";")
               .append(cr);
         }
-        for (DexMethod method : keepStruct.methods) {
+        for (DexMethod method : CollectionUtils.sort(keepStruct.methods, getComparator())) {
           sb.append("    ")
               .append(convertType(method.proto.returnType))
               .append(" ")
@@ -194,6 +197,15 @@
       options.desugaredLibraryKeepRuleConsumer.accept(sb.toString(), options.reporter);
       options.desugaredLibraryKeepRuleConsumer.finished(options.reporter);
     }
+
+    private static <T extends DexReference> Comparator<T> getComparator() {
+      return new Comparator<T>() {
+        @Override
+        public int compare(T o1, T o2) {
+          return o1.compareTo(o2);
+        }
+      };
+    }
   }
 
   public static class NopCodeToKeep extends CodeToKeep {
diff --git a/src/main/java/com/android/tools/r8/utils/CollectionUtils.java b/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
index 91b0638..6af263f 100644
--- a/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
@@ -5,7 +5,9 @@
 package com.android.tools.r8.utils;
 
 import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.Set;
 import java.util.function.Consumer;
 
@@ -24,4 +26,10 @@
       collection.forEach(consumer);
     }
   }
+
+  public static <T> Collection<T> sort(Collection<T> items, Comparator<T> comparator) {
+    ArrayList<T> ts = new ArrayList<>(items);
+    ts.sort(comparator);
+    return ts;
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
index aeb159d..ee66aab 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
@@ -188,31 +188,31 @@
   private void assertGeneratedKeepRulesAreCorrect(String keepRules) {
     String expectedResult =
         StringUtils.lines(
-            "-keep class j$.util.List$-EL {",
-            "    void sort(java.util.List, java.util.Comparator);",
-            "}",
             "-keep class j$.util.Collection$-EL {",
             "    j$.util.stream.Stream stream(java.util.Collection);",
             "}",
-            "-keep class j$.util.stream.IntStream$-CC {",
-            "    j$.util.stream.IntStream range(int, int);",
-            "}",
             "-keep class j$.util.Comparator$-CC {",
             "    java.util.Comparator comparingInt(j$.util.function.ToIntFunction);",
             "}",
+            "-keep class j$.util.DesugarArrays {",
+            "    j$.util.Spliterator spliterator(java.lang.Object[]);",
+            "    j$.util.Spliterator spliterator(java.lang.Object[], int, int);",
+            "    j$.util.stream.Stream stream(java.lang.Object[]);",
+            "    j$.util.stream.Stream stream(java.lang.Object[], int, int);",
+            "}",
+            "-keep class j$.util.List$-EL {",
+            "    void sort(java.util.List, java.util.Comparator);",
+            "}",
             "-keep class j$.util.Set$-EL {",
             "    j$.util.Spliterator spliterator(java.util.Set);",
             "}",
-            "-keep class j$.util.DesugarArrays {",
-            "    j$.util.Spliterator spliterator(java.lang.Object[]);",
-            "    j$.util.stream.Stream stream(java.lang.Object[], int, int);",
-            "    j$.util.stream.Stream stream(java.lang.Object[]);",
-            "    j$.util.Spliterator spliterator(java.lang.Object[], int, int);",
+            "-keep class j$.util.Spliterator",
+            "-keep class j$.util.function.ToIntFunction { *; }",
+            "-keep class j$.util.stream.IntStream$-CC {",
+            "    j$.util.stream.IntStream range(int, int);",
             "}",
             "-keep class j$.util.stream.IntStream",
-            "-keep class j$.util.stream.Stream",
-            "-keep class j$.util.Spliterator",
-            "-keep class j$.util.function.ToIntFunction { *; }");
+            "-keep class j$.util.stream.Stream");
     assertEquals(expectedResult, keepRules);
   }
 }