Rewrite code from javac code with for JDK-8272564

Some Dalvik and Art MVs does not support interface invokes to Object
members not explicitly defined on the symbolic reference of the
interface invoke. In these cases rewrite to a virtual invoke with
the symbolic reference java.lang.Object.

Conflicts:
Removed tests during cherry-pick. They used JDK-18 which is not
in third_party on the 3.1 branch.

Merge issue:
Added new method from
https://r8-review.googlesource.com/c/r8/+/65403/9/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java#79
Added code from
https://r8-review.googlesource.com/c/r8/+/63660/4/src/main/java/com/android/tools/r8/graph/DexItemFactory.java#1373
https://r8-review.googlesource.com/c/r8/+/63660/4/src/main/java/com/android/tools/r8/graph/DexItemFactory.java#1390

Bug: 218298666
Change-Id: I38abdea8d9bbf4d004be1877373b58de161a55a7
diff --git a/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java b/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
index d18db79..57212d4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
@@ -76,6 +76,11 @@
   @Deprecated
   DexClass definitionFor(DexType type);
 
+  default DexClassAndMethod definitionFor(DexMethod method) {
+    DexClass holder = definitionFor(method.getHolderType());
+    return holder != null ? holder.lookupClassMethod(method) : null;
+  }
+
   // Use programDefinitionFor with a context.
   @Deprecated
   default DexProgramClass definitionForProgramType(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 82fa005..dfa7276 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -1370,6 +1370,11 @@
     public final DexMethod constructor;
     public final DexMethod finalize;
     public final DexMethod toString;
+    public final DexMethod notify;
+    public final DexMethod notifyAll;
+    public final DexMethod wait;
+    public final DexMethod waitLong;
+    public final DexMethod waitLongInt;
 
     private ObjectMembers() {
       // The clone method is installed on each array, so one has to use method.match(clone).
@@ -1382,6 +1387,84 @@
           finalizeMethodName, voidType.descriptor, DexString.EMPTY_ARRAY);
       toString = createMethod(objectDescriptor,
           toStringMethodName, stringDescriptor, DexString.EMPTY_ARRAY);
+      notify =
+          createMethod(objectDescriptor, notifyMethodName, voidDescriptor, DexString.EMPTY_ARRAY);
+      notifyAll =
+          createMethod(
+              objectDescriptor, notifyAllMethodName, voidDescriptor, DexString.EMPTY_ARRAY);
+      wait = createMethod(objectDescriptor, waitMethodName, voidDescriptor, DexString.EMPTY_ARRAY);
+      waitLong =
+          createMethod(
+              objectDescriptor, waitMethodName, voidDescriptor, new DexString[] {longDescriptor});
+      waitLongInt =
+          createMethod(
+              objectDescriptor,
+              waitMethodName,
+              voidDescriptor,
+              new DexString[] {longDescriptor, intDescriptor});
+    }
+
+    public boolean isObjectMember(DexMethod method) {
+      return method.match(clone)
+          || method.match(getClass)
+          || method.match(constructor)
+          || method.match(finalize)
+          || method.match(toString)
+          || method.match(hashCode)
+          || method.match(equals)
+          || method.match(notify)
+          || method.match(notifyAll)
+          || method.match(wait)
+          || method.match(waitLong)
+          || method.match(waitLongInt);
+    }
+
+    public DexMethod matchingPublicObjectMember(DexMethod method) {
+      switch (method.getName().byteAt(0)) {
+        case 't':
+          if (method.match(toString)) {
+            return toString;
+          }
+          break;
+        case 'h':
+          if (method.match(hashCode)) {
+            return hashCode;
+          }
+          break;
+        case 'e':
+          if (method.match(equals)) {
+            return equals;
+          }
+          break;
+        case 'g':
+          if (method.match(getClass)) {
+            return getClass;
+          }
+          break;
+        case 'n':
+          if (method.match(notify)) {
+            return notify;
+          }
+          if (method.match(notifyAll)) {
+            return notifyAll;
+          }
+          break;
+        case 'w':
+          if (method.match(wait)) {
+            return wait;
+          }
+          if (method.match(waitLong)) {
+            return waitLong;
+          }
+          if (method.match(waitLongInt)) {
+            return waitLongInt;
+          }
+          break;
+        default:
+          // Methods finalize and clone are not public.
+          return null;
+      }
+      return null;
     }
   }
 
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 8f6f293..73d8774 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
@@ -1162,6 +1162,12 @@
       timing.end();
     }
 
+    if (!options.canHaveInvokeInterfaceToObjectMethodBug()) {
+      timing.begin("JDK-8272564 fix rewrite");
+      CodeRewriter.rewriteJdk8272564Fix(code, appView);
+      timing.end();
+    }
+
     boolean isDebugMode = options.debug || method.getOptimizationInfo().isReachabilitySensitive();
 
     if (isDebugMode) {
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 86fb8e9..c468c7a 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
@@ -67,6 +67,7 @@
 import com.android.tools.r8.ir.code.IntSwitch;
 import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeInterface;
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
 import com.android.tools.r8.ir.code.InvokeNewArray;
@@ -3792,6 +3793,26 @@
     }
   }
 
+  // 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) {
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
+    InstructionListIterator it = code.instructionListIterator();
+    while (it.hasNext()) {
+      Instruction instruction = it.next();
+      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()));
+        }
+      }
+    }
+  }
+
   private static NewInstance findNewInstance(Phi phi) {
     Set<Phi> seen = Sets.newIdentityHashSet();
     Set<Value> values = Sets.newIdentityHashSet();
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index e35964a..64dc5d3 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -2225,4 +2225,16 @@
   public boolean canParseNumbersWithPlusPrefix() {
     return minApiLevel.isGreaterThan(AndroidApiLevel.K);
   }
+
+  // Some Dalvik and Art MVs does not support interface invokes to Object
+  // members not explicitly defined on the symbolic reference of the
+  // interface invoke. In these cases rewrite to a virtual invoke with
+  // the symbolic reference java.lang.Object.
+  //
+  // javac started generating code like this with the fix for JDK-8272564.
+  //
+  // See b/218298666.
+  public boolean canHaveInvokeInterfaceToObjectMethodBug() {
+    return isGeneratingClassFiles() || getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O);
+  }
 }