Companion class in new synthetic infrastructure

but without hygienic name...

Bug: 183998768
Change-Id: I9387b5a3830111ef5d21f74651900a78448a1cae
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 206a3ed..1dcfe8a 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
@@ -17,7 +17,6 @@
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
-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;
@@ -28,21 +27,18 @@
 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.DexProgramClass.ChecksumSupplier;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DexValue.DexValueInt;
 import com.android.tools.r8.graph.FieldAccessFlags;
-import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.MethodCollection;
 import com.android.tools.r8.graph.NestedGraphLens;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.origin.SynthesizedOrigin;
+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.utils.Pair;
 import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
@@ -53,7 +49,6 @@
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Deque;
 import java.util.HashSet;
 import java.util.IdentityHashMap;
@@ -76,9 +71,6 @@
   private final Map<DexProgramClass, PostProcessingInterfaceInfo> postProcessingInterfaceInfos =
       new ConcurrentHashMap<>();
 
-  // All created companion classes indexed by interface type.
-  final Map<DexClass, DexProgramClass> syntheticClasses = new ConcurrentHashMap<>();
-
   InterfaceProcessor(AppView<?> appView, InterfaceMethodRewriter rewriter) {
     this.appView = appView;
     this.rewriter = rewriter;
@@ -91,7 +83,7 @@
     }
     analyzeBridges(iface);
     if (needsCompanionClass(iface)) {
-      synthesizeCompanionClass(iface, synthesizedMethods);
+      ensureCompanionClass(iface, synthesizedMethods);
     }
   }
 
@@ -124,72 +116,53 @@
     return false;
   }
 
-  private void synthesizeCompanionClass(
+  private DexProgramClass ensureCompanionClass(
       DexProgramClass iface, ProgramMethodSet synthesizedMethods) {
-    // The list of methods to be created in companion class.
-    List<DexEncodedMethod> companionMethods = new ArrayList<>();
 
-    ensureCompanionClassInitializesInterface(iface, companionMethods);
-
-    // Process virtual interface methods first.
-    processVirtualInterfaceMethods(iface, companionMethods);
-
-    // Process static and private methods, move them into companion class as well,
-    // make private instance methods public static.
-    processDirectInterfaceMethods(iface, companionMethods);
-
-    assert !companionMethods.isEmpty();
-
-    ClassAccessFlags companionClassFlags = iface.accessFlags.copy();
-    companionClassFlags.unsetAbstract();
-    companionClassFlags.unsetInterface();
-    companionClassFlags.unsetAnnotation();
-    companionClassFlags.setFinal();
-    companionClassFlags.setSynthetic();
-    // Companion class must be public so moved methods can be called from anywhere.
-    companionClassFlags.setPublic();
-
-    // Create companion class.
-    DexType companionClassType = rewriter.getCompanionClassType(iface.type);
     DexProgramClass companionClass =
-        new DexProgramClass(
-            companionClassType,
-            null,
-            new SynthesizedOrigin("interface desugaring", getClass()),
-            companionClassFlags,
-            rewriter.factory.objectType,
-            DexTypeList.empty(),
-            iface.sourceFile,
-            null,
-            Collections.emptyList(),
-            null,
-            Collections.emptyList(),
-            ClassSignature.noSignature(),
-            DexAnnotationSet.empty(),
-            DexEncodedField.EMPTY_ARRAY,
-            DexEncodedField.EMPTY_ARRAY,
-            companionMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
-            DexEncodedMethod.EMPTY_ARRAY,
-            rewriter.factory.getSkipNameValidationForTesting(),
-            getChecksumSupplier(iface));
-    syntheticClasses.put(iface, companionClass);
+        appView
+            .getSyntheticItems()
+            .ensureFixedClassWhileMigrating(
+                SyntheticNaming.SyntheticKind.COMPANION_CLASS,
+                rewriter.getCompanionClassType(iface.type),
+                iface,
+                appView,
+                builder -> {
+                  ensureCompanionClassInitializesInterface(iface, builder);
+                  processVirtualInterfaceMethods(iface, builder);
+                  processDirectInterfaceMethods(iface, builder);
+                });
+
+    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;
   }
 
   private void ensureCompanionClassInitializesInterface(
-      DexProgramClass iface, List<DexEncodedMethod> companionMethods) {
+      DexProgramClass iface, SyntheticProgramClassBuilder builder) {
     if (!hasStaticMethodThatTriggersNonTrivialClassInitializer(iface)) {
       return;
     }
+    DexEncodedField clinitField = ensureStaticClinitFieldToTriggerinterfaceInitialization(iface);
+    builder.addMethod(
+        methodBuilder -> createCompanionClassInitializer(iface, clinitField, methodBuilder));
+  }
+
+  private DexEncodedField ensureStaticClinitFieldToTriggerinterfaceInitialization(
+      DexProgramClass iface) {
     DexEncodedField clinitField =
         findExistingStaticClinitFieldToTriggerInterfaceInitialization(iface);
     if (clinitField == null) {
       clinitField = createStaticClinitFieldToTriggerInterfaceInitialization(iface);
       iface.appendStaticField(clinitField);
     }
-    companionMethods.add(createCompanionClassInitializer(iface, clinitField));
+    return clinitField;
   }
 
   private boolean hasStaticMethodThatTriggersNonTrivialClassInitializer(DexProgramClass iface) {
@@ -226,35 +199,32 @@
         DexValueInt.DEFAULT);
   }
 
