Reenable constructor shrinking
Bug: b/246679983
Change-Id: Ic201e5e23144a185080e76042c9e8ce907243ec3
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index d96d546..2ff0966 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -67,10 +67,10 @@
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
import com.android.tools.r8.optimize.MemberRebindingIdentityLensFactory;
-import com.android.tools.r8.optimize.RedundantBridgeRemover;
import com.android.tools.r8.optimize.bridgehoisting.BridgeHoisting;
import com.android.tools.r8.optimize.interfaces.analysis.CfOpenClosedInterfacesAnalysis;
import com.android.tools.r8.optimize.proto.ProtoNormalizer;
+import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemover;
import com.android.tools.r8.origin.CommandLineOrigin;
import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
import com.android.tools.r8.profile.art.rewriting.ProfileCollectionAdditions;
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 f30986c..f9a5279 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -475,9 +475,9 @@
return isInstanceInitializer() || willBeInlinedIntoInstanceInitializer(dexItemFactory);
}
- public boolean isDefaultInitializer() {
+ public boolean isDefaultInstanceInitializer() {
checkIfObsolete();
- return isInstanceInitializer() && getReference().proto.parameters.isEmpty();
+ return isInstanceInitializer() && getParameters().isEmpty();
}
public boolean isClassInitializer() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 9cdb7f2..5c14069 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -592,6 +592,9 @@
public final DexType javaUtilSetType = createStaticallyKnownType("Ljava/util/Set;");
public final DexType androidAppActivity = createStaticallyKnownType("Landroid/app/Activity;");
+ public final DexType androidAppFragment = createStaticallyKnownType("Landroid/app/Fragment;");
+ public final DexType androidAppZygotePreload =
+ createStaticallyKnownType("Landroid/app/ZygotePreload;");
public final DexType androidOsBuildType = createStaticallyKnownType("Landroid/os/Build;");
public final DexType androidOsBuildVersionType =
createStaticallyKnownType("Landroid/os/Build$VERSION;");
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
index 125b53c..c2e13af 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/CallSiteInformation.java
@@ -86,7 +86,7 @@
continue;
}
- if (method.getDefinition().isDefaultInitializer()
+ if (method.getDefinition().isDefaultInstanceInitializer()
&& appView.hasProguardCompatibilityActions()
&& appView.getProguardCompatibilityActions().isCompatInstantiated(method.getHolder())) {
continue;
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
new file mode 100644
index 0000000..a902764
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+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 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());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
similarity index 72%
rename from src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
rename to src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
index 763c40f..c050859 100644
--- a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
@@ -1,9 +1,10 @@
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.optimize;
+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;
@@ -15,23 +16,35 @@
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;
import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
-import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemovalLens;
+import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
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.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.function.Consumer;
public class RedundantBridgeRemover {
private final AppView<AppInfoWithLiveness> appView;
+ private final RedundantBridgeRemovalOptions redundantBridgeRemovalOptions;
+
+ private final InvokedReflectivelyFromPlatformAnalysis invokedReflectivelyFromPlatformAnalysis =
+ new InvokedReflectivelyFromPlatformAnalysis();
public RedundantBridgeRemover(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
+ this.redundantBridgeRemovalOptions =
+ appView.options().getRedundantBridgeRemovalOptions().ensureInitialized();
}
private DexClassAndMethod getTargetForRedundantBridge(ProgramMethod method) {
@@ -52,6 +65,9 @@
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();
@@ -236,4 +252,88 @@
});
appView.pruneItems(prunedItemsBuilder.build(), executorService);
}
+
+ 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/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index f9bd041..3a51f8b 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -74,6 +74,7 @@
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.MapVersion;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer;
+import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemovalOptions;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.profile.art.ArtProfileOptions;
@@ -862,6 +863,8 @@
private final OpenClosedInterfacesOptions openClosedInterfacesOptions =
new OpenClosedInterfacesOptions();
private final ProtoShrinkingOptions protoShrinking = new ProtoShrinkingOptions();
+ private final RedundantBridgeRemovalOptions redundantBridgeRemovalOptions =
+ new RedundantBridgeRemovalOptions(this);
private final KotlinOptimizationOptions kotlinOptimizationOptions =
new KotlinOptimizationOptions();
private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
@@ -930,6 +933,10 @@
return cfCodeAnalysisOptions;
}
+ public RedundantBridgeRemovalOptions getRedundantBridgeRemovalOptions() {
+ return redundantBridgeRemovalOptions;
+ }
+
public DumpInputFlags getDumpInputFlags() {
return dumpInputFlags;
}
@@ -2884,8 +2891,7 @@
}
public boolean canHaveNonReboundConstructorInvoke() {
- // TODO(b/246679983): Turned off while diagnosing b/246679983.
- return false && isGeneratingDex() && minApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L);
+ return isGeneratingDex() && minApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L);
}
// b/238399429 Some art 6 vms have issues with multiple monitors in the same method
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java
index c22f43e..206a8ab 100644
--- a/src/test/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -58,8 +58,7 @@
* runtime error.
*/
public boolean canHaveNonReboundConstructorInvoke() {
- // TODO(b/246679983): Turned off while diagnosing b/246679983.
- return false && isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
+ return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
}
public boolean canUseDefaultAndStaticInterfaceMethods() {
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
index 2d38860..c83d81d 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
@@ -75,15 +75,22 @@
}
private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector) {
- inspect(profileInspector, inspector, false);
+ inspect(profileInspector, inspector, false, false);
}
private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector) {
- inspect(profileInspector, inspector, parameters.isCfRuntime());
+ inspect(
+ profileInspector,
+ inspector,
+ parameters.canHaveNonReboundConstructorInvoke(),
+ parameters.isCfRuntime());
}
public void inspect(
- ArtProfileInspector profileInspector, CodeInspector inspector, boolean canUseLambdas) {
+ ArtProfileInspector profileInspector,
+ CodeInspector inspector,
+ boolean canHaveNonReboundConstructorInvoke,
+ boolean canUseLambdas) {
ClassSubject mainClassSubject = inspector.clazz(Main.class);
assertThat(mainClassSubject, isPresent());
@@ -96,21 +103,26 @@
assertThat(lambdaClassSubject, notIf(isPresent(), canUseLambdas));
MethodSubject lambdaInitializerSubject = lambdaClassSubject.uniqueInstanceInitializer();
- assertThat(lambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
+ assertThat(
+ lambdaInitializerSubject,
+ notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
MethodSubject lambdaMainMethodSubject =
lambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
assertThat(lambdaMainMethodSubject, notIf(isPresent(), canUseLambdas));
if (canUseLambdas) {
- profileInspector.assertContainsMethodRule(mainMethodSubject).assertContainsNoOtherRules();
+ profileInspector.assertContainsMethodRule(mainMethodSubject);
} else {
profileInspector
.assertContainsClassRules(lambdaClassSubject)
- .assertContainsMethodRules(
- mainMethodSubject, lambdaInitializerSubject, lambdaMainMethodSubject)
- .assertContainsNoOtherRules();
+ .assertContainsMethodRules(mainMethodSubject, lambdaMainMethodSubject)
+ .applyIf(
+ !canHaveNonReboundConstructorInvoke,
+ i -> i.assertContainsMethodRule(lambdaInitializerSubject));
}
+
+ profileInspector.assertContainsNoOtherRules();
}
static class Main {
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
index 7d9e9ab..577518d 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
@@ -104,17 +104,22 @@
private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector)
throws Exception {
- inspect(profileInspector, inspector, parameters.canUseNestBasedAccessesWhenDesugaring());
+ inspect(profileInspector, inspector, false, parameters.canUseNestBasedAccessesWhenDesugaring());
}
private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector)
throws Exception {
- inspect(profileInspector, inspector, parameters.canUseNestBasedAccesses());
+ inspect(
+ profileInspector,
+ inspector,
+ parameters.canHaveNonReboundConstructorInvoke(),
+ parameters.canUseNestBasedAccesses());
}
private void inspect(
ArtProfileInspector profileInspector,
CodeInspector inspector,
+ boolean canHaveNonReboundConstructorInvoke,
boolean canUseNestBasedAccesses)
throws Exception {
ClassSubject nestMemberClassSubject = inspector.clazz(NestMember.class);
@@ -128,7 +133,7 @@
syntheticConstructorArgumentClassSubject, notIf(isPresent(), canUseNestBasedAccesses));
MethodSubject instanceInitializer = nestMemberClassSubject.init();
- assertThat(instanceInitializer, isPresent());
+ assertThat(instanceInitializer, notIf(isPresent(), canHaveNonReboundConstructorInvoke));
MethodSubject instanceInitializerWithSyntheticArgumentSubject =
syntheticConstructorArgumentClassSubject.isPresent()
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
index 5f1886d..6d03704 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
@@ -149,6 +149,7 @@
profileInspector,
inspector,
SyntheticItemsTestUtils.syntheticRecordTagClass(),
+ false,
parameters.canUseNestBasedAccessesWhenDesugaring(),
parameters.canUseRecordsWhenDesugaring());
}
@@ -158,6 +159,7 @@
profileInspector,
inspector,
RECORD_REFERENCE,
+ parameters.canHaveNonReboundConstructorInvoke(),
parameters.canUseNestBasedAccesses(),
parameters.canUseRecords());
}
@@ -166,6 +168,7 @@
ArtProfileInspector profileInspector,
CodeInspector inspector,
ClassReference recordClassReference,
+ boolean canHaveNonReboundConstructorInvoke,
boolean canUseNestBasedAccesses,
boolean canUseRecords) {
ClassSubject mainClassSubject = inspector.clazz(MAIN_REFERENCE);
@@ -177,11 +180,14 @@
ClassSubject recordTagClassSubject = inspector.clazz(recordClassReference);
assertThat(recordTagClassSubject, notIf(isPresent(), canUseRecords));
if (!canUseRecords) {
- assertEquals(1, recordTagClassSubject.allMethods().size());
+ assertEquals(
+ canHaveNonReboundConstructorInvoke ? 0 : 1, recordTagClassSubject.allMethods().size());
}
MethodSubject recordTagInstanceInitializerSubject = recordTagClassSubject.init();
- assertThat(recordTagInstanceInitializerSubject, notIf(isPresent(), canUseRecords));
+ assertThat(
+ recordTagInstanceInitializerSubject,
+ notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseRecords));
ClassSubject personRecordClassSubject = inspector.clazz(PERSON_REFERENCE);
assertThat(personRecordClassSubject, isPresent());
@@ -290,11 +296,13 @@
hashCodeHelperClassSubject,
toStringHelperClassSubject)
.assertContainsMethodRules(
- recordTagInstanceInitializerSubject,
equalsHelperMethodSubject,
getFieldsAsObjectsMethodSubject,
hashCodeHelperMethodSubject,
- toStringHelperMethodSubject))
+ toStringHelperMethodSubject)
+ .applyIf(
+ !canHaveNonReboundConstructorInvoke,
+ j -> j.assertContainsMethodRule(recordTagInstanceInitializerSubject)))
.assertContainsNoOtherRules();
}
}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
index 385f94b..ef6b3a3 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
@@ -68,6 +68,7 @@
public void inspect(
ArtProfileInspector profileInspector,
CodeInspector inspector,
+ boolean canHaveNonReboundConstructorInvoke,
boolean canUseLambdas,
boolean canAccessModifyLambdaImplementationMethods) {
ClassSubject mainClassSubject = inspector.clazz(Main.class);
@@ -106,7 +107,9 @@
assertThat(lambdaClassSubject, notIf(isPresent(), canUseLambdas));
MethodSubject lambdaInitializerSubject = lambdaClassSubject.uniqueInstanceInitializer();
- assertThat(lambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
+ assertThat(
+ lambdaInitializerSubject,
+ notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
MethodSubject lambdaMainMethodSubject =
lambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
@@ -119,7 +122,9 @@
MethodSubject otherLambdaInitializerSubject =
otherLambdaClassSubject.uniqueInstanceInitializer();
- assertThat(otherLambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
+ assertThat(
+ otherLambdaInitializerSubject,
+ notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
MethodSubject otherLambdaMainMethodSubject =
otherLambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
@@ -149,8 +154,12 @@
// interface method implementation does not need to be included in the profile.
profileInspector
.assertContainsClassRules(lambdaClassSubject, otherLambdaClassSubject)
- .assertContainsMethodRules(
- mainMethodSubject, lambdaInitializerSubject, otherLambdaInitializerSubject)
+ .assertContainsMethodRules(mainMethodSubject)
+ .applyIf(
+ !canHaveNonReboundConstructorInvoke,
+ i ->
+ i.assertContainsMethodRules(
+ lambdaInitializerSubject, otherLambdaInitializerSubject))
.assertContainsNoOtherRules();
break;
case IMPLEMENTATION_METHOD:
@@ -221,11 +230,16 @@
}
private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector) {
- artProfileInputOutput.inspect(profileInspector, inspector, false, true);
+ artProfileInputOutput.inspect(profileInspector, inspector, false, false, true);
}
private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector) {
- artProfileInputOutput.inspect(profileInspector, inspector, parameters.isCfRuntime(), false);
+ artProfileInputOutput.inspect(
+ profileInspector,
+ inspector,
+ parameters.canHaveNonReboundConstructorInvoke(),
+ parameters.isCfRuntime(),
+ false);
}
static class Main {
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 534bbb9..7dacf64 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,14 +5,21 @@
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;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -22,48 +29,168 @@
@RunWith(Parameterized.class)
public class ForwardingConstructorUsedFromPlatformShakingOnDexTest extends TestBase {
+ private static final String APPLICATION_INFO_DESCRIPTOR = "Landroid/content/pm/ApplicationInfo;";
+ private static final String FRAGMENT_DESCRIPTOR = "Landroid/app/Fragment;";
+ private static final String ZYGOTE_PRELOAD_DESCRIPTOR = "Landroid/app/ZygotePreload;";
+
+ private static final String EXPECTED_OUTPUT =
+ StringUtils.lines(
+ "Fragment.onCreate()", "MyFragment.onCreate()", "MyZygotePreload.doPreload()");
+
+ private static List<byte[]> transformedProgramClassFileData;
+ private static List<byte[]> transformedLibraryClassFileData;
+
@Parameter(0)
+ public boolean enableModeling;
+
+ @Parameter(1)
public TestParameters parameters;
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ @Parameters(name = "{1}, modeling: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ transformedProgramClassFileData = getTransformedProgramClasses();
+ transformedLibraryClassFileData = getTransformedLibraryClasses();
}
@Test
- public void test() throws Exception {
+ public void testRuntime() throws Exception {
+ assumeFalse(enableModeling);
+ testForRuntime(parameters)
+ .addProgramClassFileData(transformedProgramClassFileData)
+ .addProgramClassFileData(transformedLibraryClassFileData)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
testForR8(parameters.getBackend())
- .addProgramClasses(Main.class, MyFragment.class)
- .addLibraryClasses(Fragment.class)
+ .addProgramClassFileData(transformedProgramClassFileData)
+ .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()
.setMinApi(parameters)
.compile()
.inspect(this::inspect)
- .addBootClasspathClasses(Fragment.class)
+ .addRunClasspathClassFileData(transformedLibraryClassFileData)
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("Instantiating");
+ .applyIf(
+ enableModeling || !parameters.canHaveNonReboundConstructorInvoke(),
+ runResult -> runResult.assertSuccessWithOutput(EXPECTED_OUTPUT),
+ runResult -> runResult.assertFailureWithErrorThatThrows(NoSuchMethodException.class));
+ }
+
+ private static List<byte[]> getTransformedProgramClasses() throws Exception {
+ return ImmutableList.of(
+ transformer(Main.class)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
+ .transform(),
+ transformer(MyFragment.class)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
+ .setSuper(FRAGMENT_DESCRIPTOR)
+ .transform(),
+ transformer(MyZygotePreload.class)
+ .replaceClassDescriptorInMembers(
+ descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
+ .setImplementsClassDescriptors(ZYGOTE_PRELOAD_DESCRIPTOR)
+ .transform());
+ }
+
+ private static List<byte[]> getTransformedLibraryClasses() throws Exception {
+ return ImmutableList.of(
+ transformer(ApplicationInfo.class)
+ .setClassDescriptor(APPLICATION_INFO_DESCRIPTOR)
+ .transform(),
+ transformer(Fragment.class).setClassDescriptor(FRAGMENT_DESCRIPTOR).transform(),
+ transformer(Platform.class)
+ .replaceClassDescriptorInMembers(descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(Fragment.class), FRAGMENT_DESCRIPTOR)
+ .replaceClassDescriptorInMembers(
+ descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(ZygotePreload.class), ZYGOTE_PRELOAD_DESCRIPTOR)
+ .transform(),
+ transformer(ZygotePreload.class)
+ .replaceClassDescriptorInMembers(
+ descriptor(ApplicationInfo.class), APPLICATION_INFO_DESCRIPTOR)
+ .setClassDescriptor(ZYGOTE_PRELOAD_DESCRIPTOR)
+ .transform());
}
private void inspect(CodeInspector inspector) {
ClassSubject myFragmentClassSubject = inspector.clazz(MyFragment.class);
assertThat(myFragmentClassSubject, isPresent());
- assertThat(myFragmentClassSubject.init(), isPresent());
+ assertThat(
+ myFragmentClassSubject.init(),
+ onlyIf(enableModeling || !parameters.canHaveNonReboundConstructorInvoke(), isPresent()));
+
+ ClassSubject myZygotePreloadClassSubject = inspector.clazz(MyZygotePreload.class);
+ assertThat(myZygotePreloadClassSubject, isPresent());
+ assertThat(
+ myZygotePreloadClassSubject.init(),
+ onlyIf(enableModeling || !parameters.canHaveNonReboundConstructorInvoke(), isPresent()));
}
+ // Library classes.
+
+ public abstract static class ApplicationInfo {}
+
public abstract static class Fragment {
- public Fragment newInstance() throws Exception {
- System.out.println("Instantiating");
- return getClass().getDeclaredConstructor().newInstance();
+ public void onCreate() {
+ System.out.println("Fragment.onCreate()");
}
}
+ public interface ZygotePreload {
+
+ void doPreload(ApplicationInfo applicationInfo);
+ }
+
+ public static class Platform {
+
+ public static void accept(Fragment fragment) throws Exception {
+ Fragment newFragment = fragment.getClass().getDeclaredConstructor().newInstance();
+ newFragment.onCreate();
+ }
+
+ public static void accept(ZygotePreload runnable) throws Exception {
+ ZygotePreload newZygotePreload = runnable.getClass().getDeclaredConstructor().newInstance();
+ newZygotePreload.doPreload(null);
+ }
+ }
+
+ // Program classes.
+
public static class Main {
public static void main(String[] args) throws Exception {
- new MyFragment().newInstance();
+ Platform.accept(new MyFragment());
+ Platform.accept(new MyZygotePreload());
}
}
@@ -71,5 +198,21 @@
public static class MyFragment extends Fragment {
public MyFragment() {}
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ System.out.println("MyFragment.onCreate()");
+ }
+ }
+
+ public static class MyZygotePreload implements ZygotePreload {
+
+ public MyZygotePreload() {}
+
+ @Override
+ public void doPreload(ApplicationInfo applicationInfo) {
+ System.out.println("MyZygotePreload.doPreload()");
+ }
}
}