Synthesize companion classpath classes expected to be present

Change-Id: Icfbea5d176adba2fa6d0c1b1b19d30d34bdcf86b
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 1df9806..dd88661 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1003,6 +1003,10 @@
   }
 
   public CfCode buildEmptyThrowingCfCode() {
+    return buildEmptyThrowingCfCode(method);
+  }
+
+  public static CfCode buildEmptyThrowingCfCode(DexMethod method) {
     CfInstruction insn[] = {new CfConstNull(), new CfThrow()};
     return new CfCode(
         method.holder,
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 1d66acd..618ff5b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -212,6 +212,15 @@
     return Iterables.transform(directMethods(predicate), method -> new ProgramMethod(this, method));
   }
 
+  public Iterable<ProgramMethod> virtualProgramMethods() {
+    return Iterables.transform(virtualMethods(), method -> new ProgramMethod(this, method));
+  }
+
+  public Iterable<ProgramMethod> virtualProgramMethods(Predicate<DexEncodedMethod> predicate) {
+    return Iterables.transform(
+        virtualMethods(predicate), method -> new ProgramMethod(this, method));
+  }
+
   public Iterable<ProgramMethod> programInstanceInitializers() {
     return directProgramMethods(DexEncodedMethod::isInstanceInitializer);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 8682669..bd6d488 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -23,6 +23,7 @@
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -35,7 +36,10 @@
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.graph.GenericSignature;
 import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
 import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.MethodCollection;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -63,6 +67,7 @@
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.shaking.MainDexClasses;
 import com.android.tools.r8.synthesis.SyntheticNaming;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.IterableUtils;
@@ -355,16 +360,16 @@
           getMethodOrigin(context.getReference()));
     }
 