-  private DexEncodedMethod createCompanionClassInitializer(
-      DexProgramClass iface, DexEncodedField clinitField) {
-    DexType companionType = rewriter.getCompanionClassType(iface.getType());
-    DexMethod clinitMethodReference = appView.dexItemFactory().createClinitMethod(companionType);
-    CfCode code =
-        new CfCode(
-            companionType,
-            1,
-            0,
-            ImmutableList.of(
-                new CfFieldInstruction(
-                    Opcodes.GETSTATIC, clinitField.getReference(), clinitField.getReference()),
-                new CfStackInstruction(Opcode.Pop),
-                new CfReturnVoid()),
-            ImmutableList.of(),
-            ImmutableList.of());
-    return new DexEncodedMethod(
-        clinitMethodReference,
-        MethodAccessFlags.builder().setConstructor().setPackagePrivate().setStatic().build(),
-        MethodTypeSignature.noSignature(),
-        DexAnnotationSet.empty(),
-        ParameterAnnotationsList.empty(),
-        code,
-        true,
-        iface.getInitialClassFileVersion());
+  private void createCompanionClassInitializer(
+      DexProgramClass iface, DexEncodedField clinitField, SyntheticMethodBuilder methodBuilder) {
+    SyntheticMethodBuilder.SyntheticCodeGenerator codeGenerator =
+        method ->
+            new CfCode(
+                method.holder,
+                1,
+                0,
+                ImmutableList.of(
+                    new CfFieldInstruction(
+                        Opcodes.GETSTATIC, clinitField.getReference(), clinitField.getReference()),
+                    new CfStackInstruction(Opcode.Pop),
+                    new CfReturnVoid()),
+                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, List<DexEncodedMethod> companionMethods) {
+      DexProgramClass iface, SyntheticProgramClassBuilder builder) {
     for (ProgramMethod method : iface.virtualProgramMethods()) {
       DexEncodedMethod virtual = method.getDefinition();
       if (rewriter.isDefaultMethod(virtual)) {
@@ -280,25 +250,29 @@
         newFlags.promoteToStatic();
         DexEncodedMethod.setDebugInfoWithFakeThisParameter(
             code, companionMethod.getArity(), appView);
-        DexEncodedMethod implMethod =
-            new DexEncodedMethod(
-                companionMethod,
-                newFlags,
-                virtual.getGenericSignature(),
-                virtual.annotations(),
-                virtual.parameterAnnotationsList,
-                code,
-                true);
-        implMethod.copyMetadata(virtual);
-        companionMethods.add(implMethod);
-        getPostProcessingInterfaceInfo(iface)
-            .mapDefaultMethodToCompanionMethod(virtual, implMethod);
+
+        builder.addMethod(
+            methodBuilder ->
+                methodBuilder
+                    .setName(companionMethod.getName())
+                    .setProto(companionMethod.getProto())
+                    .setAccessFlags(newFlags)
+                    .setGenericSignature(virtual.getGenericSignature())
+                    .setAnnotations(virtual.annotations())
+                    .setParameterAnnotationsList(virtual.getParameterAnnotations())
+                    .setCode(ignored -> virtual.getCode())
+                    .setOnBuildConsumer(
+                        implMethod -> {
+                          implMethod.copyMetadata(virtual);
+                          getPostProcessingInterfaceInfo(iface)
+                              .mapDefaultMethodToCompanionMethod(virtual, implMethod);
+                        }));
       }
     }
   }
 
   private void processDirectInterfaceMethods(
-      DexProgramClass iface, List<DexEncodedMethod> companionMethods) {
+      DexProgramClass iface, SyntheticProgramClassBuilder builder) {
     for (ProgramMethod method : iface.directProgramMethods()) {
       DexEncodedMethod definition = method.getDefinition();
       if (definition.isClassInitializer()) {
@@ -328,17 +302,22 @@
                 + "either be public or private in "
                 + iface.origin;
         DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(method);
-        DexEncodedMethod implMethod =
-            new DexEncodedMethod(
-                companionMethod,
-                newFlags,
-                definition.getGenericSignature(),
-                definition.annotations(),
-                definition.parameterAnnotationsList,
-                definition.getCode(),
-                true);
-        implMethod.copyMetadata(definition);
-        companionMethods.add(implMethod);
+
+        builder.addMethod(
+            methodBuilder ->
+                methodBuilder
+                    .setName(companionMethod.getName())
+                    .setProto(companionMethod.getProto())
+                    .setAccessFlags(newFlags)
+                    .setGenericSignature(definition.getGenericSignature())
+                    .setAnnotations(definition.annotations())
+                    .setParameterAnnotationsList(definition.getParameterAnnotations())
+                    .setCode(ignored -> definition.getCode())
+                    .setOnBuildConsumer(
+                        implMethod -> {
+                          implMethod.copyMetadata(definition);
+                        }));
+
         getPostProcessingInterfaceInfo(iface).moveMethod(oldMethod, companionMethod);
         continue;
       }
@@ -359,19 +338,25 @@
                 + oldMethod.toSourceString(),
             iface.origin);
       }
+
       DexEncodedMethod.setDebugInfoWithFakeThisParameter(code, companionMethod.getArity(), appView);
-      DexEncodedMethod implMethod =
-          new DexEncodedMethod(
-              companionMethod,
-              newFlags,
-              definition.getGenericSignature(),
-              definition.annotations(),
-              definition.parameterAnnotationsList,
-              code,
-              true);
-      implMethod.copyMetadata(definition);
-      companionMethods.add(implMethod);
-      getPostProcessingInterfaceInfo(iface).moveMethod(oldMethod, companionMethod);
+
+      builder.addMethod(
+          methodBuilder ->
+              methodBuilder
+                  .setName(companionMethod.getName())
+                  .setProto(companionMethod.getProto())
+                  .setAccessFlags(newFlags)
+                  .setGenericSignature(definition.getGenericSignature())
+                  .setAnnotations(definition.annotations())
+                  .setParameterAnnotationsList(definition.getParameterAnnotations())
+                  .setCode(ignored -> definition.getCode())
+                  .setOnBuildConsumer(
+                      implMethod -> {
+                        implMethod.copyMetadata(definition);
+                        getPostProcessingInterfaceInfo(iface)
+                            .moveMethod(oldMethod, companionMethod);
+                      }));
     }
   }
 
