Don't do default interface method processing when merging DEX

This will now report UnsupportedStaticInterfaceMethodDiagnostic/
UnsupportedDefaultInterfaceMethodDiagnostic
when a v35 DEX has "hidden" static/default interface methods and
min API level is below 24.

Allow upgrading DEX v35 with "hidden" static/default interface
methods to v37 (min API level 24 and above).

Bug: b/507323858
Change-Id: I2783490b593b9b97f8b8663356676d91d9b10298
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 a388e9e..857ee4c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -867,6 +867,10 @@
     classFileVersion = Ordered.minIgnoreNull(classFileVersion, version);
   }
 
+  public boolean dexCodeOnInput() {
+    return hasCode() && getCode().isDexCode() && !hasClassFileVersion() && !isD8R8Synthesized();
+  }
+
   public String qualifiedName() {
     checkIfObsolete();
     return getReference().qualifiedName();
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 279c3f0..8a5b25c 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
@@ -109,6 +109,9 @@
     if (!method.getHolder().isInterface()) {
       return;
     }
+    if (method.getDefinition().dexCodeOnInput() && appView.options().passthroughDexCode) {
+      return;
+    }
     if (desugaringMode == LIBRARY_DESUGARING_N_PLUS) {
       processEmulatedInterfaceOnly(method, eventConsumer);
       return;
diff --git a/src/test/java/com/android/tools/r8/dexfilemerger/DexMergeV35WithIllegalStaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/dexfilemerger/DexMergeV35WithIllegalStaticInterfaceMethodTest.java
index d8d904e..6ac52c5 100644
--- a/src/test/java/com/android/tools/r8/dexfilemerger/DexMergeV35WithIllegalStaticInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/dexfilemerger/DexMergeV35WithIllegalStaticInterfaceMethodTest.java
@@ -4,54 +4,64 @@
 
 package com.android.tools.r8.dexfilemerger;
 
+import static com.android.tools.r8.ToolHelper.DexVm.Version.V7_0_0;
 import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.DiagnosticsMatcher;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.errors.UnsupportedDefaultInterfaceMethodDiagnostic;
+import com.android.tools.r8.errors.UnsupportedStaticInterfaceMethodDiagnostic;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardOpenOption;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
 
 // Reproduction of b/507323858.
 @RunWith(Parameterized.class)
 public class DexMergeV35WithIllegalStaticInterfaceMethodTest extends TestBase {
 
-  private final TestParameters parameters;
+  @Parameter(0)
+  public TestParameters parameters;
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withNoneRuntime().build();
+    return getTestParameters()
+        .withNoneRuntime()
+        .withDexRuntimesStartingFromIncluding(V7_0_0)
+        .build();
   }
 
-  public DexMergeV35WithIllegalStaticInterfaceMethodTest(TestParameters parameters) {
-    this.parameters = parameters;
+  private Path fakeDexV35(Path dex) throws IOException {
+    Path dex35 = temp.newFile("dex35.dex").toPath();
+    byte[] dexBytes = Files.readAllBytes(dex);
+    dexBytes[6] = 0x35;
+    Files.write(dex35, dexBytes, StandardOpenOption.CREATE);
+    return dex35;
   }
 
   @Test
-  public void test() throws Exception {
-    // Create DEX v37 with an interface with a static method and class initializer.
+  public void testStaticMethod() throws Exception {
+    assumeTrue(parameters.isNoneRuntime());
+    // Create DEX v37 with an interface with a static method.
     Path dexDir =
         testForD8()
             .setMinApi(AndroidApiLevel.N)
-            .addInnerClasses(getClass())
+            .addProgramClasses(I1.class)
             .compile()
             .writeToDirectory();
 
-    // Fake DEX v35 for the v37 DEX
-    Path dex35 = temp.newFile("dex35.dex").toPath();
-    byte[] dexBytes = Files.readAllBytes(dexDir.resolve("classes.dex"));
-    dexBytes[6] = 0x35;
-    Files.write(dex35, dexBytes, StandardOpenOption.CREATE);
-
-    // Run the v35 DEX with an interface with a static method and class initializer through DEX
-    // merging.
+    // Run v35 DEX with an interface with a static method through DEX merging.
+    Path dex35 = fakeDexV35(dexDir.resolve("classes.dex"));
     assertThrows(
         CompilationFailedException.class,
         () ->
@@ -63,22 +73,76 @@
                         diagnostics
                             .assertOnlyErrors()
                             .assertAllErrorsMatch(
-                                DiagnosticsMatcher.diagnosticException(
-                                    NullPointerException.class))));
+                                DiagnosticsMatcher.diagnosticType(
+                                    UnsupportedStaticInterfaceMethodDiagnostic.class))));
   }
 
-  interface I {
-    Object o = new Object();
+  @Test
+  public void testDefaultMethod() throws Exception {
+    assumeTrue(parameters.isNoneRuntime());
+    // Create DEX v37 with an interface with a default method.
+    Path dexDir =
+        testForD8()
+            .setMinApi(AndroidApiLevel.N)
+            .addProgramClasses(I2.class)
+            .compile()
+            .writeToDirectory();
 
+    // Run v35 DEX with an interface with a default method through DEX merging.
+    Path dex35 = fakeDexV35(dexDir.resolve("classes.dex"));
+    assertThrows(
+        CompilationFailedException.class,
+        () ->
+            testForD8(Backend.DEX)
+                .addProgramFiles(dex35)
+                .setMinApi(AndroidApiLevel.K)
+                .compileWithExpectedDiagnostics(
+                    diagnostics ->
+                        diagnostics
+                            .assertOnlyErrors()
+                            .assertAllErrorsMatch(
+                                DiagnosticsMatcher.diagnosticType(
+                                    UnsupportedDefaultInterfaceMethodDiagnostic.class))));
+  }
+
+  @Test
+  public void testAllowV35WithStaticAndDefaultInterfaceMethodsWhenCompilingToV37()
+      throws Exception {
+    assumeFalse(parameters.isNoneRuntime());
+    // Create DEX v37 with an interface with a static method and an interface with a default method.
+    Path dexDir =
+        testForD8()
+            .setMinApi(AndroidApiLevel.N)
+            .addInnerClasses(getClass())
+            .compile()
+            .writeToDirectory();
+
+    // Run the v35 DEX with an interface with a static method and an interface with a default method
+    // through DEX merging.
+    Path dex35 = fakeDexV35(dexDir.resolve("classes.dex"));
+    testForD8(Backend.DEX)
+        .addProgramFiles(dex35)
+        .setMinApi(AndroidApiLevel.N)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello, world!");
+  }
+
+  interface I1 {
     static String f() {
-      return "Hello, world!";
+      return "Hello,";
+    }
+  }
+
+  interface I2 {
+    default String f() {
+      return " world!";
     }
   }
 
   static class TestClass {
 
     public static void main(String[] args) {
-      System.out.println(I.f());
+      System.out.println(I1.f() + (new I2() {}).f());
     }
   }
 }