-    DexEncodedMethod directTarget = clazz.lookupMethod(method);
+    DexClassAndMethod directTarget = clazz.lookupClassMethod(method);
     if (directTarget != null) {
       // This can be a private instance method call. Note that the referenced
       // method is expected to be in the current class since it is private, but desugaring
       // may move some methods or their code into other classes.
       instructions.replaceCurrentInstruction(
           new InvokeStatic(
-              directTarget.isPrivateMethod()
-                  ? privateAsMethodOfCompanionClass(method)
-                  : defaultAsMethodOfCompanionClass(method),
+              directTarget.getDefinition().isPrivateMethod()
+                  ? privateAsMethodOfCompanionClass(directTarget)
+                  : defaultAsMethodOfCompanionClass(directTarget),
               invoke.outValue(),
               invoke.arguments()));
     } else {
@@ -375,7 +380,7 @@
         // This is a invoke-direct call to a virtual method.
         instructions.replaceCurrentInstruction(
             new InvokeStatic(
-                defaultAsMethodOfCompanionClass(virtualTarget.getDefinition().method),
+                defaultAsMethodOfCompanionClass(virtualTarget),
                 invoke.outValue(),
                 invoke.arguments()));
       } else {
@@ -490,6 +495,7 @@
         UtilityMethodsForCodeOptimizations.synthesizeThrowNoSuchMethodErrorMethod(
             appView, context, methodProcessingId);
     throwNoSuchMethodErrorMethod.optimize(methodProcessor);
+
     InvokeStatic throwNoSuchMethodErrorInvoke =
         InvokeStatic.builder()
             .setMethod(throwNoSuchMethodErrorMethod.getMethod())
@@ -533,7 +539,7 @@
       DexMethod amendedMethod = amendDefaultMethod(context.getHolder(), invokedMethod);
       instructions.replaceCurrentInstruction(
           new InvokeStatic(
-              defaultAsMethodOfCompanionClass(amendedMethod),
+              defaultAsMethodOfCompanionClass(amendedMethod, appView.dexItemFactory()),
               invoke.outValue(),
               invoke.arguments()));
     } else {
@@ -547,7 +553,7 @@
             if (holder.isLibraryClass() && holder.isInterface()) {
               instructions.replaceCurrentInstruction(
                   new InvokeStatic(
-                      defaultAsMethodOfCompanionClass(target.getReference(), factory),
+                      defaultAsMethodOfCompanionClass(target),
                       invoke.outValue(),
                       invoke.arguments()));
             }
@@ -567,9 +573,7 @@
           DexMethod retargetMethod =
               options.desugaredLibraryConfiguration.retargetMethod(superTarget, appView);
           if (retargetMethod == null) {
-            DexMethod originalCompanionMethod =
-                instanceAsMethodOfCompanionClass(
-                    superTarget.getReference(), DEFAULT_METHOD_PREFIX, factory);
+            DexMethod originalCompanionMethod = defaultAsMethodOfCompanionClass(superTarget);
             DexMethod companionMethod =
                 factory.createMethod(
                     getCompanionClassType(emulatedItf),
@@ -955,12 +959,11 @@
 
   // Represent a static interface method as a method of companion class.
   final DexMethod staticAsMethodOfCompanionClass(DexClassAndMethod method) {
-    return staticAsMethodOfCompanionClass(method.getReference());
-  }
-
-  final DexMethod staticAsMethodOfCompanionClass(DexMethod method) {
-    // No changes for static methods.
-    return factory.createMethod(getCompanionClassType(method.holder), method.proto, method.name);
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
+    DexType companionClassType = getCompanionClassType(method.getHolderType(), dexItemFactory);
+    DexMethod rewritten = method.getReference().withHolder(companionClassType, dexItemFactory);
+    recordCompanionClassReference(appView, method, rewritten);
+    return rewritten;
   }
 
   private static DexMethod instanceAsMethodOfCompanionClass(
@@ -993,12 +996,11 @@
     return instanceAsMethodOfCompanionClass(method, DEFAULT_METHOD_PREFIX, factory);
   }
 
-  DexMethod defaultAsMethodOfCompanionClass(DexMethod method) {
-    return defaultAsMethodOfCompanionClass(method, factory);
-  }
-
-  DexMethod defaultAsMethodOfCompanionClass(DexClassAndMethod method) {
-    return defaultAsMethodOfCompanionClass(method.getReference(), factory);
+  final DexMethod defaultAsMethodOfCompanionClass(DexClassAndMethod method) {
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
+    DexMethod rewritten = defaultAsMethodOfCompanionClass(method.getReference(), dexItemFactory);
+    recordCompanionClassReference(appView, method, rewritten);
+    return rewritten;
   }
 
   // Represent a private instance interface method as a method of companion class.
@@ -1007,8 +1009,57 @@
     return instanceAsMethodOfCompanionClass(method, PRIVATE_METHOD_PREFIX, factory);
   }
 
-  DexMethod privateAsMethodOfCompanionClass(DexMethod method) {
-    return privateAsMethodOfCompanionClass(method, factory);
+  private DexMethod privateAsMethodOfCompanionClass(DexClassAndMethod method) {
+    return privateAsMethodOfCompanionClass(method.getReference(), factory);
+  }
+
+  private static void recordCompanionClassReference(
+      AppView<?> appView, DexClassAndMethod method, DexMethod rewritten) {
+    // If the interface class is a program class, we shouldn't need to synthesize the companion
+    // class on the classpath.
+    if (method.getHolder().isProgramClass()) {
+      return;
+    }
+
+    // If the companion class does not exist, then synthesize it on the classpath.
+    DexClass companionClass = appView.definitionFor(rewritten.getHolderType());
+    if (companionClass == null) {
+      companionClass =
+          synthesizeEmptyCompanionClass(appView, rewritten.getHolderType(), method.getHolder());
+    }
+
+    // If the companion class is a classpath class, then synthesize the companion class method if it
+    // does not exist.
+    if (companionClass.isClasspathClass()) {
+      synthetizeCompanionClassMethodIfNotPresent(companionClass.asClasspathClass(), rewritten);
+    }
+  }
+
+  private static DexClasspathClass synthesizeEmptyCompanionClass(
+      AppView<?> appView, DexType type, DexClass context) {
+    return appView
+        .getSyntheticItems()
+        .createClasspathClass(
+            SyntheticKind.COMPANION_CLASS, type, context, appView.dexItemFactory());
+  }
+
+  private static void synthetizeCompanionClassMethodIfNotPresent(
+      DexClasspathClass companionClass, DexMethod method) {
+    MethodCollection methodCollection = companionClass.getMethodCollection();
+    synchronized (methodCollection) {
+      if (methodCollection.getMethod(method) == null) {
+        boolean d8R8Synthesized = true;
+        methodCollection.addDirectMethod(
+            new DexEncodedMethod(
+                method,
+                MethodAccessFlags.createPublicStaticSynthetic(),
+                MethodTypeSignature.noSignature(),
+                DexAnnotationSet.empty(),
+                ParameterAnnotationsList.empty(),
+                DexEncodedMethod.buildEmptyThrowingCfCode(method),
+                d8R8Synthesized));
+      }
+    }
   }
 
   private void renameEmulatedInterfaces() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index c2db31a..e40251c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -220,23 +220,28 @@
       List<DexEncodedMethod> companionMethods,
       InterfaceProcessorNestedGraphLens.Builder graphLensBuilder) {
     List<DexEncodedMethod> newVirtualMethods = new ArrayList<>();
-    for (DexEncodedMethod virtual : iface.virtualMethods()) {
+    for (ProgramMethod method : iface.virtualProgramMethods()) {
+      DexEncodedMethod virtual = method.getDefinition();
       if (rewriter.isDefaultMethod(virtual)) {
         if (!canMoveToCompanionClass(virtual)) {
-          throw new CompilationError("One or more instruction is preventing default interface "
-              + "method from being desugared: " + virtual.method.toSourceString(), iface.origin);
+          throw new CompilationError(
+              "One or more instruction is preventing default interface "
+                  + "method from being desugared: "
+                  + method.toSourceString(),
+              iface.origin);
         }
 
         // Create a new method in a companion class to represent default method implementation.
-        DexMethod companionMethod = rewriter.defaultAsMethodOfCompanionClass(virtual.method);
+        DexMethod companionMethod = rewriter.defaultAsMethodOfCompanionClass(method);
 
         Code code = virtual.getCode();
         if (code == null) {
-          throw new CompilationError("Code is missing for default "
-              + "interface method: " + virtual.method.toSourceString(), iface.origin);
+          throw new CompilationError(
+              "Code is missing for default " + "interface method: " + method.toSourceString(),
+              iface.origin);
         }
 
-        MethodAccessFlags newFlags = virtual.accessFlags.copy();
+        MethodAccessFlags newFlags = method.getAccessFlags().copy();
         newFlags.promoteToStatic();
         DexEncodedMethod.setDebugInfoWithFakeThisParameter(
             code, companionMethod.getArity(), appView);
@@ -252,7 +257,7 @@
         implMethod.copyMetadata(virtual);
         virtual.setDefaultInterfaceMethodImplementation(implMethod);
         companionMethods.add(implMethod);
-        graphLensBuilder.recordCodeMovedToCompanionClass(virtual.method, implMethod.method);
+        graphLensBuilder.recordCodeMovedToCompanionClass(method.getReference(), implMethod.method);
       }
 
       // Remove bridge methods.
@@ -272,12 +277,13 @@
       List<DexEncodedMethod> companionMethods,
       InterfaceProcessorNestedGraphLens.Builder graphLensBuilder) {
     DexEncodedMethod clinit = null;
-    for (DexEncodedMethod method : iface.directMethods()) {
-      if (method.isClassInitializer()) {
-        clinit = method;
+    for (ProgramMethod method : iface.directProgramMethods()) {
+      DexEncodedMethod definition = method.getDefinition();
+      if (definition.isClassInitializer()) {
+        clinit = definition;
         continue;
       }
-      if (method.isInstanceInitializer()) {
+      if (definition.isInstanceInitializer()) {
         assert false
             : "Unexpected interface instance initializer: "
                 + method.getReference().toSourceString();
@@ -291,24 +297,24 @@
       }
 
       DexMethod oldMethod = method.getReference();
-      if (isStaticMethod(method)) {
+      if (isStaticMethod(definition)) {
         assert originalFlags.isPrivate() || originalFlags.isPublic()
             : "Static interface method "
                 + method.toSourceString()
                 + " is expected to "
                 + "either be public or private in "
                 + iface.origin;
-        DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(oldMethod);
+        DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(method);
         DexEncodedMethod implMethod =
             new DexEncodedMethod(
                 companionMethod,
                 newFlags,
-                method.getGenericSignature(),
-                method.annotations(),
-                method.parameterAnnotationsList,
-                method.getCode(),
+                definition.getGenericSignature(),
+                definition.annotations(),
+                definition.parameterAnnotationsList,
+                definition.getCode(),
                 true);
-        implMethod.copyMetadata(method);
+        implMethod.copyMetadata(definition);
         companionMethods.add(implMethod);
         graphLensBuilder.move(oldMethod, companionMethod);
         continue;
@@ -318,9 +324,10 @@
 
       newFlags.promoteToStatic();
 
-      DexMethod companionMethod = rewriter.privateAsMethodOfCompanionClass(oldMethod);
+      DexMethod companionMethod =
+          rewriter.privateAsMethodOfCompanionClass(oldMethod, appView.dexItemFactory());
 
-      Code code = method.getCode();
+      Code code = definition.getCode();
       if (code == null) {
         throw new CompilationError(
             "Code is missing for private instance "
@@ -333,12 +340,12 @@
           new DexEncodedMethod(
               companionMethod,
               newFlags,
-              method.getGenericSignature(),
-              method.annotations(),
-              method.parameterAnnotationsList,
+              definition.getGenericSignature(),
+              definition.annotations(),
+              definition.parameterAnnotationsList,
               code,
               true);
-      implMethod.copyMetadata(method);
+      implMethod.copyMetadata(definition);
       companionMethods.add(implMethod);
       graphLensBuilder.move(oldMethod, companionMethod);
     }