Ensure static methods while desugaring.

Bug: 183998768
Change-Id: Ia2e1ebb7c2bc51c27d040549781af2cf167bea45
diff --git a/src/main/java/com/android/tools/r8/graph/InvalidCode.java b/src/main/java/com/android/tools/r8/graph/InvalidCode.java
new file mode 100644
index 0000000..418de86
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/InvalidCode.java
@@ -0,0 +1,69 @@
+// 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.graph;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.origin.Origin;
+
+public class InvalidCode extends Code {
+
+  private static final InvalidCode INSTANCE = new InvalidCode();
+
+  public static Code getInstance() {
+    return INSTANCE;
+  }
+
+  public static boolean isInvalidCode(Code code) {
+    return code == INSTANCE;
+  }
+
+  private InvalidCode() {}
+
+  @Override
+  public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) {
+    throw new Unreachable();
+  }
+
+  @Override
+  public void registerCodeReferences(ProgramMethod method, UseRegistry registry) {
+    throw new Unreachable();
+  }
+
+  @Override
+  public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) {
+    throw new Unreachable();
+  }
+
+  @Override
+  public String toString() {
+    return "<invalid-code>";
+  }
+
+  @Override
+  public String toString(DexEncodedMethod method, ClassNameMapper naming) {
+    return toString();
+  }
+
+  @Override
+  public int estimatedDexCodeSizeUpperBoundInBytes() {
+    throw new Unreachable();
+  }
+
+  @Override
+  public boolean isEmptyVoidMethod() {
+    throw new Unreachable();
+  }
+
+  @Override
+  protected int computeHashCode() {
+    return System.identityHashCode(this);
+  }
+
+  @Override
+  protected boolean computeEquals(Object other) {
+    return this == other;
+  }
+}
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 79afbf8..ae334dc 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
@@ -40,6 +40,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.InvalidCode;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
@@ -858,9 +859,9 @@
     assert resolutionResult != null;
     assert resolutionResult.getResolvedMethod().isStatic();
     assert invokeNeedsRewriting(invokedMethod, STATIC);
-
-    return rewriteInvoke.apply(
-        staticAsMethodOfCompanionClass(resolutionResult.getResolutionPair()));
+    DexClassAndMethod companionMethod =
+        ensureStaticAsMethodOfCompanionClassStub(resolutionResult.getResolutionPair());
+    return rewriteInvoke.apply(companionMethod.getReference());
   }
 
   private Collection<CfInstruction> rewriteInvokeSuper(
@@ -1187,12 +1188,21 @@
     return factory.createType(interfaceTypeDescriptor);
   }
 
+  DexClassAndMethod ensureStaticAsMethodOfCompanionClassStub(DexClassAndMethod method) {
+    if (method.isProgramMethod()) {
+      return ensureStaticAsMethodOfProgramCompanionClassStub(method.asProgramMethod());
+    } else {
+      ClasspathOrLibraryClass context = method.getHolder().asClasspathOrLibraryClass();
+      DexMethod companionMethodReference = staticAsMethodOfCompanionClass(method);
+      return ensureMethodOfClasspathCompanionClassStub(companionMethodReference, context, appView);
+    }
+  }
+
   // Represent a static interface method as a method of companion class.
   final DexMethod staticAsMethodOfCompanionClass(DexClassAndMethod method) {
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     DexType companionClassType = getCompanionClassType(method.getHolderType(), dexItemFactory);
     DexMethod rewritten = method.getReference().withHolder(companionClassType, dexItemFactory);
-    recordCompanionClassReference(appView, method, rewritten);
     return rewritten;
   }
 
@@ -1244,19 +1254,24 @@
     return privateAsMethodOfCompanionClass(method.getReference(), factory);
   }
 
