Filter emulated interface sub interfaces

- insert rewriting of emulated interface only once

Bug: 183998768
Change-Id: Ic991a269d7a7e2b1441a04dae66c12d8e06e5272
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
index 2f06a5e..5e200e5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
@@ -6,7 +6,7 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexApplication.Builder;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -125,8 +125,18 @@
     assert rewriter.isEmulatedInterface(emulatedInterface.type);
     DexType newType = emulatedInterfaces.get(emulatedInterface.type);
     assert newType != null;
-    rewriter.addRewritePrefix(emulatedInterface.type, newType.toString());
-    renameEmulatedInterface(emulatedInterface, newType);
+    emulatedInterface.type = newType;
+    emulatedInterface.setVirtualMethods(renameHolder(emulatedInterface.virtualMethods(), newType));
+    emulatedInterface.setDirectMethods(renameHolder(emulatedInterface.directMethods(), newType));
+  }
+
+  private DexEncodedMethod[] renameHolder(Iterable<DexEncodedMethod> methods, DexType newName) {
+    List<DexEncodedMethod> methods1 = IterableUtils.toNewArrayList(methods);
+    DexEncodedMethod[] newMethods = new DexEncodedMethod[methods1.size()];
+    for (int i = 0; i < newMethods.length; i++) {
+      newMethods[i] = methods1.get(i).toRenamedHolderMethod(newName, appView.dexItemFactory());
+    }
+    return newMethods;
   }
 
   void generateEmulateInterfaceLibrary(DexProgramClass emulatedInterface) {
@@ -285,8 +295,7 @@
   }
 
   @Override
-  public void finalizeProcessing(
-      DexApplication.Builder<?> builder, ProgramMethodSet synthesizedMethods) {
+  public void finalizeProcessing(Builder<?> builder, ProgramMethodSet synthesizedMethods) {
     warnMissingEmulatedInterfaces();
     if (!appView.options().isDesugaredLibraryCompilation()) {
       assert syntheticClasses.isEmpty();
@@ -298,67 +307,53 @@
           appView.appInfo().addSynthesizedClass(synthesizedClass, interfaceClass);
           synthesizedClass.forEachProgramMethod(synthesizedMethods::add);
         });
-    // TODO(b/183918843): renameEmulatedInterfaceHierarchies should be removed.
-    // The desugared library configuration does not include rewritings for the subtypes of
-    // emulated interfaces while they need to be rewritten.
-    // Here we iterate here over all interfaces, and if they inherit from an emulated interface,
-    // we auto-magically apply the package renaming of the emulated interface to the interface.
-    renameEmulatedInterfaceHierarchies();
+    // TODO(b/183918843): Investigate what to do for the filtering, the minimum would be to make
+    // the rewriting rule explicit instead of using the synthesized class prefix.
+    filterEmulatedInterfaceSubInterfaces(builder);
   }
 
