Reland "Remove redundant abstract method bridges"
This reverts commit 62b2b4b74d258e0ff4acf32e371e8dba5076f591.
Change-Id: I5b2933b47573d2de2bc639aba02b3c908a016b31
diff --git a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
index 8847f5f..5cbb1b6 100644
--- a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
@@ -10,12 +10,17 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.FailedResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemovalLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepMethodInfo;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -31,12 +36,7 @@
}
private DexClassAndMethod getTargetForRedundantBridge(ProgramMethod method) {
- // Clean-up the predicate check.
- if (appView.appInfo().isPinned(method.getReference())) {
- return null;
- }
DexEncodedMethod definition = method.getDefinition();
- // TODO(b/197490164): Remove if method is abstract.
BridgeInfo bridgeInfo = definition.getOptimizationInfo().getBridgeInfo();
boolean isBridge = definition.isBridge() || bridgeInfo != null;
if (!isBridge || definition.isAbstract()) {
@@ -140,6 +140,16 @@
ProgramMethodSet bridgesToRemoveForClass = ProgramMethodSet.create();
clazz.forEachProgramMethod(
method -> {
+ KeepMethodInfo keepInfo = appView.getKeepInfo(method);
+ if (!keepInfo.isShrinkingAllowed(appView.options())
+ || !keepInfo.isOptimizationAllowed(appView.options())) {
+ return;
+ }
+ if (isRedundantAbstractBridge(method)) {
+ // Record that the redundant bridge should be removed.
+ bridgesToRemoveForClass.add(method);
+ return;
+ }
DexClassAndMethod target = getTargetForRedundantBridge(method);
if (target != null) {
// Record that the redundant bridge should be removed.
@@ -167,6 +177,55 @@
return bridgesToRemove;
}
+ private boolean isRedundantAbstractBridge(ProgramMethod method) {
+ if (!method.getAccessFlags().isAbstract() || method.getDefinition().getCode() != null) {
+ return false;
+ }
+ DexProgramClass holder = method.getHolder();
+ if (holder.getSuperType() == null) {
+ assert holder.getType() == appView.dexItemFactory().objectType;
+ return false;
+ }
+ MethodResolutionResult superTypeResolution =
+ appView.appInfo().resolveMethodOn(holder.getSuperType(), method.getReference(), false);
+ if (superTypeResolution.isMultiMethodResolutionResult()) {
+ return false;
+ }
+ // Check if there is a definition in the super type hieararchy that is also abstract and has the
+ // same visibility.
+ if (superTypeResolution.isSingleResolution()) {
+ DexClassAndMethod resolutionPair =
+ superTypeResolution.asSingleResolution().getResolutionPair();
+ return resolutionPair.getDefinition().isAbstract()
+ && resolutionPair
+ .getDefinition()
+ .isAtLeastAsVisibleAsOtherInSameHierarchy(method.getDefinition(), appView)
+ && (!resolutionPair.getHolder().isInterface() || holder.getInterfaces().isEmpty());
+ }
+ // Only check for interfaces if resolving the method on super type causes NoSuchMethodError.
+ FailedResolutionResult failedResolutionResult = superTypeResolution.asFailedResolution();
+ if (failedResolutionResult == null
+ || !failedResolutionResult.isNoSuchMethodErrorResult(holder, appView.appInfo())
+ || holder.getInterfaces().isEmpty()) {
+ return false;
+ }
+ for (DexType iface : holder.getInterfaces()) {
+ SingleResolutionResult<?> singleIfaceResult =
+ appView
+ .appInfo()
+ .resolveMethodOn(iface, method.getReference(), true)
+ .asSingleResolution();
+ if (singleIfaceResult == null
+ || !singleIfaceResult.getResolvedMethod().isAbstract()
+ || !singleIfaceResult
+ .getResolvedMethod()
+ .isAtLeastAsVisibleAsOtherInSameHierarchy(method.getDefinition(), appView)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private void pruneApp(
Map<DexProgramClass, ProgramMethodSet> bridgesToRemove, ExecutorService executorService)
throws ExecutionException {
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeTest.java
index d569899..6d96680 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeTest.java
@@ -5,9 +5,8 @@
package com.android.tools.r8.memberrebinding;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isSynthetic;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
@@ -16,7 +15,6 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -58,10 +56,7 @@
inspector -> {
ClassSubject clazz = inspector.clazz(J.class);
assertThat(clazz, isPresent());
- // TODO(b/197490164): We should remove the bridge inserted here.
- assertEquals(1, clazz.allMethods().size());
- FoundMethodSubject foundMethodSubject = clazz.allMethods().get(0);
- assertThat(foundMethodSubject, isSynthetic());
+ assertTrue(clazz.allMethods().isEmpty());
});
}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java
index 4551dbe..dd2f0c1 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java
@@ -5,9 +5,8 @@
package com.android.tools.r8.memberrebinding;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isSynthetic;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
@@ -18,7 +17,6 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -63,10 +61,7 @@
inspector -> {
ClassSubject clazz = inspector.clazz(A.class);
assertThat(clazz, isPresent());
- // TODO(b/197490164): We should remove the bridge inserted here.
- assertEquals(1, clazz.virtualMethods().size());
- FoundMethodSubject foundMethodSubject = clazz.virtualMethods().get(0);
- assertThat(foundMethodSubject, isSynthetic());
+ assertTrue(clazz.virtualMethods().isEmpty());
});
}