Publicize methods for direct dispatch in vertical class merger
This changes the vertical class merger to publicize methods that are introduced in order to rewrite invoke-super calls to target a method directly.
Change-Id: I4803dacad9ff0f3320be8214b92120966cc63c1e
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 4b5152b..7fab178 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -505,6 +505,11 @@
return !isPrivateMethod() && isVirtualMethod();
}
+ public boolean isNonStaticPrivateMethod() {
+ checkIfObsolete();
+ return isInstance() && isPrivate();
+ }
+
/**
* Returns true if this method can be invoked via invoke-virtual, invoke-super or invoke-interface
* and is non-abstract.
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 2a06ead..91e1ce4 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.ir.code.Invoke.Type.DIRECT;
import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
+import static com.android.tools.r8.ir.code.Invoke.Type.VIRTUAL;
import static com.android.tools.r8.utils.AndroidApiLevelUtils.getApiReferenceLevelForMerging;
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
@@ -1061,7 +1062,7 @@
}
}
- DexEncodedMethod resultingDirectMethod;
+ DexEncodedMethod resultingMethod;
if (source.accessFlags.isInterface()) {
// Moving a default interface method into its subtype. This method could be hit directly
// via an invoke-super instruction from any of the transitive subtypes of this interface,
@@ -1070,7 +1071,7 @@
// method name that does not collide with one in the hierarchy of this class.
MemberPool<DexMethod> methodPoolForTarget =
methodPoolCollection.buildForHierarchy(target, executorService, timing);
- resultingDirectMethod =
+ resultingMethod =
renameMethod(
virtualMethod,
method ->
@@ -1079,26 +1080,32 @@
MethodSignatureEquivalence.get().wrap(method)),
Rename.ALWAYS,
appView.dexItemFactory().prependHolderToProto(virtualMethod.getReference()));
- makeStatic(resultingDirectMethod);
+ makeStatic(resultingMethod);
// Update method pool collection now that we are adding a new public method.
- methodPoolForTarget.seen(resultingDirectMethod.getReference());
+ methodPoolForTarget.seen(resultingMethod.getReference());
} else {
// This virtual method could be called directly from a sub class via an invoke-super in-
- // struction. Therefore, we translate this virtual method into a direct method, such that
- // relevant invoke-super instructions can be rewritten into invoke-direct instructions.
- resultingDirectMethod =
- renameMethod(virtualMethod, availableMethodSignatures, Rename.ALWAYS);
- makePrivate(resultingDirectMethod);
+ // struction. Therefore, we translate this virtual method into an instance method with a
+ // unique name, such that relevant invoke-super instructions can be rewritten to target
+ // this method directly.
+ resultingMethod = renameMethod(virtualMethod, availableMethodSignatures, Rename.ALWAYS);
+ if (appView.options().getProguardConfiguration().isAccessModificationAllowed()) {
+ makePublic(resultingMethod);
+ } else {
+ makePrivate(resultingMethod);
+ }
}
- add(directMethods, resultingDirectMethod, MethodSignatureEquivalence.get());
+ add(
+ resultingMethod.belongsToDirectPool() ? directMethods : virtualMethods,
+ resultingMethod,
+ MethodSignatureEquivalence.get());
// Record that invoke-super instructions in the target class should be redirected to the
// newly created direct method.
- redirectSuperCallsInTarget(
- virtualMethod.getReference(), resultingDirectMethod.getReference());
- blockRedirectionOfSuperCalls(resultingDirectMethod.getReference());
+ redirectSuperCallsInTarget(virtualMethod, resultingMethod);
+ blockRedirectionOfSuperCalls(resultingMethod.getReference());
if (shadowedBy == null) {
// In addition to the newly added direct method, create a virtual method such that we do
@@ -1106,15 +1113,14 @@
// Note that this method is added independently of whether it will actually be used. If
// it turns out that the method is never used, it will be removed by the final round
// of tree shaking.
- shadowedBy = buildBridgeMethod(virtualMethod, resultingDirectMethod);
+ shadowedBy = buildBridgeMethod(virtualMethod, resultingMethod);
deferredRenamings.recordCreationOfBridgeMethod(
virtualMethod.getReference(), shadowedBy.getReference());
add(virtualMethods, shadowedBy, MethodSignatureEquivalence.get());
}
deferredRenamings.map(virtualMethod.getReference(), shadowedBy.getReference());
- deferredRenamings.recordMove(
- virtualMethod.getReference(), resultingDirectMethod.getReference());
+ deferredRenamings.recordMove(virtualMethod.getReference(), resultingMethod.getReference());
}
if (abortMerge) {
@@ -1369,7 +1375,11 @@
return synthesizedBridges;
}
- private void redirectSuperCallsInTarget(DexMethod oldTarget, DexMethod newTarget) {
+ private void redirectSuperCallsInTarget(
+ DexEncodedMethod oldTarget, DexEncodedMethod newTarget) {
+ DexMethod oldTargetReference = oldTarget.getReference();
+ DexMethod newTargetReference = newTarget.getReference();
+ Type newTargetType = newTarget.isNonPrivateVirtualMethod() ? VIRTUAL : DIRECT;
if (source.accessFlags.isInterface()) {
// If we merge a default interface method from interface I to its subtype C, then we need
// to rewrite invocations on the form "invoke-super I.m()" to "invoke-direct C.m$I()".
@@ -1379,21 +1389,23 @@
// if I has a supertype J. This is due to the fact that invoke-super instructions that
// resolve to a method on an interface never hit an implementation below that interface.
deferredRenamings.mapVirtualMethodToDirectInType(
- oldTarget,
- prototypeChanges -> new MethodLookupResult(newTarget, null, STATIC, prototypeChanges),
+ oldTargetReference,
+ prototypeChanges ->
+ new MethodLookupResult(newTargetReference, null, STATIC, prototypeChanges),
target.type);
} else {
// If we merge class B into class C, and class C contains an invocation super.m(), then it
- // is insufficient to rewrite "invoke-super B.m()" to "invoke-direct C.m$B()" (the method
- // C.m$B denotes the direct method that has been created in C for B.m). In particular, there
- // might be an instruction "invoke-super A.m()" in C that resolves to B.m at runtime (A is
- // a superclass of B), which also needs to be rewritten to "invoke-direct C.m$B()".
+ // is insufficient to rewrite "invoke-super B.m()" to "invoke-{direct,virtual} C.m$B()" (the
+ // method C.m$B denotes the direct/virtual method that has been created in C for B.m). In
+ // particular, there might be an instruction "invoke-super A.m()" in C that resolves to B.m
+ // at runtime (A is a superclass of B), which also needs to be rewritten to
+ // "invoke-{direct,virtual} C.m$B()".
//
// We handle this by adding a mapping for [target] and all of its supertypes.
DexProgramClass holder = target;
while (holder != null && holder.isProgramClass()) {
DexMethod signatureInHolder =
- application.dexItemFactory.createMethod(holder.type, oldTarget.proto, oldTarget.name);
+ oldTargetReference.withHolder(holder, appView.dexItemFactory());
// Only rewrite the invoke-super call if it does not lead to a NoSuchMethodError.
boolean resolutionSucceeds =
holder.lookupVirtualMethod(signatureInHolder) != null
@@ -1402,7 +1414,8 @@
deferredRenamings.mapVirtualMethodToDirectInType(
signatureInHolder,
prototypeChanges ->
- new MethodLookupResult(newTarget, null, DIRECT, prototypeChanges),
+ new MethodLookupResult(
+ newTargetReference, null, newTargetType, prototypeChanges),
target.type);
} else {
break;
@@ -1416,7 +1429,7 @@
Set<DexType> mergedTypes = mergedClasses.getKeys(holder.type);
for (DexType type : mergedTypes) {
DexMethod signatureInType =
- application.dexItemFactory.createMethod(type, oldTarget.proto, oldTarget.name);
+ oldTargetReference.withHolder(type, appView.dexItemFactory());
// Resolution would have succeeded if the method used to be in [type], or if one of
// its super classes declared the method.
boolean resolutionSucceededBeforeMerge =
@@ -1426,7 +1439,8 @@
deferredRenamings.mapVirtualMethodToDirectInType(
signatureInType,
prototypeChanges ->
- new MethodLookupResult(newTarget, null, DIRECT, prototypeChanges),
+ new MethodLookupResult(
+ newTargetReference, null, newTargetType, prototypeChanges),
target.type);
}
}
@@ -1469,13 +1483,17 @@
accessFlags.setSynthetic();
accessFlags.unsetAbstract();
- assert invocationTarget.isPrivateMethod() == !invocationTarget.isStatic();
+ assert invocationTarget.isStatic()
+ || invocationTarget.isNonPrivateVirtualMethod()
+ || invocationTarget.isNonStaticPrivateMethod();
SynthesizedBridgeCode code =
new SynthesizedBridgeCode(
newMethod,
appView.graphLens().getOriginalMethodSignature(method.getReference()),
invocationTarget.getReference(),
- invocationTarget.isPrivateMethod() ? DIRECT : STATIC,
+ invocationTarget.isStatic()
+ ? STATIC
+ : (invocationTarget.isNonPrivateVirtualMethod() ? VIRTUAL : DIRECT),
target.isInterface());
// Add the bridge to the list of synthesized bridges such that the method signatures will
@@ -1694,6 +1712,14 @@
method.accessFlags.setPrivate();
}
+ private static void makePublic(DexEncodedMethod method) {
+ MethodAccessFlags accessFlags = method.getAccessFlags();
+ assert !accessFlags.isAbstract();
+ accessFlags.unsetPrivate();
+ accessFlags.unsetProtected();
+ accessFlags.setPublic();
+ }
+
private static class VerticalClassMergerTreeFixer extends TreeFixerBase {
private final AppView<AppInfoWithLiveness> appView;
@@ -1979,7 +2005,7 @@
DexClass clazz = appInfo.definitionFor(newMethod.holder);
if (clazz != null && !clazz.accessFlags.isInterface()) {
assert appInfo.definitionFor(method.holder).accessFlags.isInterface();
- methodLookupResultBuilder.setType(Type.VIRTUAL);
+ methodLookupResultBuilder.setType(VIRTUAL);
}
}
return methodLookupResultBuilder.build();
@@ -2087,14 +2113,14 @@
@Override
public void registerInvokeVirtual(DexMethod method) {
MethodLookupResult lookup =
- appView.graphLens().lookupMethod(method, getContext().getReference(), Type.VIRTUAL);
+ appView.graphLens().lookupMethod(method, getContext().getReference(), VIRTUAL);
checkMethodReference(lookup.getReference(), OptionalBool.FALSE);
}
@Override
public void registerInvokeDirect(DexMethod method) {
MethodLookupResult lookup =
- appView.graphLens().lookupMethod(method, getContext().getReference(), Type.DIRECT);
+ appView.graphLens().lookupMethod(method, getContext().getReference(), DIRECT);
checkMethodReference(lookup.getReference(), OptionalBool.UNKNOWN);
}
@@ -2255,7 +2281,7 @@
forwardSourceCodeBuilder
.setReceiver(method.holder)
.setOriginalMethod(originalMethod)
- .setTargetReceiver(type == DIRECT ? method.holder : null)
+ .setTargetReceiver(type.isStatic() ? null : method.holder)
.setTarget(invocationTarget)
.setInvokeType(type)
.setIsInterface(isInterface);
@@ -2270,11 +2296,12 @@
case DIRECT:
registry.registerInvokeDirect(invocationTarget);
break;
-
case STATIC:
registry.registerInvokeStatic(invocationTarget);
break;
-
+ case VIRTUAL:
+ registry.registerInvokeVirtual(invocationTarget);
+ break;
default:
throw new Unreachable("Unexpected invocation type: " + type);
}