-  private void renameEmulatedInterfaceHierarchies() {
-    for (DexClass clazz : appView.appInfo().classes()) {
-      if (clazz.isInterface() && !rewriter.isEmulatedInterface(clazz.type)) {
-        DexType newType = inferEmulatedInterfaceName(clazz);
-        if (newType != null && !appView.rewritePrefix.hasRewrittenType(clazz.type, appView)) {
-          rewriter.addRewritePrefix(clazz.type, newType.toString());
-          renameEmulatedInterface(clazz, newType);
-        }
+  private void filterEmulatedInterfaceSubInterfaces(Builder<?> builder) {
+    ArrayList<DexProgramClass> filteredProgramClasses = new ArrayList<>();
+    for (DexProgramClass clazz : builder.getProgramClasses()) {
+      if (clazz.isInterface()
+          && !rewriter.isEmulatedInterface(clazz.type)
+          && !appView.rewritePrefix.hasRewrittenType(clazz.type, appView)
+          && isEmulatedInterfaceSubInterface(clazz)) {
+        String prefix =
+            DescriptorUtils.getJavaTypeFromBinaryName(
+                appView
+                    .options()
+                    .desugaredLibraryConfiguration
+                    .getSynthesizedLibraryClassesPackagePrefix());
+        String interfaceType = clazz.type.toString();
+        // TODO(b/183918843): We are currently computing a new name for the companion class
+        // by replacing the initial package prefix by the synthesized library class package
+        // prefix, it would be better to make the rewriting explicit in the desugared library
+        // json file.
+        int firstPackage = interfaceType.indexOf('.');
+        String newName = prefix + interfaceType.substring(firstPackage + 1);
+        rewriter.addCompanionClassRewriteRule(clazz.type, newName);
+      } else {
+        filteredProgramClasses.add(clazz);
       }
     }
+    builder.replaceProgramClasses(filteredProgramClasses);
   }
 
-  private DexType inferEmulatedInterfaceName(DexClass subInterface) {
+  private boolean isEmulatedInterfaceSubInterface(DexClass subInterface) {
     assert !rewriter.isEmulatedInterface(subInterface.type);
     LinkedList<DexType> workList = new LinkedList<>(Arrays.asList(subInterface.interfaces.values));
     while (!workList.isEmpty()) {
       DexType next = workList.removeFirst();
-      if (emulatedInterfaces.get(next) != null) {
-        return inferEmulatedInterfaceName(subInterface.type, next);
+      if (rewriter.isEmulatedInterface(next)) {
+        return true;
       }
       DexClass nextClass = appView.definitionFor(next);
       if (nextClass != null) {
         workList.addAll(Arrays.asList(nextClass.interfaces.values));
       }
     }
-    return null;
-  }
-
-  private DexType inferEmulatedInterfaceName(DexType subInterfaceType, DexType interfaceType) {
-    String initialPrefix = interfaceType.getPackageName();
-    String rewrittenPrefix = emulatedInterfaces.get(interfaceType).getPackageName();
-    String suffix = subInterfaceType.toString().substring(initialPrefix.length());
-    return appView
-        .dexItemFactory()
-        .createType(DescriptorUtils.javaTypeToDescriptor(rewrittenPrefix + suffix));
-  }
-
-  private void renameEmulatedInterface(DexClass theInterface, DexType renamedInterface) {
-    theInterface.type = renamedInterface;
-    theInterface.setVirtualMethods(renameHolder(theInterface.virtualMethods(), renamedInterface));
-    theInterface.setDirectMethods(renameHolder(theInterface.directMethods(), renamedInterface));
-  }
-
-  private DexEncodedMethod[] renameHolder(Iterable<DexEncodedMethod> methods, DexType newName) {
-    return renameHolder(IterableUtils.toNewArrayList(methods), newName);
-  }
-
-  private DexEncodedMethod[] renameHolder(List<DexEncodedMethod> methods, DexType newName) {
-    DexEncodedMethod[] newMethods = new DexEncodedMethod[methods.size()];
-    for (int i = 0; i < newMethods.length; i++) {
-      newMethods[i] = methods.get(i).toRenamedHolderMethod(newName, appView.dexItemFactory());
-    }
-    return newMethods;
+    return false;
   }
 
   private void warnMissingEmulatedInterfaces() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 3ce00a2..8f8f607 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -193,7 +193,8 @@
     Map<DexType, DexType> emulateLibraryInterface =
         options.desugaredLibraryConfiguration.getEmulateLibraryInterface();
     for (DexType interfaceType : emulateLibraryInterface.keySet()) {
-      addRewritePrefix(interfaceType, emulateLibraryInterface.get(interfaceType).toSourceString());
+      addRewriteRulesForEmulatedInterface(
+          interfaceType, emulateLibraryInterface.get(interfaceType).toSourceString());
       DexClass emulatedInterfaceClass = appView.definitionFor(interfaceType);
       if (emulatedInterfaceClass != null) {
         for (DexEncodedMethod encodedMethod :
@@ -204,16 +205,21 @@
     }
   }
 
-  void addRewritePrefix(DexType interfaceType, String rewrittenType) {
+  void addRewriteRulesForEmulatedInterface(
+      DexType emulatedInterface, String rewrittenEmulatedInterface) {
+    addCompanionClassRewriteRule(emulatedInterface, rewrittenEmulatedInterface);
+    appView.rewritePrefix.rewriteType(
+        getEmulateLibraryInterfaceClassType(emulatedInterface, factory),
+        factory.createType(
+            DescriptorUtils.javaTypeToDescriptor(
+                rewrittenEmulatedInterface + EMULATE_LIBRARY_CLASS_NAME_SUFFIX)));
+  }
+
+  void addCompanionClassRewriteRule(DexType interfaceType, String rewrittenType) {
     appView.rewritePrefix.rewriteType(
         getCompanionClassType(interfaceType),
         factory.createType(
             DescriptorUtils.javaTypeToDescriptor(rewrittenType + COMPANION_CLASS_NAME_SUFFIX)));
-    appView.rewritePrefix.rewriteType(
-        getEmulateLibraryInterfaceClassType(interfaceType, factory),
-        factory.createType(
-            DescriptorUtils.javaTypeToDescriptor(
-                rewrittenType + EMULATE_LIBRARY_CLASS_NAME_SUFFIX)));
   }
 
   boolean isEmulatedInterface(DexType itf) {