Introduce deduplicate flags for Human flags

- Move as many common flags in the common flags section

Bug: 184026720
Change-Id: Iea63747191e0a1ec933946f1d46042d8c9afd76b
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
index 8adb9b7..bf87cd8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
@@ -176,14 +176,17 @@
       this.retargetCoreLibMember = new IdentityHashMap<>(retargetCoreLibMember);
       this.backportCoreLibraryMember = new IdentityHashMap<>(backportCoreLibraryMember);
       this.customConversions = new IdentityHashMap<>(customConversions);
-      this.dontRewriteInvocation = dontRewriteInvocation;
-      this.dontRetargetLibMember = dontRetargetLibMember;
-      this.wrapperConversions = wrapperConversions;
+      this.dontRewriteInvocation = Sets.newIdentityHashSet();
+      this.dontRewriteInvocation.addAll(dontRewriteInvocation);
+      this.dontRetargetLibMember = Sets.newIdentityHashSet();
+      this.dontRetargetLibMember.addAll(dontRetargetLibMember);
+      this.wrapperConversions = Sets.newIdentityHashSet();
+      this.wrapperConversions.addAll(wrapperConversions);
     }
 
-    // Utility to set values. Currently assumes the key is fresh.
+    // Utility to set values.
     private <K, V> void put(Map<K, V> map, K key, V value, String desc) {
-      if (map.containsKey(key)) {
+      if (map.containsKey(key) && !map.get(key).equals(value)) {
         throw reporter.fatalError(
             new StringDiagnostic(
                 "Invalid desugared library configuration. "
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecification.java
index 6ea4fd1..2ade60c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecification.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification;
 
+import com.android.tools.r8.origin.Origin;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import java.util.Map;
 
@@ -52,4 +53,9 @@
   public Map<Integer, HumanRewritingFlags> getProgramFlagsForTesting() {
     return programFlags;
   }
+
+  public Origin getOrigin() {
+    // TODO(b/184026720): Maintain the origin.
+    return Origin.unknown();
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
new file mode 100644
index 0000000..18a0ed2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
@@ -0,0 +1,171 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification;
+
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.Reporter;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.IntArraySet;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+public class MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator {
+
+  public static Origin createOrigin(String source) {
+    return new Origin(Origin.root()) {
+      @Override
+      public String part() {
+        return source;
+      }
+    };
+  }
+
+  public static void deduplicateFlags(
+      MultiAPILevelHumanDesugaredLibrarySpecification specification,
+      DexItemFactory factory,
+      Reporter reporter) {
+
+    IntArraySet apis = new IntArraySet();
+    apis.addAll(specification.getCommonFlags().keySet());
+    apis.addAll(specification.getLibraryFlags().keySet());
+    apis.addAll(specification.getProgramFlags().keySet());
+
+    for (Integer api : apis) {
+      deduplicateFlags(api, specification, factory, reporter);
+    }
+  }
+
+  private static void deduplicateFlags(
+      int api,
+      MultiAPILevelHumanDesugaredLibrarySpecification specification,
+      DexItemFactory factory,
+      Reporter reporter) {
+
+    Int2ObjectMap<HumanRewritingFlags> commonFlags = specification.getCommonFlags();
+    Int2ObjectMap<HumanRewritingFlags> libraryFlags = specification.getLibraryFlags();
+    Int2ObjectMap<HumanRewritingFlags> programFlags = specification.getProgramFlags();
+
+    HumanRewritingFlags library = libraryFlags.get(api);
+    HumanRewritingFlags program = programFlags.get(api);
+
+    if (library == null || program == null) {
+      return;
+    }
+
+    Origin origin = specification.getOrigin();
+    HumanRewritingFlags.Builder commonBuilder =
+        commonFlags.get(api) == null
+            ? HumanRewritingFlags.builder(factory, reporter, origin)
+            : commonFlags.get(api).newBuilder(factory, reporter, origin);
+    HumanRewritingFlags.Builder libraryBuilder =
+        HumanRewritingFlags.builder(factory, reporter, origin);
+    HumanRewritingFlags.Builder programBuilder =
+        HumanRewritingFlags.builder(factory, reporter, origin);
+
+    // Iterate over all library/program flags, add them in common if also in the other, else add
+    // them to library/program.
+    deduplicateFlags(library, program, commonBuilder, libraryBuilder);
+    deduplicateFlags(program, library, commonBuilder, programBuilder);
+
+    commonFlags.put(api, commonBuilder.build());
+    libraryFlags.put(api, libraryBuilder.build());
+    programFlags.put(api, programBuilder.build());
+  }
+
+  private static void deduplicateFlags(
+      HumanRewritingFlags flags,
+      HumanRewritingFlags otherFlags,
+      HumanRewritingFlags.Builder commonBuilder,
+      HumanRewritingFlags.Builder builder) {
+    deduplicateRewritePrefix(flags, otherFlags, commonBuilder, builder);
+
+    deduplicateFlags(
+        flags.getEmulateLibraryInterface(),
+        otherFlags.getEmulateLibraryInterface(),
+        commonBuilder::putEmulateLibraryInterface,
+        builder::putEmulateLibraryInterface);
+    deduplicateFlags(
+        flags.getRetargetCoreLibMember(),
+        otherFlags.getRetargetCoreLibMember(),
+        commonBuilder::putRetargetCoreLibMember,
+        builder::putRetargetCoreLibMember);
+    deduplicateFlags(
+        flags.getBackportCoreLibraryMember(),
+        otherFlags.getBackportCoreLibraryMember(),
+        commonBuilder::putBackportCoreLibraryMember,
+        builder::putBackportCoreLibraryMember);
+    deduplicateFlags(
+        flags.getCustomConversions(),
+        otherFlags.getCustomConversions(),
+        commonBuilder::putCustomConversion,
+        builder::putCustomConversion);
+
+    deduplicateFlags(
+        flags.getDontRewriteInvocation(),
+        otherFlags.getDontRewriteInvocation(),
+        commonBuilder::addDontRewriteInvocation,
+        builder::addDontRewriteInvocation);
+    deduplicateFlags(
+        flags.getDontRetargetLibMember(),
+        otherFlags.getDontRetargetLibMember(),
+        commonBuilder::addDontRetargetLibMember,
+        builder::addDontRetargetLibMember);
+    deduplicateFlags(
+        flags.getWrapperConversions(),
+        otherFlags.getWrapperConversions(),
+        commonBuilder::addWrapperConversion,
+        builder::addWrapperConversion);
+  }
+
+  private static void deduplicateRewritePrefix(
+      HumanRewritingFlags flags,
+      HumanRewritingFlags otherFlags,
+      HumanRewritingFlags.Builder commonBuilder,
+      HumanRewritingFlags.Builder builder) {
+    flags
+        .getRewritePrefix()
+        .forEach(
+            (k, v) -> {
+              if (otherFlags.getRewritePrefix().get(k) != null
+                  && otherFlags.getRewritePrefix().get(k).equals(v)) {
+                commonBuilder.putRewritePrefix(k, v);
+              } else {
+                builder.putRewritePrefix(k, v);
+              }
+            });
+  }
+
+  private static <T extends DexItem> void deduplicateFlags(
+      Map<T, DexType> flags,
+      Map<T, DexType> otherFlags,
+      BiConsumer<T, DexType> common,
+      BiConsumer<T, DexType> specific) {
+    flags.forEach(
+        (k, v) -> {
+          if (otherFlags.get(k) == v) {
+            common.accept(k, v);
+          } else {
+            specific.accept(k, v);
+          }
+        });
+  }
+
+  private static <T extends DexItem> void deduplicateFlags(
+      Set<T> flags, Set<T> otherFlags, Consumer<T> common, Consumer<T> specific) {
+    flags.forEach(
+        e -> {
+          if (otherFlags.contains(e)) {
+            common.accept(e);
+          } else {
+            specific.accept(e);
+          }
+        });
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
index 6e26eff..ebdc9a1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanTopLevelFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.MultiAPILevelHumanDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyRewritingFlags;
@@ -55,6 +56,8 @@
             .parseMultiLevelConfiguration(inputSpecification);
     MultiAPILevelHumanDesugaredLibrarySpecification humanSpec =
         convertAllAPILevels(legacySpec, androidLib, options);
+    MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.deduplicateFlags(
+        humanSpec, options.dexItemFactory(), options.reporter);
     MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.export(humanSpec, output);
   }
 
@@ -94,7 +97,8 @@
 
   private void legacyLibraryFlagHacks(
       Int2ObjectArrayMap<HumanRewritingFlags> libraryFlags, DexApplication app) {
-    HumanRewritingFlags humanRewritingFlags = libraryFlags.get(AndroidApiLevel.N_MR1.getLevel());
+    int level = AndroidApiLevel.N_MR1.getLevel();
+    HumanRewritingFlags humanRewritingFlags = libraryFlags.get(level);
     HumanRewritingFlags.Builder builder =
         humanRewritingFlags.newBuilder(
             app.dexItemFactory(), app.options.reporter, Origin.unknown());
@@ -122,7 +126,7 @@
     target = itemFactory.createType("Ljava/util/DesugarTimeZone;");
     builder.putRetargetCoreLibMember(source, target);
 
-    libraryFlags.put(25, builder.build());
+    libraryFlags.put(level, builder.build());
   }
 
   private DirectMappedDexApplication readApp(Path androidLib, InternalOptions options)