Refactor building of companion class methods.

Change-Id: I107ceba12353e314afa019c235467645645148aa
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 6ba6863..2174002 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
@@ -27,6 +27,8 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexValue.DexValueInt;
 import com.android.tools.r8.graph.FieldAccessFlags;
@@ -39,8 +41,7 @@
 import com.android.tools.r8.graph.NestedGraphLens;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
-import com.android.tools.r8.synthesis.SyntheticNaming;
-import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
@@ -58,6 +59,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
 import org.objectweb.asm.Opcodes;
 
 // Default and static method interface desugaring processor for interfaces.
@@ -86,9 +88,7 @@
       return;
     }
     analyzeBridges(iface);
-    if (needsCompanionClass(iface)) {
-      ensureCompanionClass(iface, synthesizedMethods);
-    }
+    ensureCompanionClassMethods(iface, synthesizedMethods);
   }
 
   private void analyzeBridges(DexProgramClass iface) {
@@ -101,66 +101,58 @@
     }
   }
 
-  private boolean needsCompanionClass(DexProgramClass iface) {
-    if (hasStaticMethodThatTriggersNonTrivialClassInitializer(iface)) {
-      return true;
-    }
-    for (ProgramMethod method : iface.virtualProgramMethods()) {
-      DexEncodedMethod virtual = method.getDefinition();
-      if (rewriter.isDefaultMethod(virtual)) {
-        return true;
-      }
-    }
-    for (ProgramMethod method : iface.directProgramMethods()) {
-      DexEncodedMethod definition = method.getDefinition();
-      if (!definition.isInitializer()) {
-        return true;
-      }
-    }
-    return false;
+  private void ensureCompanionClassMethods(
+      DexProgramClass iface, ProgramMethodSet synthesizedMethods) {
+    ensureCompanionClassInitializesInterface(iface, synthesizedMethods);
+    // TODO(b/183998768): Once fixed, the methods should be added for processing.
+    // D8 and R8 don't need to optimize the methods since they are just moved from interfaces and
+    // don't need to be re-processed.
+    processVirtualInterfaceMethods(iface);
+    processDirectInterfaceMethods(iface);
   }
 
-  private DexProgramClass ensureCompanionClass(
-      DexProgramClass iface, ProgramMethodSet synthesizedMethods) {
-
-    DexProgramClass companionClass =
+  private ProgramMethod ensureCompanionMethod(
+      DexProgramClass iface,
+      DexString methodName,
+      DexProto methodProto,
+      Consumer<SyntheticMethodBuilder> fn) {
+    ProgramMethod method =
         appView
             .getSyntheticItems()
-            .ensureFixedClass(
-                SyntheticNaming.SyntheticKind.COMPANION_CLASS,
+            .ensureFixedClassMethod(
+                methodName,
+                methodProto,
+                SyntheticKind.COMPANION_CLASS,
                 iface,
                 appView,
-                builder -> {
-                  builder.setSourceFile(iface.sourceFile);
-                  builder.setGenericSignature(
-                      iface.getClassSignature().toObjectBoundWithSameFormals(objectTypeSignature));
-                  ensureCompanionClassInitializesInterface(iface, builder);
-                  processVirtualInterfaceMethods(iface, builder);
-                  processDirectInterfaceMethods(iface, builder);
-                });
-    assert companionClass.getType() == rewriter.getCompanionClassType(iface.type);
-    assert companionClass.hasMethods();
-
-    // D8 and R8 don't need to optimize the methods since they are just moved from interfaces and
-    // don't need to be re-processed, besides the clinit, which has just been inserted.
-    if (companionClass.hasClassInitializer()) {
-      synthesizedMethods.add(companionClass.getProgramClassInitializer());
-    }
-
-    return companionClass;
+                builder ->
+                    builder
+                        .setSourceFile(iface.sourceFile)
+                        .setGenericSignature(
+                            iface
+                                .getClassSignature()
+                                .toObjectBoundWithSameFormals(objectTypeSignature)),
+                fn);
+    assert method.getHolderType() == rewriter.getCompanionClassType(iface.type);
+    return method;
   }
 
   private void ensureCompanionClassInitializesInterface(
-      DexProgramClass iface, SyntheticProgramClassBuilder builder) {
+      DexProgramClass iface, ProgramMethodSet synthesizedMethods) {
     if (!hasStaticMethodThatTriggersNonTrivialClassInitializer(iface)) {
       return;
     }
-    DexEncodedField clinitField = ensureStaticClinitFieldToTriggerinterfaceInitialization(iface);
-    builder.addMethod(
-        methodBuilder -> createCompanionClassInitializer(iface, clinitField, methodBuilder));
+    DexEncodedField clinitField = ensureStaticClinitFieldToTriggerInterfaceInitialization(iface);
+    ProgramMethod clinit =
+        ensureCompanionMethod(
+            iface,
+            appView.dexItemFactory().classConstructorMethodName,
+            appView.dexItemFactory().createProto(appView.dexItemFactory().voidType),
+            methodBuilder -> createCompanionClassInitializer(iface, clinitField, methodBuilder));
+    synthesizedMethods.add(clinit);
   }
 
-  private DexEncodedField ensureStaticClinitFieldToTriggerinterfaceInitialization(
+  private DexEncodedField ensureStaticClinitFieldToTriggerInterfaceInitialization(
       DexProgramClass iface) {
     DexEncodedField clinitField =
         findExistingStaticClinitFieldToTriggerInterfaceInitialization(iface);
@@ -223,16 +215,13 @@
                 ImmutableList.of(),
                 ImmutableList.of());
     methodBuilder
-        .setName(appView.dexItemFactory().classConstructorMethodName)
-        .setProto(appView.dexItemFactory().createProto(appView.dexItemFactory().voidType))
         .setAccessFlags(
             MethodAccessFlags.builder().setConstructor().setPackagePrivate().setStatic().build())
         .setCode(codeGenerator)
         .setClassFileVersion(iface.getInitialClassFileVersion());
   }
 
-  private void processVirtualInterfaceMethods(
-      DexProgramClass iface, SyntheticProgramClassBuilder builder) {
+  private void processVirtualInterfaceMethods(DexProgramClass iface) {
     for (ProgramMethod method : iface.virtualProgramMethods()) {
       DexEncodedMethod virtual = method.getDefinition();
       if (rewriter.isDefaultMethod(virtual)) {
@@ -259,11 +248,12 @@
         DexEncodedMethod.setDebugInfoWithFakeThisParameter(
             code, companionMethod.getArity(), appView);
 
-        builder.addMethod(
+        ensureCompanionMethod(
+            iface,
+            companionMethod.getName(),
+            companionMethod.getProto(),
             methodBuilder ->
                 methodBuilder
-                    .setName(companionMethod.getName())
-                    .setProto(companionMethod.getProto())
                     .setAccessFlags(newFlags)
                     .setGenericSignature(MethodTypeSignature.noSignature())
                     .setAnnotations(virtual.annotations())
@@ -279,8 +269,7 @@
     }
   }
 
-  private void processDirectInterfaceMethods(
-      DexProgramClass iface, SyntheticProgramClassBuilder builder) {
+  private void processDirectInterfaceMethods(DexProgramClass iface) {
     for (ProgramMethod method : iface.directProgramMethods()) {
       DexEncodedMethod definition = method.getDefinition();
       if (definition.isClassInitializer()) {
@@ -311,11 +300,12 @@
                 + iface.origin;
         DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(method);
 
-        builder.addMethod(
+        ensureCompanionMethod(
+            iface,
+            companionMethod.getName(),
+            companionMethod.getProto(),
             methodBuilder ->
                 methodBuilder
-                    .setName(companionMethod.getName())
-                    .setProto(companionMethod.getProto())
                     .setAccessFlags(newFlags)
                     .setGenericSignature(definition.getGenericSignature())
                     .setAnnotations(definition.annotations())
@@ -349,11 +339,12 @@
 
       DexEncodedMethod.setDebugInfoWithFakeThisParameter(code, companionMethod.getArity(), appView);
 
-      builder.addMethod(
+      ensureCompanionMethod(
+          iface,
+          companionMethod.getName(),
+          companionMethod.getProto(),
           methodBuilder ->
               methodBuilder
-                  .setName(companionMethod.getName())
-                  .setProto(companionMethod.getProto())
                   .setAccessFlags(newFlags)
                   .setGenericSignature(definition.getGenericSignature())
                   .setAnnotations(definition.annotations())
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index e1f4b12..450f844 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -12,10 +12,13 @@
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
 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.graph.GraphLens.NonIdentityGraphLens;
 import com.android.tools.r8.graph.ProgramDefinition;
@@ -535,6 +538,33 @@
     }
   }
 
+  public ProgramMethod ensureFixedClassMethod(
+      DexString name,
+      DexProto proto,
+      SyntheticKind kind,
+      DexProgramClass context,
+      AppView<?> appView,
+      Consumer<SyntheticProgramClassBuilder> buildClassCallback,
+      Consumer<SyntheticMethodBuilder> buildMethodCallback) {
+    DexProgramClass clazz = ensureFixedClass(kind, context, appView, buildClassCallback);
+    DexMethod methodReference = appView.dexItemFactory().createMethod(clazz.getType(), proto, name);
+    DexEncodedMethod methodDefinition = clazz.getMethodCollection().getMethod(methodReference);
+    if (methodDefinition != null) {
+      return new ProgramMethod(clazz, methodDefinition);
+    }
+    // TODO(b/183998768): Make this thread safe and safe to use for recursive definitions.
+    SyntheticMethodBuilder builder =
+        new SyntheticMethodBuilder(appView.dexItemFactory(), clazz.getType());
+    builder.setName(name);
+    builder.setProto(proto);
+    buildMethodCallback.accept(builder);
+    DexEncodedMethod method = builder.build();
+    assert method.getName() == name;
+    assert method.getProto() == proto;
+    clazz.addMethod(method);
+    return new ProgramMethod(clazz, method);
+  }
+
   public DexClasspathClass createFixedClasspathClass(
       SyntheticKind kind, DexClasspathClass context, DexItemFactory factory) {
     // Obtain the outer synthesizing context in the case the context itself is synthetic.