Harmonize interface desugaring class processing

Bug: 183998768
Change-Id: Ib632a635cfc20d1c4675a3aa7ee2691655f60666
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 7bf8f2f..9d9a502 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMember;
 import com.android.tools.r8.graph.DexClassAndMethod;
@@ -22,7 +23,6 @@
 import com.android.tools.r8.graph.LibraryMethod;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult;
 import com.android.tools.r8.ir.synthetic.ExceptionThrowingSourceCode;
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
@@ -55,7 +55,7 @@
  * In other words, the traversal is in top-down (edges from type to its subtypes) topological order.
  * The traversal is lazy, starting from the unordered set of program classes.
  */
-final class ClassProcessor {
+final class ClassProcessor implements InterfaceDesugaringProcessor {
 
   // Collection for method signatures that may cause forwarding methods to be created.
   private static class MethodSignatures {
@@ -336,7 +336,6 @@
   private final AppView<?> appView;
   private final DexItemFactory dexItemFactory;
   private final InterfaceMethodRewriter rewriter;
-  private final Consumer<ProgramMethod> newSynthesizedMethodConsumer;
   private final MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
   private final boolean needsLibraryInfo;
 
@@ -353,14 +352,10 @@
   private final Map<DexProgramClass, ProgramMethodSet> newSyntheticMethods =
       new IdentityHashMap<>();
 
-  ClassProcessor(
-      AppView<?> appView,
-      InterfaceMethodRewriter rewriter,
-      Consumer<ProgramMethod> newSynthesizedMethodConsumer) {
+  ClassProcessor(AppView<?> appView, InterfaceMethodRewriter rewriter) {
     this.appView = appView;
     this.dexItemFactory = appView.dexItemFactory();
     this.rewriter = rewriter;
-    this.newSynthesizedMethodConsumer = newSynthesizedMethodConsumer;
     needsLibraryInfo =
         !appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface().isEmpty()
             || !appView
@@ -378,15 +373,25 @@
     return !needsLibraryInfo;
   }
 
-  public void processClass(DexProgramClass clazz) {
+  @Override
+  public void process(DexProgramClass clazz, ProgramMethodSet synthesizedMethods) {
     visitClassInfo(clazz, new ReportingContext(clazz, clazz));
   }
 
-  final void addSyntheticMethods() {
+  @Override
+  public boolean shouldProcess(DexProgramClass clazz) {
+    return !clazz.isInterface();
+  }
+
+  // We introduce forwarding methods only once all desugaring has been performed to avoid
+  // confusing the look-up with inserted forwarding methods.
+  @Override
+  public final void finalizeProcessing(
+      DexApplication.Builder<?> builder, ProgramMethodSet synthesizedMethods) {
     newSyntheticMethods.forEach(
         (clazz, newForwardingMethods) -> {
           clazz.addVirtualMethods(newForwardingMethods.toDefinitionSet());
-          newForwardingMethods.forEach(newSynthesizedMethodConsumer);
+          newForwardingMethods.forEach(synthesizedMethods::add);
         });
   }
 
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 b786f7b..2f06a5e 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
@@ -33,7 +33,7 @@
 import java.util.Map;
 import java.util.Set;
 
-public final class EmulatedInterfaceProcessor {
+public final class EmulatedInterfaceProcessor implements InterfaceDesugaringProcessor {
   private final AppView<?> appView;
   private final InterfaceMethodRewriter rewriter;
   private final Map<DexType, DexType> emulatedInterfaces;
@@ -268,6 +268,23 @@
     return false;
   }
 
+  @Override
+  public boolean shouldProcess(DexProgramClass clazz) {
+    return appView.options().isDesugaredLibraryCompilation()
+        && rewriter.isEmulatedInterface(clazz.type);
+  }
+
+  @Override
+  public void process(DexProgramClass clazz, ProgramMethodSet synthesizedMethods) {
+    // TODO(b/183998768): Due to sequential dependencies we cannot generateEmulateInterfaceLibrary
+    // and replaceInterfacesInEmulatedInterface here. We need to merge this into a single loop.
+    // Uncomment these two lines instead of running them separately.
+    // generateEmulateInterfaceLibrary(clazz);
+    // replaceInterfacesInEmulatedInterface(clazz);
+    renameEmulatedInterface(clazz);
+  }
+
+  @Override
   public void finalizeProcessing(
       DexApplication.Builder<?> builder, ProgramMethodSet synthesizedMethods) {
     warnMissingEmulatedInterfaces();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringProcessor.java
new file mode 100644
index 0000000..d85e8a2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringProcessor.java
@@ -0,0 +1,17 @@
+// 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.itf;
+
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
+
+public interface InterfaceDesugaringProcessor {
+  boolean shouldProcess(DexProgramClass clazz);
+
+  void process(DexProgramClass clazz, ProgramMethodSet synthesizedMethods);
+
+  void finalizeProcessing(DexApplication.Builder<?> builder, ProgramMethodSet synthesizedMethods);
+}
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 c0e00a3..3ce00a2 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
@@ -62,7 +62,6 @@
 import com.android.tools.r8.ir.conversion.MethodProcessor;
 import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
 import com.android.tools.r8.ir.desugar.itf.DefaultMethodsHelper.Collection;
-import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor.InterfaceProcessorNestedGraphLens;
 import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations;
 import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.MethodSynthesizerConsumer;
 import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.UtilityMethodForCodeOptimizations;
@@ -205,7 +204,7 @@
     }
   }
 
-  public void addRewritePrefix(DexType interfaceType, String rewrittenType) {
+  void addRewritePrefix(DexType interfaceType, String rewrittenType) {
     appView.rewritePrefix.rewriteType(
         getCompanionClassType(interfaceType),
         factory.createType(
@@ -217,7 +216,7 @@
                 rewrittenType + EMULATE_LIBRARY_CLASS_NAME_SUFFIX)));
   }
 
-  public boolean isEmulatedInterface(DexType itf) {
+  boolean isEmulatedInterface(DexType itf) {
     return emulatedInterfaces.containsKey(itf);
   }
 
@@ -769,7 +768,7 @@
     return false;
   }
 
-  public DexMethod emulateInterfaceLibraryMethod(DexClassAndMethod method) {
+  DexMethod emulateInterfaceLibraryMethod(DexClassAndMethod method) {
     return factory.createMethod(
         getEmulateLibraryInterfaceClassType(method.getHolderType(), factory),
         factory.prependTypeToProto(method.getHolderType(), method.getProto()),
@@ -782,7 +781,7 @@
         + ";";
   }
 
-  public static DexType getEmulateLibraryInterfaceClassType(DexType type, DexItemFactory factory) {
+  static DexType getEmulateLibraryInterfaceClassType(DexType type, DexItemFactory factory) {
     assert type.isClassType();
     String descriptor = type.descriptor.toString();
     String elTypeDescriptor = getEmulateLibraryInterfaceClassDescriptor(descriptor);
@@ -846,7 +845,7 @@
   }
 
   // Represent a static interface method as a method of companion class.
-  public final DexMethod staticAsMethodOfCompanionClass(DexClassAndMethod method) {
+  final DexMethod staticAsMethodOfCompanionClass(DexClassAndMethod method) {
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     DexType companionClassType = getCompanionClassType(method.getHolderType(), dexItemFactory);
     DexMethod rewritten = method.getReference().withHolder(companionClassType, dexItemFactory);
@@ -958,14 +957,18 @@
   public void desugarInterfaceMethods(
       Builder<?> builder, Flavor flavour, ExecutorService executorService)
       throws ExecutionException {
+    // TODO(b/183998768): Merge the following five sequential loops into a single one.
+
     EmulatedInterfaceProcessor emulatedInterfaceProcessor =
         new EmulatedInterfaceProcessor(appView, this);
+    // TODO(b/183998768): Move this to emulatedInterfaceProcessor#process
     forEachProgramEmulatedInterface(emulatedInterfaceProcessor::generateEmulateInterfaceLibrary);
 
     // Process all classes first. Add missing forwarding methods to
     // replace desugared default interface methods.
-    processClasses(builder, flavour, synthesizedMethods::add);
+    process(new ClassProcessor(appView, this), builder, flavour);
 
+    // TODO(b/183998768): Move this to emulatedInterfaceProcessor#process
     forEachProgramEmulatedInterface(
         emulatedInterfaceProcessor::replaceInterfacesInEmulatedInterface);
 
@@ -973,26 +976,11 @@
     // methods to companion class, copy default interface methods to companion classes,
     // make original default methods abstract, remove bridge methods, create dispatch
     // classes if needed.
-    AppInfo appInfo = appView.appInfo();
-    InterfaceProcessorNestedGraphLens.Builder graphLensBuilder =
-        InterfaceProcessorNestedGraphLens.builder();
-    Map<DexClass, DexProgramClass> classMapping =
-        processInterfaces(builder, flavour, graphLensBuilder, synthesizedMethods::add);
-    InterfaceProcessorNestedGraphLens graphLens = graphLensBuilder.build(appView);
-    if (appView.enableWholeProgramOptimizations() && graphLens != null) {
-      appView.setGraphLens(graphLens);
-    }
-    classMapping.forEach(
-        (interfaceClass, synthesizedClass) -> {
-          // Don't need to optimize synthesized class since all of its methods
-          // are just moved from interfaces and don't need to be re-processed.
-          builder.addSynthesizedClass(synthesizedClass);
-          appInfo.addSynthesizedClass(synthesizedClass, interfaceClass.asProgramClass());
-        });
-    new InterfaceMethodRewriterFixup(appView, graphLens).run();
+    process(new InterfaceProcessor(appView, this), builder, flavour);
 
-    forEachProgramEmulatedInterface(emulatedInterfaceProcessor::renameEmulatedInterface);
-    emulatedInterfaceProcessor.finalizeProcessing(builder, synthesizedMethods);
+    // During L8 compilation, emulated interfaces are processed to be renamed, to have
+    // their interfaces fixed-up and to generate the emulated dispatch code.
+    process(emulatedInterfaceProcessor, builder, flavour);
 
     converter.processMethodsConcurrently(synthesizedMethods, executorService);
 
@@ -1017,41 +1005,20 @@
     this.synthesizedMethods.clear();
   }
 
-  private static boolean shouldProcess(
-      DexProgramClass clazz, Flavor flavour, boolean mustBeInterface) {
-    return (!clazz.originatesFromDexResource() || flavour == Flavor.IncludeAllResources)
-        && clazz.isInterface() == mustBeInterface;
+  private boolean shouldProcess(DexProgramClass clazz, Flavor flavour) {
+    if (appView.isAlreadyLibraryDesugared(clazz)) {
+      return false;
+    }
+    return (!clazz.originatesFromDexResource() || flavour == Flavor.IncludeAllResources);
   }
 
-  private Map<DexClass, DexProgramClass> processInterfaces(
-      Builder<?> builder,
-      Flavor flavour,
-      InterfaceProcessorNestedGraphLens.Builder graphLensBuilder,
-      Consumer<ProgramMethod> newSynthesizedMethodConsumer) {
-    InterfaceProcessor processor = new InterfaceProcessor(appView, this);
+  private void process(InterfaceDesugaringProcessor processor, Builder<?> builder, Flavor flavour) {
     for (DexProgramClass clazz : builder.getProgramClasses()) {
-      if (shouldProcess(clazz, flavour, true)) {
-        processor.process(clazz, graphLensBuilder, newSynthesizedMethodConsumer);
+      if (shouldProcess(clazz, flavour) && processor.shouldProcess(clazz)) {
+        processor.process(clazz, synthesizedMethods);
       }
     }
-    return processor.syntheticClasses;
-  }
-
-  private void processClasses(
-      Builder<?> builder, Flavor flavour, Consumer<ProgramMethod> newSynthesizedMethodConsumer) {
-    ClassProcessor processor = new ClassProcessor(appView, this, newSynthesizedMethodConsumer);
-    // First we compute all desugaring *without* introducing forwarding methods.
-    assert appView.getSyntheticItems().verifyNonLegacySyntheticsAreCommitted();
-    for (DexProgramClass clazz : builder.getProgramClasses()) {
-      if (shouldProcess(clazz, flavour, false)) {
-        if (appView.isAlreadyLibraryDesugared(clazz)) {
-          continue;
-        }
-        processor.processClass(clazz);
-      }
-    }
-    // Then we introduce forwarding methods.
-    processor.addSyntheticMethods();
+    processor.finalizeProcessing(builder, synthesizedMethods);
   }
 
   final boolean isDefaultMethod(DexEncodedMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index 4caff1a..32c8c64 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.graph.ClassAccessFlags;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexAnnotationSet;
+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;
@@ -50,6 +51,7 @@
 import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
 import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
 import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
@@ -60,7 +62,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.Consumer;
 import org.objectweb.asm.Opcodes;
 
 // Default and static method interface desugaring processor for interfaces.
@@ -69,23 +70,28 @@
 // a companion class. Removes bridge default methods.
 //
 // Also moves static interface methods into a companion class.
-public final class InterfaceProcessor {
+public final class InterfaceProcessor implements InterfaceDesugaringProcessor {
 
   private final AppView<?> appView;
   private final InterfaceMethodRewriter rewriter;
+  private final InterfaceProcessorNestedGraphLens.Builder graphLensBuilder;
 
-  // All created companion and dispatch classes indexed by interface type.
+  // All created companion classes indexed by interface type.
   final Map<DexClass, DexProgramClass> syntheticClasses = new IdentityHashMap<>();
 
   InterfaceProcessor(AppView<?> appView, InterfaceMethodRewriter rewriter) {
     this.appView = appView;
     this.rewriter = rewriter;
+    graphLensBuilder = InterfaceProcessorNestedGraphLens.builder();
   }
 
-  void process(
-      DexProgramClass iface,
-      InterfaceProcessorNestedGraphLens.Builder graphLensBuilder,
-      Consumer<ProgramMethod> newSynthesizedMethodConsumer) {
+  @Override
+  public boolean shouldProcess(DexProgramClass clazz) {
+    return clazz.isInterface();
+  }
+
+  @Override
+  public void process(DexProgramClass iface, ProgramMethodSet synthesizedMethods) {
     assert iface.isInterface();
     // The list of methods to be created in companion class.
     List<DexEncodedMethod> companionMethods = new ArrayList<>();
@@ -137,7 +143,7 @@
             getChecksumSupplier(iface));
     syntheticClasses.put(iface, companionClass);
     if (companionClass.hasClassInitializer()) {
-      newSynthesizedMethodConsumer.accept(companionClass.getProgramClassInitializer());
+      synthesizedMethods.add(companionClass.getProgramClassInitializer());
     }
   }
 
@@ -438,6 +444,22 @@
         && !rewriter.factory.isClassConstructor(method.getReference());
   }
 
+  @Override
+  public void finalizeProcessing(Builder<?> builder, ProgramMethodSet synthesizedMethods) {
+    InterfaceProcessorNestedGraphLens graphLens = graphLensBuilder.build(appView);
+    if (appView.enableWholeProgramOptimizations() && graphLens != null) {
+      appView.setGraphLens(graphLens);
+    }
+    syntheticClasses.forEach(
+        (interfaceClass, synthesizedClass) -> {
+          // Don't need to optimize synthesized class since all of its methods
+          // are just moved from interfaces and don't need to be re-processed.
+          builder.addSynthesizedClass(synthesizedClass);
+          appView.appInfo().addSynthesizedClass(synthesizedClass, interfaceClass.asProgramClass());
+        });
+    new InterfaceMethodRewriterFixup(appView, graphLens).run();
+  }
+
   // Specific lens which remaps invocation types to static since all rewrites performed here
   // are to static companion methods.
   public static class InterfaceProcessorNestedGraphLens extends NestedGraphLens {