Strenghten the check for the Art JDK-8272564 workaround

Also add a test of invalid invokes not changing.

Bug: b/218298666
Change-Id: I9ab27dad52a04808b54ddaa37eccf7c4aaa9deaa
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 73d8774..1e73aab 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -1164,7 +1164,7 @@
 
     if (!options.canHaveInvokeInterfaceToObjectMethodBug()) {
       timing.begin("JDK-8272564 fix rewrite");
-      CodeRewriter.rewriteJdk8272564Fix(code, appView);
+      CodeRewriter.rewriteJdk8272564Fix(code, context, appView);
       timing.end();
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index c468c7a..868ef79 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -3796,7 +3796,7 @@
   // The javac fix for JDK-8272564 has to be rewritten back to invoke-virtual on Object if the
   // method with an Object signature is not defined on the interface. See
   // https://bugs.openjdk.java.net/browse/JDK-8272564
-  public static void rewriteJdk8272564Fix(IRCode code, AppView<?> appView) {
+  public static void rewriteJdk8272564Fix(IRCode code, ProgramMethod context, AppView<?> appView) {
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     InstructionListIterator it = code.instructionListIterator();
     while (it.hasNext()) {
@@ -3804,10 +3804,15 @@
       if (instruction.isInvokeInterface()) {
         InvokeInterface invoke = instruction.asInvokeInterface();
         DexMethod method = invoke.getInvokedMethod();
-        DexMethod objectMember = dexItemFactory.objectMembers.matchingPublicObjectMember(method);
-        if (objectMember != null && appView.definitionFor(method) == null) {
-          it.replaceCurrentInstruction(
-              new InvokeVirtual(objectMember, invoke.outValue(), invoke.arguments()));
+        DexClass clazz = appView.definitionFor(method.getHolderType(), context);
+        if (clazz == null || clazz.isInterface()) {
+          DexMethod objectMember = dexItemFactory.objectMembers.matchingPublicObjectMember(method);
+          // javac before JDK-8272564 would still use invoke interface if the method is defined
+          // directly on the interface reference, so mimic that by not rewriting.
+          if (objectMember != null && appView.definitionFor(method) == null) {
+            it.replaceCurrentInstruction(
+                new InvokeVirtual(objectMember, invoke.outValue(), invoke.arguments()));
+          }
         }
       }
     }
diff --git a/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564InvalidCode.java b/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564InvalidCode.java
new file mode 100644
index 0000000..561c094
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564InvalidCode.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2022, 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.desugar.jdk8272564;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class Jdk8272564InvalidCode extends TestBase {
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        .withCfRuntimes()
+        .withDexRuntimes()
+        .withAllApiLevelsAlsoForCf()
+        .build();
+  }
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  private boolean isDefaultCfParameters() {
+    return parameters.isCfRuntime() && parameters.getApiLevel().equals(AndroidApiLevel.B);
+  }
+
+  @Test
+  public void testRuntime() throws Exception {
+    assumeTrue(isDefaultCfParameters());
+    testForJvm()
+        .addProgramClasses(I.class)
+        .addProgramClassFileData(getTransformedClass())
+        .run(parameters.getRuntime(), A.class)
+        .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+  }
+
+  @Test
+  public void testDesugaring() throws Exception {
+    assumeTrue(parameters.isDexRuntime() || isDefaultCfParameters());
+    testForDesugaring(parameters)
+        .addProgramClasses(I.class)
+        .addProgramClassFileData(getTransformedClass())
+        .run(parameters.getRuntime(), A.class)
+        .applyIf(
+            parameters.isDexRuntime()
+                && parameters
+                    .getRuntime()
+                    .asDex()
+                    .getVm()
+                    .getVersion()
+                    .isOlderThanOrEqual(Version.V4_4_4),
+            r -> r.assertFailureWithErrorThatThrows(NoSuchMethodError.class),
+            r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class));
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    assumeTrue(parameters.isDexRuntime() || isDefaultCfParameters());
+    // The R8 lens code rewriter rewrites to the code prior to fixing JDK-8272564.
+    testForR8(parameters.getBackend())
+        .addProgramClasses(I.class)
+        .addProgramClassFileData(getTransformedClass())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(A.class)
+        .addOptionsModification(options -> options.testing.allowInvokeErrors = true)
+        .run(parameters.getRuntime(), A.class)
+        .applyIf(
+            parameters.isDexRuntime()
+                && parameters
+                    .getRuntime()
+                    .asDex()
+                    .getVm()
+                    .getVersion()
+                    .isOlderThanOrEqual(Version.V4_4_4),
+            r -> r.assertFailureWithErrorThatThrows(NoSuchMethodError.class),
+            r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class));
+  }
+
+  private byte[] getTransformedClass() throws Exception {
+    return transformer(A.class)
+        .transformMethodInsnInMethod(
+            "main",
+            ((opcode, owner, name, descriptor, isInterface, continuation) -> {
+              continuation.visitMethodInsn(
+                  name.equals("hashCode") ? Opcodes.INVOKEINTERFACE : opcode,
+                  // javac generates java.lang.object as holder, change it to A.
+                  name.equals("hashCode") ? DescriptorUtils.getClassBinaryName(A.class) : owner,
+                  name,
+                  descriptor,
+                  name.equals("hashCode") || isInterface);
+            }))
+        .transform();
+  }
+
+  interface I {}
+
+  static class A implements I {
+
+    public static void main(String[] args) {
+      new A().hashCode();
+    }
+  }
+}