Synthesize rules to disallow shrinking of Fragment/ZygotePreload inits
Bug: b/281468010
Change-Id: I86dfb7f4e5a3ae3cd013790cb58a239122832e14
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 634a532..d44a39b 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -317,21 +317,13 @@
appView.getSyntheticItems().commit(appView.appInfo().app())));
}
- List<ProguardConfigurationRule> synthesizedProguardRules = new ArrayList<>();
timing.begin("Strip unused code");
timing.begin("Before enqueuer");
RuntimeTypeCheckInfo.Builder classMergingEnqueuerExtensionBuilder =
new RuntimeTypeCheckInfo.Builder(appView);
+ List<ProguardConfigurationRule> synthesizedProguardRules;
try {
- // Add synthesized -assumenosideeffects from min api if relevant.
- if (options.isGeneratingDex()) {
- if (!ProguardConfigurationUtils.hasExplicitAssumeValuesOrAssumeNoSideEffectsRuleForMinSdk(
- options.itemFactory, options.getProguardConfiguration().getRules())) {
- synthesizedProguardRules.add(
- ProguardConfigurationUtils.buildAssumeNoSideEffectsRuleForApiLevel(
- options.itemFactory, options.getMinApiLevel()));
- }
- }
+ synthesizedProguardRules = ProguardConfigurationUtils.synthesizeRules(appView);
ProfileCollectionAdditions profileCollectionAdditions =
ProfileCollectionAdditions.create(appView);
AssumeInfoCollection.Builder assumeInfoCollectionBuilder = AssumeInfoCollection.builder();
diff --git a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java
index c290822..0ca1265 100644
--- a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java
+++ b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java
@@ -4,42 +4,9 @@
package com.android.tools.r8.optimize.redundantbridgeremoval;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexLibraryClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.SetUtils;
-import java.util.Collections;
-import java.util.Set;
-
public class RedundantBridgeRemovalOptions {
- private final InternalOptions options;
-
private boolean enableRetargetingOfConstructorBridgeCalls = true;
- private Set<DexType> noConstructorShrinkingHierarchies;
-
- public RedundantBridgeRemovalOptions(InternalOptions options) {
- this.options = options;
- }
-
- public void clearNoConstructorShrinkingHierarchiesForTesting() {
- noConstructorShrinkingHierarchies = Collections.emptySet();
- }
-
- public RedundantBridgeRemovalOptions ensureInitialized() {
- if (noConstructorShrinkingHierarchies == null) {
- DexItemFactory dexItemFactory = options.dexItemFactory();
- noConstructorShrinkingHierarchies =
- SetUtils.newIdentityHashSet(
- dexItemFactory.androidAppFragment, dexItemFactory.androidAppZygotePreload);
- }
- return this;
- }
-
- public boolean isPlatformReflectingOnDefaultConstructorInSubclasses(DexLibraryClass clazz) {
- return noConstructorShrinkingHierarchies.contains(clazz.getType());
- }
public boolean isRetargetingOfConstructorBridgeCallsEnabled() {
return enableRetargetingOfConstructorBridgeCalls;
diff --git a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
index ab054e2..7ae9ca2 100644
--- a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.optimize.redundantbridgeremoval;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
@@ -25,20 +24,13 @@
import com.android.tools.r8.optimize.argumentpropagation.utils.ProgramClassesBidirectedGraph;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepMethodInfo;
-import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.WorkList;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import com.google.common.collect.Iterables;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import java.util.function.Consumer;
public class RedundantBridgeRemover {
@@ -46,16 +38,13 @@
private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
private final RedundantBridgeRemovalOptions redundantBridgeRemovalOptions;
- private final InvokedReflectivelyFromPlatformAnalysis invokedReflectivelyFromPlatformAnalysis =
- new InvokedReflectivelyFromPlatformAnalysis();
private final RedundantBridgeRemovalLens.Builder lensBuilder =
new RedundantBridgeRemovalLens.Builder();
public RedundantBridgeRemover(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
this.immediateSubtypingInfo = ImmediateProgramSubtypingInfo.create(appView);
- this.redundantBridgeRemovalOptions =
- appView.options().getRedundantBridgeRemovalOptions().ensureInitialized();
+ this.redundantBridgeRemovalOptions = appView.options().getRedundantBridgeRemovalOptions();
}
private DexClassAndMethod getTargetForRedundantBridge(ProgramMethod method) {
@@ -76,9 +65,6 @@
if (!isTargetingSuperMethod(method, targetExtractor.getKind(), target)) {
return null;
}
- if (invokedReflectivelyFromPlatformAnalysis.isMaybeInvokedReflectivelyFromPlatform(method)) {
- return null;
- }
// This is a visibility forward, so check for the direct target.
DexClassAndMethod targetMethod =
appView.appInfo().unsafeResolveMethodDueToDexFormatLegacy(target).getResolutionPair();
@@ -329,88 +315,4 @@
// Empty.
}
}
-
- class InvokedReflectivelyFromPlatformAnalysis {
-
- // Maps each class to a boolean indicating if the class inherits from android.app.Fragment or
- // android.app.ZygotePreload.
- private final Map<DexClass, Boolean> cache = new ConcurrentHashMap<>();
-
- boolean isMaybeInvokedReflectivelyFromPlatform(ProgramMethod method) {
- return method.getDefinition().isDefaultInstanceInitializer()
- && !method.getHolder().isAbstract()
- && computeIsPlatformReflectingOnDefaultConstructor(method.getHolder());
- }
-
- private boolean computeIsPlatformReflectingOnDefaultConstructor(DexProgramClass clazz) {
- Boolean cacheResult = cache.get(clazz);
- if (cacheResult != null) {
- return cacheResult;
- }
- WorkList.<WorklistItem>newIdentityWorkList(new NotProcessedWorklistItem(clazz))
- .process(WorklistItem::accept);
- assert cache.containsKey(clazz);
- return cache.get(clazz);
- }
-
- abstract class WorklistItem implements Consumer<WorkList<WorklistItem>> {
-
- protected final DexClass clazz;
-
- WorklistItem(DexClass clazz) {
- this.clazz = clazz;
- }
-
- Iterable<DexClass> getImmediateSupertypes() {
- return IterableUtils.flatMap(
- clazz.allImmediateSupertypes(),
- supertype -> {
- DexClass definition = appView.definitionFor(supertype);
- return definition != null
- ? Collections.singletonList(definition)
- : Collections.emptyList();
- });
- }
- }
-
- class NotProcessedWorklistItem extends WorklistItem {
-
- NotProcessedWorklistItem(DexClass clazz) {
- super(clazz);
- }
-
- @Override
- public void accept(WorkList<WorklistItem> worklist) {
- // Enqueue a worklist item to process the current class after processing its super classes.
- worklist.addFirstIgnoringSeenSet(new ProcessedWorklistItem(clazz));
- // Enqueue all superclasses for processing.
- for (DexClass supertype : getImmediateSupertypes()) {
- if (!cache.containsKey(supertype)) {
- worklist.addFirstIgnoringSeenSet(new NotProcessedWorklistItem(supertype));
- }
- }
- }
- }
-
- class ProcessedWorklistItem extends WorklistItem {
-
- ProcessedWorklistItem(DexClass clazz) {
- super(clazz);
- }
-
- @Override
- public void accept(WorkList<WorklistItem> worklist) {
- cache.put(
- clazz,
- Iterables.any(
- getImmediateSupertypes(),
- supertype ->
- cache.get(supertype)
- || (supertype.isLibraryClass()
- && redundantBridgeRemovalOptions
- .isPlatformReflectingOnDefaultConstructorInSubclasses(
- supertype.asLibraryClass()))));
- }
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java b/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
index 9bd37a5..cdf0264 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
@@ -132,8 +132,9 @@
return isSet(Constants.ACC_FINAL);
}
- public void setAbstract() {
+ public ProguardAccessFlags setAbstract() {
set(Constants.ACC_ABSTRACT);
+ return this;
}
public boolean isAbstract() {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index b050658..2c51922 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -2384,6 +2384,10 @@
this.wildcards = wildcards;
}
+ static IdentifierPatternWithWildcards init() {
+ return withoutWildcards("<init>");
+ }
+
static IdentifierPatternWithWildcards withoutWildcards(String pattern) {
return new IdentifierPatternWithWildcards(pattern, ImmutableList.of());
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index 94ab239..9b0ed2e 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -4,17 +4,53 @@
package com.android.tools.r8.shaking;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.LongInterval;
import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
public class ProguardConfigurationUtils {
- public static ProguardAssumeNoSideEffectRule buildAssumeNoSideEffectsRuleForApiLevel(
+ public static List<ProguardConfigurationRule> synthesizeRules(AppView<?> appView) {
+ List<ProguardConfigurationRule> synthesizedRules = new ArrayList<>();
+ DexItemFactory factory = appView.dexItemFactory();
+ InternalOptions options = appView.options();
+ // Add synthesized -assumenosideeffects from min api if relevant.
+ if (options.isGeneratingDex()) {
+ if (!hasExplicitAssumeValuesOrAssumeNoSideEffectsRuleForMinSdk(
+ factory, options.getProguardConfiguration().getRules())) {
+ synthesizedRules.add(
+ buildAssumeNoSideEffectsRuleForApiLevel(factory, options.getMinApiLevel()));
+ }
+ }
+ // Add synthesized -keepclassmembers rules for the default initializer of classes that inherit
+ // from android.app.Fragment and android.app.ZygotePreload. This is needed since the Android
+ // Platform may reflectively access these instance initializers.
+ DexClass androidAppFragment =
+ appView.appInfo().definitionForWithoutExistenceAssert(factory.androidAppFragment);
+ if (androidAppFragment != null) {
+ synthesizedRules.add(
+ buildKeepClassMembersNoShrinkingOfInitializerOnSubclasses(factory, androidAppFragment));
+ }
+ DexClass androidAppZygotePreload =
+ appView.appInfo().definitionForWithoutExistenceAssert(factory.androidAppZygotePreload);
+ if (androidAppZygotePreload != null) {
+ synthesizedRules.add(
+ buildKeepClassMembersNoShrinkingOfInitializerOnSubclasses(
+ factory, androidAppZygotePreload));
+ }
+ return synthesizedRules;
+ }
+
+ private static ProguardAssumeNoSideEffectRule buildAssumeNoSideEffectsRuleForApiLevel(
DexItemFactory factory, AndroidApiLevel apiLevel) {
Origin synthesizedFromApiLevel =
new Origin(Origin.root()) {
@@ -53,7 +89,7 @@
* Check if an explicit rule matching the field public static final int
* android.os.Build$VERSION.SDK_INT is present.
*/
- public static boolean hasExplicitAssumeValuesOrAssumeNoSideEffectsRuleForMinSdk(
+ private static boolean hasExplicitAssumeValuesOrAssumeNoSideEffectsRuleForMinSdk(
DexItemFactory factory, List<ProguardConfigurationRule> rules) {
for (ProguardConfigurationRule rule : rules) {
if (!(rule instanceof ProguardAssumeValuesRule
@@ -108,4 +144,28 @@
}
return false;
}
+
+ // -keepclassmembers,allow* !abstract class * extends T { void <init>(); }
+ private static ProguardKeepRule buildKeepClassMembersNoShrinkingOfInitializerOnSubclasses(
+ DexItemFactory factory, DexClass clazz) {
+ return ProguardKeepRule.builder()
+ .setClassNames(ProguardClassNameList.singletonList(ProguardTypeMatcher.allClassesMatcher()))
+ .setClassType(ProguardClassType.CLASS)
+ .setInheritanceClassName(ProguardTypeMatcher.create(clazz.getType()))
+ .setInheritanceIsExtends(!clazz.isInterface())
+ .setMemberRules(
+ Collections.singletonList(
+ ProguardMemberRule.builder()
+ .setRuleType(ProguardMemberType.INIT)
+ .setName(IdentifierPatternWithWildcards.init())
+ .setArguments(Collections.emptyList())
+ .setTypeMatcher(ProguardTypeMatcher.create(factory.voidType))
+ .build()))
+ .setNegatedClassAccessFlags(new ProguardAccessFlags().setAbstract())
+ .setOrigin(clazz.getOrigin())
+ .setType(ProguardKeepRuleType.KEEP_CLASS_MEMBERS)
+ .updateModifiers(
+ modifiersBuilder -> modifiersBuilder.setAllowsAll().setAllowsShrinking(false).build())
+ .build();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
index a2619f7..ec825c3 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
@@ -18,6 +18,16 @@
private Builder() {}
+ public Builder setAllowsAll() {
+ setAllowsAccessModification(true);
+ setAllowsAnnotationRemoval(true);
+ setAllowsObfuscation(true);
+ setAllowsOptimization(true);
+ setAllowsRepackaging(true);
+ setAllowsShrinking(true);
+ return this;
+ }
+
public Builder setAllowsAccessModification(boolean allowsAccessModification) {
this.allowsAccessModification = allowsAccessModification;
return this;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
index 19bd876..fee5278 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -125,6 +125,10 @@
return new MatchSpecificType(type);
}
+ public static ProguardTypeMatcher allClassesMatcher() {
+ return MatchClassTypes.MATCH_CLASS_TYPES;
+ }
+
public static ProguardTypeMatcher defaultAllMatcher() {
return MatchAllTypes.MATCH_ALL_TYPES;
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 0f8c7cf..8a733a5 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -890,7 +890,7 @@
new OpenClosedInterfacesOptions();
private final ProtoShrinkingOptions protoShrinking = new ProtoShrinkingOptions();
private final RedundantBridgeRemovalOptions redundantBridgeRemovalOptions =
- new RedundantBridgeRemovalOptions(this);
+ new RedundantBridgeRemovalOptions();
private final KotlinOptimizationOptions kotlinOptimizationOptions =
new KotlinOptimizationOptions();
private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
diff --git a/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
index 7dacf64..f4d629f 100644
--- a/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
@@ -5,15 +5,13 @@
package com.android.tools.r8.shaking.constructor;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assume.assumeFalse;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.apimodel.ApiModelingTestHelper;
-import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -41,15 +39,11 @@
private static List<byte[]> transformedLibraryClassFileData;
@Parameter(0)
- public boolean enableModeling;
-
- @Parameter(1)
public TestParameters parameters;
- @Parameters(name = "{1}, modeling: {0}")
- public static List<Object[]> data() {
- return buildParameters(
- BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
@BeforeClass
@@ -60,7 +54,6 @@
@Test
public void testRuntime() throws Exception {
- assumeFalse(enableModeling);
testForRuntime(parameters)
.addProgramClassFileData(transformedProgramClassFileData)
.addProgramClassFileData(transformedLibraryClassFileData)
@@ -75,14 +68,6 @@
.addLibraryClassFileData(transformedLibraryClassFileData)
.addLibraryFiles(parameters.getDefaultRuntimeLibrary())
.addKeepMainRule(Main.class)
- .applyIf(
- !enableModeling,
- testBuilder ->
- testBuilder.addOptionsModification(
- options ->
- options
- .getRedundantBridgeRemovalOptions()
- .clearNoConstructorShrinkingHierarchiesForTesting()))
// Since Fragment is first defined in API 11.
.apply(ApiModelingTestHelper::disableStubbingOfClasses)
.enableNeverClassInliningAnnotations()
@@ -91,10 +76,7 @@
.inspect(this::inspect)
.addRunClasspathClassFileData(transformedLibraryClassFileData)
.run(parameters.getRuntime(), Main.class)
- .applyIf(
- enableModeling || !parameters.canHaveNonReboundConstructorInvoke(),
- runResult -> runResult.assertSuccessWithOutput(EXPECTED_OUTPUT),
- runResult -> runResult.assertFailureWithErrorThatThrows(NoSuchMethodException.class));
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
}
private static List<byte[]> getTransformedProgramClasses() throws Exception {
@@ -144,15 +126,11 @@
private void inspect(CodeInspector inspector) {
ClassSubject myFragmentClassSubject = inspector.clazz(MyFragment.class);
assertThat(myFragmentClassSubject, isPresent());
- assertThat(
- myFragmentClassSubject.init(),
- onlyIf(enableModeling || !parameters.canHaveNonReboundConstructorInvoke(), isPresent()));
+ assertThat(myFragmentClassSubject.init(), isPresent());
ClassSubject myZygotePreloadClassSubject = inspector.clazz(MyZygotePreload.class);
assertThat(myZygotePreloadClassSubject, isPresent());
- assertThat(
- myZygotePreloadClassSubject.init(),
- onlyIf(enableModeling || !parameters.canHaveNonReboundConstructorInvoke(), isPresent()));
+ assertThat(myZygotePreloadClassSubject.init(), isPresent());
}
// Library classes.