Use TopDownTraversal in MethodMinification
Bug: 213415674
Change-Id: Ib39cd680ddbdaf089d6633788ac96153ca742eaa
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index cd6c271..8a8d804 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.MethodResolutionResult;
-import com.android.tools.r8.graph.SubtypingInfo;
+import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
@@ -123,7 +123,6 @@
}
private final AppView<AppInfoWithLiveness> appView;
- private final SubtypingInfo subtypingInfo;
private final MemberNamingStrategy strategy;
private final Map<DexMethod, DexString> renaming = new IdentityHashMap<>();
@@ -141,12 +140,11 @@
MethodNameMinifier(
AppView<AppInfoWithLiveness> appView,
- SubtypingInfo subtypingInfo,
MemberNamingStrategy strategy) {
this.appView = appView;
- this.subtypingInfo = subtypingInfo;
this.strategy = strategy;
rootReservationState = MethodReservationState.createRoot(getReservationKeyTransform());
+ reservationStates.put(null, rootReservationState);
rootNamingState =
MethodNamingState.createRoot(getNamingKeyTransform(), strategy, rootReservationState);
namingStates.put(null, rootNamingState);
@@ -202,7 +200,8 @@
timing.end();
// Phase 4: Assign names top-down by traversing the subtype hierarchy.
timing.begin("Phase 4");
- assignNamesToClassesMethods(appView.dexItemFactory().objectType, rootNamingState);
+ assignNamesToClassesMethods();
+ renameMethodsInUnrelatedClasspathClasses();
timing.end();
timing.begin("Phase 5: non-rebound references");
renameNonReboundReferences(executorService);
@@ -211,21 +210,46 @@
return new MethodRenaming(renaming);
}
- private void assignNamesToClassesMethods(DexType type, MethodNamingState<?> parentNamingState) {
- MethodReservationState<?> reservationState =
- reservationStates.get(frontiers.getOrDefault(type, type));
- assert reservationState != null : "Could not find reservation state for " + type.toString();
- MethodNamingState<?> namingState =
- namingStates.computeIfAbsent(
- type, ignore -> parentNamingState.createChild(reservationState));
- DexClass holder = appView.definitionFor(type);
- if (holder != null && strategy.allowMemberRenaming(holder)) {
- for (DexEncodedMethod method : holder.allMethodsSorted()) {
- assignNameToMethod(holder, method, namingState);
- }
- }
- for (DexType subType : subtypingInfo.allImmediateExtendsSubtypes(type)) {
- assignNamesToClassesMethods(subType, namingState);
+ private void assignNamesToClassesMethods() {
+ TopDownClassHierarchyTraversal.forAllClasses(appView)
+ .excludeInterfaces()
+ .visit(
+ appView.appInfo().classes(),
+ clazz -> {
+ DexType type = clazz.type;
+ MethodReservationState<?> reservationState =
+ reservationStates.get(frontiers.getOrDefault(type, type));
+ assert reservationState != null
+ : "Could not find reservation state for " + type.toString();
+ MethodNamingState<?> namingState =
+ namingStates.computeIfAbsent(
+ type,
+ ignore ->
+ namingStates
+ .getOrDefault(clazz.superType, rootNamingState)
+ .createChild(reservationState));
+ DexClass holder = appView.definitionFor(type);
+ if (holder != null && strategy.allowMemberRenaming(holder)) {
+ for (DexEncodedMethod method : holder.allMethodsSorted()) {
+ assignNameToMethod(holder, method, namingState);
+ }
+ }
+ });
+ }
+
+ private void renameMethodsInUnrelatedClasspathClasses() {
+ if (appView.options().getProguardConfiguration().hasApplyMappingFile()) {
+ appView
+ .appInfo()
+ .forEachReferencedClasspathClass(
+ clazz -> {
+ for (DexEncodedMethod method : clazz.methods()) {
+ DexString reservedName = strategy.getReservedName(method, clazz);
+ if (reservedName != null && reservedName != method.getReference().name) {
+ renaming.put(method.getReference(), reservedName);
+ }
+ }
+ });
}
}
@@ -248,42 +272,30 @@
}
private void reserveNamesInClasses() {
- reserveNamesInClasses(
- appView.dexItemFactory().objectType,
- appView.dexItemFactory().objectType,
- rootReservationState);
+ TopDownClassHierarchyTraversal.forAllClasses(appView)
+ .visit(
+ appView.appInfo().classes(),
+ clazz -> {
+ DexType type = clazz.type;
+ DexType frontier = frontiers.getOrDefault(clazz.superType, type);
+ if (frontier != type || clazz.isProgramClass()) {
+ DexType existingValue = frontiers.put(clazz.type, frontier);
+ assert existingValue == null;
+ }
+ // If this is not a program class (or effectively a library class as it is missing)
+ // move the frontier forward. This will ensure all reservations are put on the library
+ // or classpath frontier for the program path.
+ allocateReservationStateAndReserve(
+ type,
+ frontier,
+ reservationStates.getOrDefault(clazz.superType, rootReservationState));
+ });
}
- private void reserveNamesInClasses(
- DexType type, DexType libraryFrontier, MethodReservationState<?> parentReservationState) {
- assert !appView.isInterface(type).isTrue();
-
- MethodReservationState<?> reservationState =
- allocateReservationStateAndReserve(type, libraryFrontier, parentReservationState);
-
- // If this is not a program class (or effectively a library class as it is missing) move the
- // frontier forward. This will ensure all reservations are put on the library or classpath
- // frontier for the program path.
- DexClass holder = appView.definitionFor(type);
- for (DexType subtype : subtypingInfo.allImmediateExtendsSubtypes(type)) {
- reserveNamesInClasses(
- subtype,
- holder == null || holder.isNotProgramClass() ? subtype : libraryFrontier,
- reservationState);
- }
- }
-
- private MethodReservationState<?> allocateReservationStateAndReserve(
+ private void allocateReservationStateAndReserve(
DexType type, DexType frontier, MethodReservationState<?> parent) {
- assert parent != null;
-
- if (frontier != type) {
- frontiers.put(type, frontier);
- }
-
MethodReservationState<?> state =
reservationStates.computeIfAbsent(frontier, ignore -> parent.createChild());
-
DexClass holder = appView.definitionFor(type);
if (holder != null) {
for (DexEncodedMethod method : shuffleMethods(holder.methods(), appView.options())) {
@@ -293,8 +305,6 @@
}
}
}
-
- return state;
}
private MethodNamingState<?> getOrAllocateMethodNamingStates(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index e7106a6..738be08 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -66,7 +66,7 @@
MemberNamingStrategy minifyMembers = new MinifierMemberNamingStrategy(appView);
timing.begin("MinifyMethods");
MethodRenaming methodRenaming =
- new MethodNameMinifier(appView, subtypingInfo, minifyMembers)
+ new MethodNameMinifier(appView, minifyMembers)
.computeRenaming(interfaces, executorService, timing);
timing.end();
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index 5c07e4a..7ee0681 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -147,7 +147,7 @@
new ApplyMappingMemberNamingStrategy(appView, memberNames);
timing.begin("MinifyMethods");
MethodRenaming methodRenaming =
- new MethodNameMinifier(appView, subtypingInfo, nameStrategy)
+ new MethodNameMinifier(appView, nameStrategy)
.computeRenaming(interfaces, executorService, timing);
// Amend the method renamings with the default interface methods.
methodRenaming.renaming.putAll(defaultInterfaceMethodImplementationNames);
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java
index ccdaae7..99554bf 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java
@@ -4,9 +4,12 @@
package com.android.tools.r8.naming.applymapping;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -38,6 +41,7 @@
public static class LibClassA implements LibInterfaceA {
@Override
+ @NoMethodStaticizing
public void foo() {
System.out.println("LibClassA::foo");
}
@@ -47,10 +51,12 @@
public static class LibClassB implements LibInterfaceB {
@Override
+ @NoMethodStaticizing
public void foo() {
System.out.println("LibClassB::foo");
}
+ @NoMethodStaticizing
public void bar() {
System.out.println("LibClassB::bar");
}
@@ -74,7 +80,7 @@
}
}
- private static final Class<?>[] LIBRARY_CLASSES = {
+ private static final Class<?>[] CLASSPATH_CLASSES = {
LibInterfaceA.class, LibInterfaceB.class, LibClassA.class, LibClassB.class
};
@@ -92,7 +98,7 @@
public void runOnJvm() throws Throwable {
Assume.assumeTrue(parameters.isCfRuntime());
testForJvm()
- .addProgramClasses(LIBRARY_CLASSES)
+ .addProgramClasses(CLASSPATH_CLASSES)
.addProgramClasses(PROGRAM_CLASSES)
.run(parameters.getRuntime(), ProgramClass.class)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
@@ -102,15 +108,18 @@
public void devirtualizingNoRenamingOfOverriddenNotKeptInterfaceMethods() throws Exception {
R8TestCompileResult libraryResult =
testForR8(parameters.getBackend())
- .addProgramClasses(LIBRARY_CLASSES)
- .addKeepClassAndMembersRulesWithAllowObfuscation(LibClassA.class, LibClassB.class)
+ .addProgramClasses(CLASSPATH_CLASSES)
+ .addKeepClassAndMembersRulesWithAllowObfuscation(LibClassA.class)
+ .addKeepMainRule(LibClassB.class)
+ .addKeepClassAndDefaultConstructor(LibClassB.class)
.addOptionsModification(options -> options.inlinerOptions().enableInlining = false)
+ .enableNoMethodStaticizingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile();
CodeInspector inspector = libraryResult.inspector();
- assertThat(inspector.clazz(LibClassA.class), isPresent());
- assertThat(inspector.clazz(LibClassB.class), isPresent());
+ assertThat(inspector.clazz(LibClassA.class), isPresentAndRenamed());
+ assertThat(inspector.clazz(LibClassB.class), isPresentAndNotRenamed());
// LibInterfaceX should have been moved into LibClassX.
assertThat(inspector.clazz(LibInterfaceA.class), not(isPresent()));
@@ -121,8 +130,7 @@
.noMinification()
.addProgramClasses(PROGRAM_CLASSES)
.addApplyMapping(libraryResult.getProguardMap())
- .addLibraryClasses(LIBRARY_CLASSES)
- .addDefaultRuntimeLibrary(parameters)
+ .addClasspathClasses(CLASSPATH_CLASSES)
.setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(libraryResult.writeToZip())
@@ -134,19 +142,20 @@
public void devirtualizingNoRenamingOfOverriddenKeptInterfaceMethods() throws Exception {
R8TestCompileResult libraryResult =
testForR8(parameters.getBackend())
- .addProgramClasses(LIBRARY_CLASSES)
+ .addProgramClasses(CLASSPATH_CLASSES)
.addKeepClassAndMembersRulesWithAllowObfuscation(
LibClassA.class, LibClassB.class, LibInterfaceA.class)
.addOptionsModification(options -> options.inlinerOptions().enableInlining = false)
+ .enableNoMethodStaticizingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile();
CodeInspector inspector = libraryResult.inspector();
- assertThat(inspector.clazz(LibClassA.class), isPresent());
- assertThat(inspector.clazz(LibClassB.class), isPresent());
+ assertThat(inspector.clazz(LibClassA.class), isPresentAndRenamed());
+ assertThat(inspector.clazz(LibClassB.class), isPresentAndRenamed());
// LibInterfaceA is now kept.
- assertThat(inspector.clazz(LibInterfaceA.class), isPresent());
+ assertThat(inspector.clazz(LibInterfaceA.class), isPresentAndRenamed());
assertThat(inspector.clazz(LibInterfaceB.class), not(isPresent()));
testForR8(parameters.getBackend())
@@ -154,8 +163,7 @@
.noMinification()
.addProgramClasses(PROGRAM_CLASSES)
.addApplyMapping(libraryResult.getProguardMap())
- .addLibraryClasses(LIBRARY_CLASSES)
- .addDefaultRuntimeLibrary(parameters)
+ .addClasspathClasses(CLASSPATH_CLASSES)
.setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(libraryResult.writeToZip())
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceInvokeTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceInvokeTest.java
index a1ef87f..b4b6807 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceInvokeTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceInvokeTest.java
@@ -4,13 +4,10 @@
package com.android.tools.r8.naming.applymapping;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -57,7 +54,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public ApplyMappingInterfaceInvokeTest(TestParameters parameters) {
@@ -65,14 +62,13 @@
}
@Test
- public void testInvokeVirtual()
- throws IOException, CompilationFailedException, ExecutionException {
+ public void testInvokeVirtual() throws Exception {
Class<?>[] classPathClasses = {I.class, A.class, B.class, C.class};
R8TestCompileResult libraryResult =
testForR8(parameters.getBackend())
.addProgramClasses(classPathClasses)
.addKeepAllClassesRuleWithAllowObfuscation()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile();
testForR8(parameters.getBackend())
.addClasspathClasses(classPathClasses)
@@ -80,7 +76,7 @@
.noMinification()
.noTreeShaking()
.addApplyMapping(libraryResult.getProguardMap())
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(libraryResult.writeToZip())
.run(parameters.getRuntime(), TestApp.class)