-  private static void recordCompanionClassReference(
+  private static DexClassAndMethod recordCompanionClassReference(
       AppView<?> appView, DexClassAndMethod method, DexMethod rewritten) {
     ClasspathOrLibraryClass context = method.getHolder().asClasspathOrLibraryClass();
     // If the interface class is a program class, we shouldn't need to synthesize the companion
     // class on the classpath.
     if (context == null) {
-      return;
+      return null;
     }
-    appView
+    return ensureMethodOfClasspathCompanionClassStub(rewritten, context, appView);
+  }
+
+  private static DexClassAndMethod ensureMethodOfClasspathCompanionClassStub(
+      DexMethod companionMethodReference, ClasspathOrLibraryClass context, AppView<?> appView) {
+    return appView
         .getSyntheticItems()
         .ensureFixedClasspathClassMethod(
-            rewritten.getName(),
-            rewritten.getProto(),
+            companionMethodReference.getName(),
+            companionMethodReference.getProto(),
             SyntheticKind.COMPANION_CLASS,
             context,
             appView,
@@ -1267,6 +1282,32 @@
                     .setCode(DexEncodedMethod::buildEmptyThrowingCfCode));
   }
 
+  ProgramMethod ensureStaticAsMethodOfProgramCompanionClassStub(ProgramMethod method) {
+    DexMethod companionMethodReference = staticAsMethodOfCompanionClass(method);
+    DexEncodedMethod definition = method.getDefinition();
+    return InterfaceProcessor.ensureCompanionMethod(
+        method.getHolder(),
+        companionMethodReference.getName(),
+        companionMethodReference.getProto(),
+        appView,
+        methodBuilder -> {
+          MethodAccessFlags newFlags = definition.getAccessFlags().copy();
+          newFlags.promoteToPublic();
+          methodBuilder
+              .setAccessFlags(newFlags)
+              .setGenericSignature(definition.getGenericSignature())
+              .setAnnotations(definition.annotations())
+              .setParameterAnnotationsList(definition.getParameterAnnotations())
+              // TODO(b/183998768): Once R8 desugars in the enqueuer this should set an invalid
+              //  code to ensure it is never used before desugared and installed.
+              .setCode(
+                  m ->
+                      appView.enableWholeProgramOptimizations()
+                          ? definition.getCode()
+                          : InvalidCode.getInstance());
+        });
+  }
+
   /**
    * Move static and default interface methods to companion classes, add missing methods to forward
    * to moved default methods implementation.
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 0eebbdb..ab61be0 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
@@ -36,6 +36,7 @@
 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.InvalidCode;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.MethodCollection;
 import com.android.tools.r8.graph.NestedGraphLens;
@@ -74,12 +75,10 @@
   private final InterfaceMethodRewriter rewriter;
   private final Map<DexProgramClass, PostProcessingInterfaceInfo> postProcessingInterfaceInfos =
       new ConcurrentHashMap<>();
-  private final ClassTypeSignature objectTypeSignature;
 
   InterfaceProcessor(AppView<?> appView, InterfaceMethodRewriter rewriter) {
     this.appView = appView;
     this.rewriter = rewriter;
-    this.objectTypeSignature = new ClassTypeSignature(appView.dexItemFactory().objectType);
   }
 
   @Override
@@ -111,30 +110,29 @@
     processDirectInterfaceMethods(iface);
   }
 
-  private ProgramMethod ensureCompanionMethod(
+  static ProgramMethod ensureCompanionMethod(
       DexProgramClass iface,
       DexString methodName,
       DexProto methodProto,
+      AppView<?> appView,
       Consumer<SyntheticMethodBuilder> fn) {
-    ProgramMethod method =
-        appView
-            .getSyntheticItems()
-            .ensureFixedClassMethod(
-                methodName,
-                methodProto,
-                SyntheticKind.COMPANION_CLASS,
-                iface,
-                appView,
-                builder ->
-                    builder
-                        .setSourceFile(iface.sourceFile)
-                        .setGenericSignature(
-                            iface
-                                .getClassSignature()
-                                .toObjectBoundWithSameFormals(objectTypeSignature)),
-                fn);
-    assert method.getHolderType() == rewriter.getCompanionClassType(iface.type);
-    return method;
+    return appView
+        .getSyntheticItems()
+        .ensureFixedClassMethod(
+            methodName,
+            methodProto,
+            SyntheticKind.COMPANION_CLASS,
+            iface,
+            appView,
+            builder ->
+                builder
+                    .setSourceFile(iface.sourceFile)
+                    .setGenericSignature(
+                        iface
+                            .getClassSignature()
+                            .toObjectBoundWithSameFormals(
+                                new ClassTypeSignature(appView.dexItemFactory().objectType))),
+            fn);
   }
 
   private void ensureCompanionClassInitializesInterface(
@@ -148,6 +146,7 @@
             iface,
             appView.dexItemFactory().classConstructorMethodName,
             appView.dexItemFactory().createProto(appView.dexItemFactory().voidType),
+            appView,
             methodBuilder -> createCompanionClassInitializer(iface, clinitField, methodBuilder));
     synthesizedMethods.add(clinit);
   }
@@ -252,6 +251,7 @@
             iface,
             companionMethod.getName(),
             companionMethod.getProto(),
+            appView,
             methodBuilder ->
                 methodBuilder
                     .setAccessFlags(newFlags)
@@ -302,25 +302,17 @@
                 + " is expected to "
                 + "either be public or private in "
                 + iface.origin;
-        DexMethod companionMethod = rewriter.staticAsMethodOfCompanionClass(method);
-
-        ensureCompanionMethod(
-            iface,
-            companionMethod.getName(),
-            companionMethod.getProto(),
-            methodBuilder ->
-                methodBuilder
-                    .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);
+        ProgramMethod companion = rewriter.ensureStaticAsMethodOfProgramCompanionClassStub(method);
+        // TODO(b/183998768): R8 should also install an "invalid code" object until the actual code
+        //  moves.
+        assert appView.enableWholeProgramOptimizations()
+            || InvalidCode.isInvalidCode(companion.getDefinition().getCode());
+        if (definition.hasClassFileVersion()) {
+          companion.getDefinition().downgradeClassFileVersion(definition.getClassFileVersion());
+        }
+        companion.getDefinition().setCode(definition.getCode(), appView);
+        getPostProcessingInterfaceInfo(iface).moveMethod(oldMethod, companion.getReference());
+        definition.setCode(InvalidCode.getInstance(), appView);
         continue;
       }
 
@@ -347,6 +339,7 @@
           iface,
           companionMethod.getName(),
           companionMethod.getProto(),
+          appView,
           methodBuilder ->
               methodBuilder
                   .setAccessFlags(newFlags)
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 608f635..eebc9fe 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -168,7 +168,7 @@
         .withBuilderTransformation(ToolHelper::allowTestProguardOptions)
         .withBuilderTransformation(
             b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
-        .withDexCheck(inspector -> checkLambdaCount(inspector, 7, "lambdadesugaringnplus"))
+        .withDexCheck(inspector -> checkLambdaCount(inspector, 6, "lambdadesugaringnplus"))
         .run();
 
     test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
@@ -177,7 +177,7 @@
         .withBuilderTransformation(ToolHelper::allowTestProguardOptions)
         .withBuilderTransformation(
             b -> b.addProguardConfiguration(PROGUARD_OPTIONS_N_PLUS, Origin.unknown()))
-        .withDexCheck(inspector -> checkLambdaCount(inspector, 4, "lambdadesugaringnplus"))
+        .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaringnplus"))
         .run();
   }