Reland "Make merging more resilient to backported names from old versions of R8""

Bug: 146419367
Change-Id: I0e09897fa01efd368cb965f32123eaa9d9af1970
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index cc39610..bbed9bc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -24,13 +24,19 @@
 import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
 import com.android.tools.r8.utils.Pair;
 import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
 import java.util.function.Predicate;
 
 public class DexType extends DexReference implements PresortedComparable<DexType> {
   public static final DexType[] EMPTY_ARRAY = {};
 
+  // Bundletool is merging classes that may originate from a build with an old version of R8.
+  // Allow merging of classes that use names from older versions of R8.
+  private static List<String> OLD_SYNTHESIZED_NAMES = ImmutableList.of("$r8$java8methods$utility");
+
   public final DexString descriptor;
   private String toStringCache = null;
 
@@ -264,7 +270,17 @@
         || name.contains(TwrCloseResourceRewriter.UTILITY_CLASS_NAME)
         || name.contains(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME)
         || name.contains(BackportedMethodRewriter.UTILITY_CLASS_NAME_PREFIX)
-        || name.contains(ServiceLoaderRewriter.SERVICE_LOADER_CLASS_NAME);
+        || name.contains(ServiceLoaderRewriter.SERVICE_LOADER_CLASS_NAME)
+        || oldSynthesizedName(name);
+  }
+
+  private boolean oldSynthesizedName(String name) {
+    for (String synthesizedPrefix : OLD_SYNTHESIZED_NAMES) {
+      if (name.contains(synthesizedPrefix)) {
+        return true;
+      }
+    }
+    return false;
   }
 
   public boolean isProgramType(DexDefinitionSupplier definitions) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index fa68de4..8f33a5e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -62,6 +62,8 @@
 
 public final class BackportedMethodRewriter {
 
+  // Don't change this name, at least not without adding special-casing in DexType to support
+  // merging old dex code in Bundletool.
   public static final String UTILITY_CLASS_NAME_PREFIX = "$r8$backportedMethods$utility";
 
   private final AppView<?> appView;
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
index 44da91c..d602a51 100644
--- a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
@@ -10,12 +10,7 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
-import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
-import com.android.tools.r8.ir.desugar.LambdaRewriter;
-import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
-import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
 import com.android.tools.r8.references.Reference;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
@@ -90,17 +85,13 @@
 
   private static DexProgramClass mergeClasses(
       Reporter reporter, DexProgramClass a, DexProgramClass b) {
-    if (LambdaRewriter.hasLambdaClassPrefix(a.type)
-        || BackportedMethodRewriter.hasRewrittenMethodPrefix(a.type)
-        || InterfaceMethodRewriter.hasDispatchClassSuffix(a.type)
-        || NestBasedAccessDesugaring.isNestConstructor(a.type)
-        || TwrCloseResourceRewriter.isUtilityClassDescriptor(a.type)) {
-      assert assertEqualClasses(a, b);
-      return a;
-    }
     if (DesugaredLibraryWrapperSynthesizer.isSynthesizedWrapper(a.type)) {
       return mergeWrappers(a, b);
     }
+    if (a.type.isD8R8SynthesizedClassType()) {
+      assert assertEqualClasses(a, b);
+      return a;
+    }
     throw reportDuplicateTypes(reporter, a, b);
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugar/BackportedMethodMergeTest.java b/src/test/java/com/android/tools/r8/desugar/BackportedMethodMergeTest.java
index 44f32ee..f71139f 100644
--- a/src/test/java/com/android/tools/r8/desugar/BackportedMethodMergeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/BackportedMethodMergeTest.java
@@ -4,9 +4,14 @@
 
 package com.android.tools.r8.desugar;
 
+import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.transformers.ClassTransformer;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import java.io.IOException;
 import java.nio.file.Path;
+import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 
 public class BackportedMethodMergeTest extends TestBase {
@@ -40,6 +45,61 @@
         .assertSuccessWithOutput(jvmOutput);
   }
 
+  @Test
+  public void testMergeOldPrefix()
+      throws IOException, CompilationFailedException, ExecutionException {
+    byte[] transform = transformer($r8$java8methods$utility_MergeInputWithOldBackportedPrefix.class)
+        .addClassTransformer(new ClassTransformer() {
+          @Override
+          public void visit(int version, int access, String name, String signature,
+              String superName, String[] interfaces) {
+            ClassAccessFlags accessFlags = ClassAccessFlags.fromCfAccessFlags(access);
+            accessFlags.setSynthetic();
+            super.visit(version, accessFlags.getAsCfAccessFlags(),
+                name, signature, superName, interfaces);
+          }
+        }).transform();
+
+    Path zip1 = temp.newFile("first.zip").toPath();
+    Path zip2 = temp.newFile("second.zip").toPath();
+    testForD8()
+        .setMinApi(AndroidApiLevel.L)
+        .addProgramClasses(MergeRunWithOldBackportedPrefix.class)
+        .addProgramClassFileData(transform)
+        .compile()
+        .assertNoMessages()
+        .writeToZip(zip1);
+    testForD8()
+        .setMinApi(AndroidApiLevel.L)
+        .addProgramClassFileData(transform)
+        .compile()
+        .assertNoMessages()
+        .writeToZip(zip2);
+    testForD8()
+        .addProgramFiles(zip1, zip2)
+        .setMinApi(AndroidApiLevel.L)
+        .compile()
+        .assertNoMessages()
+        .run(MergeRunWithOldBackportedPrefix.class)
+        .assertSuccessWithOutputLines("foobar");
+  }
+
+
+  static class $r8$java8methods$utility_MergeInputWithOldBackportedPrefix {
+    public void foo() {
+      System.out.println("foobar");
+    }
+
+  }
+
+  static class MergeRunWithOldBackportedPrefix {
+    public static void main(String[] args) {
+      $r8$java8methods$utility_MergeInputWithOldBackportedPrefix a =
+          new $r8$java8methods$utility_MergeInputWithOldBackportedPrefix();
+      a.foo();
+    }
+  }
+
   static class MergeInputA {
     public void foo() {
       System.out.println(Integer.hashCode(42));