Use resolution to determine library target in interface desugaring

Bug: b/234711664
Change-Id: I8ba2040463caa3a13f9d215d530503be03300b13
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 cbf88a6..01236b0 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
@@ -591,6 +591,19 @@
     // 1.7 or below, this will make a VerificationError on the input a VerificationError
     // on the output. If the input was 1.8 or above the runtime behaviour (potential ICCE)
     // will remain the same.
+    upgradeCfVersionToSupportInterfaceMethodInvoke(method);
+  }
+
+  private void leavingSuperInvokeToInterface(ProgramMethod method) {
+    // When leaving interface method invokes possibly upgrade the class file
+    // version, but don't go above the initial class file version. If the input was
+    // 1.7 or below, this will make a VerificationError on the input a VerificationError
+    // on the output. If the input was 1.8 or above the runtime behaviour (potential ICCE)
+    // will remain the same.
+    upgradeCfVersionToSupportInterfaceMethodInvoke(method);
+  }
+
+  private void upgradeCfVersionToSupportInterfaceMethodInvoke(ProgramMethod method) {
     if (method.getHolder().hasClassFileVersion()) {
       method
           .getDefinition()
@@ -644,7 +657,7 @@
       return computeInvokeAsThrowRewrite(invoke, resolutionResult, context);
     }
 
-    if (clazz.isInterface() && !clazz.isLibraryClass()) {
+    if (clazz.isInterface() && !resolutionResult.getResolutionPair().getHolder().isLibraryClass()) {
       // NOTE: we intentionally don't desugar super calls into interface methods
       // coming from android.jar since it is only possible in case v24+ version
       // of android.jar is provided.
@@ -697,7 +710,7 @@
                   //  resolution was implemented in full.
                   DexMethod amendedMethod =
                       amendDefaultMethod(context12.getHolder(), invokedMethod);
-                  // assert method.getReference() == amendedMethod;
+                  assert method.getReference() == amendedMethod;
                   DexClassAndMethod companionMethod =
                       helper.ensureDefaultAsMethodOfCompanionClassStub(method);
                   acceptCompanionMethod(method, companionMethod, eventConsumer);
@@ -709,20 +722,27 @@
 
     DesugarDescription emulatedInterfaceDesugaring =
         computeEmulatedInterfaceInvokeSpecial(clazz, invokedMethod, context);
-    if (!emulatedInterfaceDesugaring.needsDesugaring() && context.isDefaultMethod()) {
-      return AlwaysThrowingInstructionDesugaring.computeInvokeAsThrowNSMERewrite(
-          appView,
-          invoke,
-          () ->
-              appView
-                  .reporter()
-                  .warning(
-                      new StringDiagnostic(
-                          "Interface method desugaring has inserted NoSuchMethodError replacing a"
-                              + " super call in "
-                              + context.toSourceString(),
-                          context.getOrigin())));
+    if (!emulatedInterfaceDesugaring.needsDesugaring()) {
+      if (context.isDefaultMethod()) {
+        return AlwaysThrowingInstructionDesugaring.computeInvokeAsThrowNSMERewrite(
+            appView,
+            invoke,
+            () ->
+                appView
+                    .reporter()
+                    .warning(
+                        new StringDiagnostic(
+                            "Interface method desugaring has inserted NoSuchMethodError replacing a"
+                                + " super call in "
+                                + context.toSourceString(),
+                            context.getOrigin())));
+      } else {
+        return DesugarDescription.builder()
+            .addScanEffect(() -> leavingSuperInvokeToInterface(context))
+            .build();
+      }
     }
+
     return emulatedInterfaceDesugaring;
   }
 
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultMethodResolvingToLibraryTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultMethodResolvingToLibraryTest.java
index 0319cf1..6e19e01 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultMethodResolvingToLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultMethodResolvingToLibraryTest.java
@@ -5,11 +5,14 @@
 package com.android.tools.r8.desugaring.interfacemethods;
 
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.testing.AndroidBuildVersion;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -34,28 +37,14 @@
   private static final String EXPECTED_OUTPUT = StringUtils.lines("8");
 
   private void inspect(CodeInspector inspector) {
-    if (parameters
-        .getApiLevel()
-        .isLessThan(TestBase.apiLevelWithDefaultInterfaceMethodsSupport())) {
-      assertTrue(
-          inspector
-              .clazz(B.class)
-              .uniqueMethodWithName("compose")
-              .streamInstructions()
-              .filter(InstructionSubject::isInvoke)
-              .map(invoke -> invoke.getMethod().getHolderType().toString())
-              // TODO(b/234711664): This should not happen.
-              .anyMatch(name -> name.equals("java.util.function.Function$-CC")));
-    } else {
-      assertTrue(
-          inspector
-              .clazz(B.class)
-              .uniqueMethodWithName("compose")
-              .streamInstructions()
-              .filter(InstructionSubject::isInvoke)
-              .map(invoke -> invoke.getMethod().getHolderType().toString())
-              .noneMatch(name -> name.endsWith("$-CC")));
-    }
+    assertTrue(
+        inspector
+            .clazz(B.class)
+            .uniqueMethodWithName("compose")
+            .streamInstructions()
+            .filter(InstructionSubject::isInvoke)
+            .map(invoke -> invoke.getMethod().getHolderType().toString())
+            .noneMatch(name -> name.endsWith("$-CC")));
   }
 
   @Test
@@ -65,7 +54,87 @@
         .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
         .setMinApi(parameters.getApiLevel())
         .compile()
-        .inspect(this::inspect);
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .applyIf(
+            parameters.isDexRuntime()
+                && parameters
+                    .getRuntime()
+                    .asDex()
+                    .maxSupportedApiLevel()
+                    .isLessThan(AndroidApiLevel.N),
+            r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+            r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+  }
+
+  @Test
+  public void testDesugaringWithApiLevelCheck() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+        .addAndroidBuildVersion(parameters.getRuntime().asDex().maxSupportedApiLevel())
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClassWithApiLevelCheck.class)
+        .applyIf(
+            parameters.isDexRuntime()
+                && parameters
+                    .getRuntime()
+                    .asDex()
+                    .maxSupportedApiLevel()
+                    .isLessThan(AndroidApiLevel.N),
+            r -> r.assertSuccessWithOutputLines("No call"),
+            r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    try {
+      testForR8(parameters.getBackend())
+          .addInnerClasses(getClass())
+          .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+          .setMinApi(parameters.getApiLevel())
+          .addKeepMainRule(TestClass.class)
+          .compile()
+          // .inspect(this::inspect)
+          .run(parameters.getRuntime(), TestClass.class)
+          .applyIf(
+              parameters.isDexRuntime()
+                  && parameters
+                      .getRuntime()
+                      .asDex()
+                      .maxSupportedApiLevel()
+                      .isLessThan(AndroidApiLevel.N),
+              r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+              r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+    } catch (CompilationFailedException e) {
+      // TODO(b/235184674): Fix this.
+      assertTrue(parameters.isCfRuntime());
+    }
+  }
+
+  // TODO(b/235184674): Fix this.
+  @Test(expected = CompilationFailedException.class)
+  public void testR8WithApiLevelCheck() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(TestClassWithApiLevelCheck.class)
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClassWithApiLevelCheck.class)
+        .applyIf(
+            parameters.isDexRuntime()
+                && parameters
+                    .getRuntime()
+                    .asDex()
+                    .maxSupportedApiLevel()
+                    .isLessThan(AndroidApiLevel.N),
+            r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+            r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
   }
 
   static class TestClass {
@@ -79,6 +148,21 @@
     }
   }
 
+  static class TestClassWithApiLevelCheck {
+
+    private static void m(C c) {
+      System.out.println(c.compose(c).apply(2));
+    }
+
+    public static void main(String[] args) {
+      if (AndroidBuildVersion.VERSION >= 24) {
+        m(new C());
+      } else {
+        System.out.println("No call");
+      }
+    }
+  }
+
   interface MyFunction<V, R> extends Function<V, R> {}
 
   abstract static class B<V, R> implements MyFunction<V, R> {