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);
+ }
}