Only consider abstract methods that shadow abstract methods
Bug: 198542172
Change-Id: I08a610f7e31b37a23b51914977e23498ebe400cb
diff --git a/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java b/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
index 4123c10..e0c7f83 100644
--- a/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AbstractMethodRemover.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult;
import com.android.tools.r8.utils.IterableUtils;
-import java.util.ArrayList;
+import com.android.tools.r8.utils.ListUtils;
import java.util.List;
/**
@@ -58,32 +58,31 @@
if (virtualMethods == null) {
return null;
}
- // Removal of abstract methods is rare, so avoid copying the array until we find one.
- List<DexEncodedMethod> methods = null;
- for (int i = 0; i < virtualMethods.size(); i++) {
- DexEncodedMethod method = virtualMethods.get(i);
- if (scope.addMethodIfMoreVisible(method) != AddMethodIfMoreVisibleResult.NOT_ADDED
- || !method.accessFlags.isAbstract()
- || appView.appInfo().isPinned(method.getReference())) {
- if (methods != null) {
- methods.add(method);
- }
- } else {
- if (methods == null) {
- methods = new ArrayList<>(virtualMethods.size() - 1);
- for (int j = 0; j < i; j++) {
- methods.add(virtualMethods.get(j));
- }
- }
- if (Log.ENABLED) {
- Log.debug(getClass(), "Removing abstract method %s.", method.getReference());
- }
- }
+ // Removal of abstract methods is rare, ListUtils.filterOrElse does no copying if nothing is
+ // filtered out.
+ List<DexEncodedMethod> filteredMethods =
+ ListUtils.filterOrElse(virtualMethods, this::isNonAbstractPinnedOrWideningVisibility);
+ return filteredMethods == virtualMethods
+ ? null
+ : filteredMethods.toArray(DexEncodedMethod.EMPTY_ARRAY);
+ }
+
+ private boolean isNonAbstractPinnedOrWideningVisibility(DexEncodedMethod method) {
+ if (!method.accessFlags.isAbstract()) {
+ return true;
}
- if (methods != null) {
- return methods.toArray(DexEncodedMethod.EMPTY_ARRAY);
+ // Check if the method widens visibility. Adding to the scope mutates it.
+ if (scope.addMethodIfMoreVisible(method) != AddMethodIfMoreVisibleResult.NOT_ADDED) {
+ return true;
}
- return null;
+ if (appView.appInfo().isPinned(method.getReference())) {
+ return true;
+ }
+ // We will filter the method out since it is not pinned.
+ if (Log.ENABLED) {
+ Log.debug(getClass(), "Removing abstract method %s.", method.getReference());
+ }
+ return false;
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index 891f1c2..8fd84fc 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -159,6 +159,14 @@
return mapOrElse(list, fn, list);
}
+ /**
+ * Takes elements from the input list depending on the predicate being true. Returns the filtered
+ * list otherwise returns the original list of none were removed.
+ */
+ public static <T> List<T> filterOrElse(List<T> list, Predicate<T> predicate) {
+ return mapOrElse(list, element -> predicate.test(element) ? element : null, list);
+ }
+
public static <T> ArrayList<T> newArrayList(T element) {
ArrayList<T> list = new ArrayList<>(1);
list.add(element);
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/KeepAbstractMethodShadowingTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/KeepAbstractMethodShadowingTest.java
index 1836e19..04cacdf 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/KeepAbstractMethodShadowingTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/KeepAbstractMethodShadowingTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.bridgeremoval;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.TestBase;
@@ -53,15 +52,13 @@
.addKeepMainRule(Main.class)
.addKeepClassAndMembersRules(A.class)
.run(parameters.getRuntime(), Main.class)
- // TODO(b/198542172): Should be AbstractMethodError.
- .assertSuccessWithOutputLines("Hello World")
- .inspect(
+ .assertFailureWithErrorThatThrows(AbstractMethodError.class)
+ .inspectFailure(
inspector -> {
ClassSubject clazz = inspector.clazz(B.class);
assertThat(clazz, isPresent());
MethodSubject foo = clazz.uniqueMethodWithName("foo");
- // TODO(b/198542172): Should be present.
- assertThat(foo, not(isPresent()));
+ assertThat(foo, isPresent());
});
}