@@ -385,14 +370,6 @@
     }
   }
 
-  private ChecksumSupplier getChecksumSupplier(DexProgramClass iface) {
-    if (!appView.options().encodeChecksums) {
-      return DexProgramClass::invalidChecksumRequest;
-    }
-    long checksum = iface.getChecksum();
-    return c -> 7 * checksum;
-  }
-
   private boolean canMoveToCompanionClass(DexEncodedMethod method) {
     Code code = method.getCode();
     assert code != null;
@@ -519,13 +496,6 @@
     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();
   }
 
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
index 487cb53..6522872 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ParameterAnnotationsList;
+import java.util.function.Consumer;
 
 public class SyntheticMethodBuilder {
 
@@ -29,6 +30,10 @@
   private CfVersion classFileVersion;
   private SyntheticCodeGenerator codeGenerator = null;
   private MethodAccessFlags accessFlags = null;
+  private MethodTypeSignature genericSignature = MethodTypeSignature.noSignature();
+  private DexAnnotationSet annotations = DexAnnotationSet.empty();
+  private ParameterAnnotationsList parameterAnnotationsList = ParameterAnnotationsList.empty();
+  private Consumer<DexEncodedMethod> onBuildConsumer = null;
 
   SyntheticMethodBuilder(SyntheticClassBuilder<?, ?> parent) {
     this.factory = parent.getFactory();
@@ -71,6 +76,27 @@
     return this;
   }
 
+  public SyntheticMethodBuilder setGenericSignature(MethodTypeSignature genericSignature) {
+    this.genericSignature = genericSignature;
+    return this;
+  }
+
+  public SyntheticMethodBuilder setAnnotations(DexAnnotationSet annotations) {
+    this.annotations = annotations;
+    return this;
+  }
+
+  public SyntheticMethodBuilder setParameterAnnotationsList(
+      ParameterAnnotationsList parameterAnnotationsList) {
+    this.parameterAnnotationsList = parameterAnnotationsList;
+    return this;
+  }
+
+  public SyntheticMethodBuilder setOnBuildConsumer(Consumer<DexEncodedMethod> onBuildConsumer) {
+    this.onBuildConsumer = onBuildConsumer;
+    return this;
+  }
+
   DexEncodedMethod build() {
     assert name != null;
     boolean isCompilerSynthesized = true;
@@ -79,13 +105,16 @@
         new DexEncodedMethod(
             methodSignature,
             getAccessFlags(),
-            MethodTypeSignature.noSignature(),
-            DexAnnotationSet.empty(),
-            ParameterAnnotationsList.empty(),
+            genericSignature,
+            annotations,
+            parameterAnnotationsList,
             getCodeObject(methodSignature),
             isCompilerSynthesized,
             classFileVersion);
     assert isValidSyntheticMethod(method);
+    if (onBuildConsumer != null) {
+      onBuildConsumer.accept(method);
+    }
     return method;
   }
 
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index cb1e91b..7bac324 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -24,7 +24,7 @@
   public enum SyntheticKind {
     // Class synthetics.
     RECORD_TAG("", false, true, true),
-    COMPANION_CLASS("CompanionClass", false),
+    COMPANION_CLASS("-CC", false, true),
     LAMBDA("Lambda", false),
     INIT_TYPE_ARGUMENT("-IA", false, true),
     HORIZONTAL_INIT_TYPE_ARGUMENT_1(SYNTHETIC_CLASS_SEPARATOR + "IA$1", false, true),