Handle invoke-super to private interface method in same nest
Bug: 216694478
Bug: 145775365
Change-Id: I6b70d51193d10092d7971bfab29bd65ff8bcd900
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
index 1f845ed..113c31c 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -361,6 +361,10 @@
return null;
}
+ if (getResolvedHolder().isInterface() && getResolvedMethod().isPrivate()) {
+ return getResolutionPair();
+ }
+
// The symbolic reference is the holder type that resolution was initiated at.
DexClass symbolicReference = initialResolutionHolder;
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 3155b54..813a237 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AccessControl;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
@@ -990,25 +991,42 @@
&& !virtualMethods.containsKey(wrapped);
};
- for (DexEncodedMethod directMethod : source.directMethods()) {
- if (directMethod.isInstanceInitializer()) {
- DexEncodedMethod resultingConstructor =
- renameConstructor(directMethod, availableMethodSignatures);
- add(directMethods, resultingConstructor, MethodSignatureEquivalence.get());
- blockRedirectionOfSuperCalls(resultingConstructor.getReference());
- } else {
- DexEncodedMethod resultingDirectMethod =
- renameMethod(
- directMethod,
- availableMethodSignatures,
- directMethod.isClassInitializer() ? Rename.NEVER : Rename.IF_NEEDED);
- add(directMethods, resultingDirectMethod, MethodSignatureEquivalence.get());
- deferredRenamings.map(directMethod.getReference(), resultingDirectMethod.getReference());
- deferredRenamings.recordMove(
- directMethod.getReference(), resultingDirectMethod.getReference());
- blockRedirectionOfSuperCalls(resultingDirectMethod.getReference());
- }
- }
+ source.forEachProgramDirectMethod(
+ directMethod -> {
+ DexEncodedMethod definition = directMethod.getDefinition();
+ if (definition.isInstanceInitializer()) {
+ DexEncodedMethod resultingConstructor =
+ renameConstructor(definition, availableMethodSignatures);
+ add(directMethods, resultingConstructor, MethodSignatureEquivalence.get());
+ blockRedirectionOfSuperCalls(resultingConstructor.getReference());
+ } else {
+ DexEncodedMethod resultingDirectMethod =
+ renameMethod(
+ definition,
+ availableMethodSignatures,
+ definition.isClassInitializer() ? Rename.NEVER : Rename.IF_NEEDED);
+ add(directMethods, resultingDirectMethod, MethodSignatureEquivalence.get());
+ deferredRenamings.map(
+ directMethod.getReference(), resultingDirectMethod.getReference());
+ deferredRenamings.recordMove(
+ directMethod.getReference(), resultingDirectMethod.getReference());
+ blockRedirectionOfSuperCalls(resultingDirectMethod.getReference());
+
+ // Private methods in the parent class may be targeted with invoke-super if the two
+ // classes are in the same nest. Ensure such calls are mapped to invoke-direct.
+ if (definition.isInstance()
+ && definition.isPrivate()
+ && AccessControl.isMemberAccessible(directMethod, source, target, appView)
+ .isTrue()) {
+ deferredRenamings.mapVirtualMethodToDirectInType(
+ directMethod.getReference(),
+ prototypeChanges ->
+ new MethodLookupResult(
+ resultingDirectMethod.getReference(), null, DIRECT, prototypeChanges),
+ target.getType());
+ }
+ }
+ });
for (DexEncodedMethod virtualMethod : source.virtualMethods()) {
DexEncodedMethod shadowedBy = findMethodInTarget(virtualMethod);
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
index 3803d1a..898cfae 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessTest.java
@@ -224,8 +224,8 @@
static class A implements I {
public void foo() {
// Rewritten to invoke-special A.bar or I.bar which resolves to private method A.bar
- // When targeting B.bar => throws NoSuchMethodError.
- // When targeting A.bar:
+ // When targeting A.bar => throws NoSuchMethodError.
+ // When targeting I.bar:
// - in same nest => success.
// - not in nest => throws IllegalAccessError.
bar();