Enable extra round of class merging
This also:
* enables class merging for all synthetics in the final round of class merging (cf. SyntheticItems.isEligibleForClassMerging()),
* moves the merging of interfaces to the final round of class merging (cf. InternalOptions.isInterfaceMergingEnabled()).
Bug: 181846319
Change-Id: I98c88ad4739192788d68627eba1ed5b555951919
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index d9df6e8..2067ad6 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -92,7 +92,7 @@
private boolean allCodeProcessed = false;
private Predicate<DexType> classesEscapingIntoLibrary = Predicates.alwaysTrue();
private InitializedClassesInInstanceMethods initializedClassesInInstanceMethods;
- private HorizontallyMergedClasses horizontallyMergedClasses;
+ private HorizontallyMergedClasses horizontallyMergedClasses = HorizontallyMergedClasses.empty();
private VerticallyMergedClasses verticallyMergedClasses;
private EnumDataMap unboxedEnums = EnumDataMap.empty();
// TODO(b/169115389): Remove
@@ -497,7 +497,7 @@
public MergedClassesCollection allMergedClasses() {
MergedClassesCollection collection = new MergedClassesCollection();
- if (horizontallyMergedClasses != null) {
+ if (hasHorizontallyMergedClasses()) {
collection.add(horizontallyMergedClasses);
}
if (verticallyMergedClasses != null) {
@@ -507,7 +507,7 @@
}
public boolean hasHorizontallyMergedClasses() {
- return horizontallyMergedClasses != null;
+ return !horizontallyMergedClasses.isEmpty();
}
/**
@@ -520,15 +520,12 @@
public void setHorizontallyMergedClasses(
HorizontallyMergedClasses horizontallyMergedClasses, HorizontalClassMerger.Mode mode) {
- if (mode.isInitial()) {
- assert !hasHorizontallyMergedClasses();
- this.horizontallyMergedClasses = horizontallyMergedClasses;
+ assert !hasHorizontallyMergedClasses() || mode.isFinal();
+ this.horizontallyMergedClasses = horizontallyMergedClasses().extend(horizontallyMergedClasses);
+ if (mode.isFinal()) {
testing()
.horizontallyMergedClassesConsumer
- .accept(dexItemFactory(), horizontallyMergedClasses);
- } else {
- // TODO(b/187496738): Enable final class merging.
- assert horizontallyMergedClasses.isEmpty();
+ .accept(dexItemFactory(), horizontallyMergedClasses());
}
}
@@ -650,7 +647,7 @@
public void rewriteWithLens(NonIdentityGraphLens lens) {
if (lens != null) {
- rewriteWithLens(lens, appInfo().app().asDirect(), withLiveness(), lens.getPrevious());
+ rewriteWithLens(lens, appInfo().app().asDirect(), withClassHierarchy(), lens.getPrevious());
}
}
@@ -663,13 +660,13 @@
NonIdentityGraphLens lens, DirectMappedDexApplication application, GraphLens appliedLens) {
assert lens != null;
assert application != null;
- rewriteWithLens(lens, application, withLiveness(), appliedLens);
+ rewriteWithLens(lens, application, withClassHierarchy(), appliedLens);
}
private static void rewriteWithLens(
NonIdentityGraphLens lens,
DirectMappedDexApplication application,
- AppView<AppInfoWithLiveness> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
GraphLens appliedLens) {
if (lens == null) {
return;
@@ -701,7 +698,11 @@
firstUnappliedLens.withAlternativeParentLens(
newMemberRebindingLens,
() -> {
- appView.setAppInfo(appView.appInfo().rewrittenWithLens(application, lens));
+ if (appView.hasLiveness()) {
+ appView
+ .withLiveness()
+ .setAppInfo(appView.appInfoWithLiveness().rewrittenWithLens(application, lens));
+ }
appView.setAppServices(appView.appServices().rewrittenWithLens(lens));
if (appView.hasInitClassLens()) {
appView.setInitClassLens(appView.initClassLens().rewrittenWithLens(lens));
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 88d0852..f87f10d 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -23,11 +23,13 @@
import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -579,10 +581,16 @@
return builder.build();
}
- public <T> ImmutableMap<DexType, T> rewriteTypeKeys(Map<DexType, T> map) {
- ImmutableMap.Builder<DexType, T> builder = ImmutableMap.builder();
- map.forEach((type, value) -> builder.put(lookupType(type), value));
- return builder.build();
+ public <T> Map<DexType, T> rewriteTypeKeys(Map<DexType, T> map, BiFunction<T, T, T> merge) {
+ Map<DexType, T> newMap = new IdentityHashMap<>();
+ map.forEach(
+ (type, value) -> {
+ DexType rewrittenType = lookupType(type);
+ T previousValue = newMap.get(rewrittenType);
+ newMap.put(
+ rewrittenType, previousValue != null ? merge.apply(value, previousValue) : value);
+ });
+ return Collections.unmodifiableMap(newMap);
}
public boolean verifyMappingToOriginalProgram(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index 5eea854..2b65950 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -10,7 +10,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinition;
@@ -117,8 +117,7 @@
}
DexMethod newClinit = dexItemFactory.createClassInitializer(group.getTarget().getType());
-
- CfCode code = classInitializerSynthesizedCode.synthesizeCode(group.getTarget().getType());
+ Code code = classInitializerSynthesizedCode.getOrCreateCode(group.getTarget().getType());
if (!group.getTarget().hasClassInitializer()) {
classMethodsBuilder.addDirectMethod(
new DexEncodedMethod(
@@ -134,9 +133,11 @@
} else {
DexEncodedMethod clinit = group.getTarget().getClassInitializer();
clinit.setCode(code, appView);
- CfVersion cfVersion = classInitializerSynthesizedCode.getCfVersion();
- if (cfVersion != null) {
- clinit.upgradeClassFileVersion(cfVersion);
+ if (code.isCfCode()) {
+ CfVersion cfVersion = classInitializerSynthesizedCode.getCfVersion();
+ if (cfVersion != null) {
+ clinit.upgradeClassFileVersion(cfVersion);
+ }
}
classMethodsBuilder.addDirectMethod(clinit);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index df5f1a6..1a88983 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
-import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
import com.android.tools.r8.utils.Timing;
import java.util.ArrayList;
@@ -156,12 +155,7 @@
private List<MergeGroup> getInitialGroups() {
MergeGroup initialClassGroup = new MergeGroup();
MergeGroup initialInterfaceGroup = new MergeGroup();
- HorizontalClassMergerOptions options = appView.options().horizontalClassMergerOptions();
- SyntheticItems syntheticItems = appView.getSyntheticItems();
for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
- if (options.isRestrictedToSynthetics() && !syntheticItems.isSyntheticClass(clazz)) {
- continue;
- }
if (clazz.isInterface()) {
initialInterfaceGroup.add(clazz);
} else {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
index 67d390b..9caf1a9 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
@@ -32,6 +32,24 @@
return new HorizontallyMergedClasses(new EmptyBidirectionalOneToOneMap<>());
}
+ public HorizontallyMergedClasses extend(HorizontallyMergedClasses newHorizontallyMergedClasses) {
+ if (isEmpty()) {
+ return newHorizontallyMergedClasses;
+ }
+ if (newHorizontallyMergedClasses.isEmpty()) {
+ return this;
+ }
+ Builder builder = builder();
+ forEachMergeGroup(
+ (sources, target) -> {
+ DexType rewrittenTarget = newHorizontallyMergedClasses.getMergeTargetOrDefault(target);
+ sources.forEach(source -> builder.add(source, rewrittenTarget));
+ });
+ newHorizontallyMergedClasses.forEachMergeGroup(
+ (sources, target) -> sources.forEach(source -> builder.add(source, target)));
+ return builder.build();
+ }
+
@Override
public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) {
mergedClasses.forEachManyToOneMapping(consumer);
@@ -91,8 +109,13 @@
private final MutableBidirectionalManyToOneMap<DexType, DexType> mergedClasses =
BidirectionalManyToOneHashMap.newIdentityHashMap();
+ void add(DexType source, DexType target) {
+ assert !mergedClasses.containsKey(source);
+ mergedClasses.put(source, target);
+ }
+
void addMergeGroup(MergeGroup group) {
- group.forEachSource(clazz -> mergedClasses.put(clazz.getType(), group.getTarget().getType()));
+ group.forEachSource(clazz -> add(clazz.getType(), group.getTarget().getType()));
}
Builder addMergeGroups(Iterable<MergeGroup> groups) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
index 57302300..1a5e5a7 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.horizontalclassmerging.policies.AllInstantiatedOrUninstantiated;
import com.android.tools.r8.horizontalclassmerging.policies.AtMostOneClassInitializer;
import com.android.tools.r8.horizontalclassmerging.policies.CheckAbstractClasses;
+import com.android.tools.r8.horizontalclassmerging.policies.CheckSyntheticClasses;
import com.android.tools.r8.horizontalclassmerging.policies.LimitGroups;
import com.android.tools.r8.horizontalclassmerging.policies.MinimizeInstanceFieldCasts;
import com.android.tools.r8.horizontalclassmerging.policies.NoAnnotationClasses;
@@ -99,7 +100,7 @@
private static void addRequiredSingleClassPolicies(
AppView<? extends AppInfoWithClassHierarchy> appView,
ImmutableList.Builder<SingleClassPolicy> builder) {
- builder.add(new NoKeepRules(appView));
+ builder.add(new CheckSyntheticClasses(appView), new NoKeepRules(appView));
}
private static void addSingleClassPoliciesForMergingNonSyntheticClasses(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerSynthesizedCode.java
index 99d8f48..1b8a318 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerSynthesizedCode.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.cf.code.CfLabel;
import com.android.tools.r8.cf.code.CfReturnVoid;
import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.CfVersionUtils;
@@ -58,7 +59,13 @@
}
}
- public CfCode synthesizeCode(DexType originalHolder) {
+ public Code getOrCreateCode(DexType originalHolder) {
+ assert !staticClassInitializers.isEmpty();
+
+ if (staticClassInitializers.size() == 1) {
+ return staticClassInitializers.get(0).getCode();
+ }
+
// Building the instructions will adjust maxStack and maxLocals. Build it here before invoking
// the CfCode constructor to ensure that the value passed in is the updated values.
List<CfInstruction> instructions = buildInstructions();
@@ -83,6 +90,11 @@
}
public CfVersion getCfVersion() {
+ if (staticClassInitializers.size() == 1) {
+ DexEncodedMethod method = staticClassInitializers.get(0);
+ return method.hasClassFileVersion() ? method.getClassFileVersion() : null;
+ }
+ assert staticClassInitializers.stream().allMatch(method -> method.getCode().isCfCode());
return CfVersionUtils.max(staticClassInitializers);
}
@@ -92,7 +104,6 @@
public void add(DexEncodedMethod method) {
assert method.isClassInitializer();
assert method.hasCode();
- assert method.getCode().isCfCode();
staticClassInitializers.add(method);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckSyntheticClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckSyntheticClasses.java
new file mode 100644
index 0000000..8c08712
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckSyntheticClasses.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2020, 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.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
+import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
+
+public class CheckSyntheticClasses extends SingleClassPolicy {
+
+ private final HorizontalClassMergerOptions options;
+ private final SyntheticItems syntheticItems;
+
+ public CheckSyntheticClasses(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ this.options = appView.options().horizontalClassMergerOptions();
+ this.syntheticItems = appView.getSyntheticItems();
+ }
+
+ @Override
+ public boolean canMerge(DexProgramClass clazz) {
+ if (!options.isSyntheticMergingEnabled() && syntheticItems.isSyntheticClass(clazz)) {
+ return false;
+ }
+ if (options.isRestrictedToSynthetics() && !syntheticItems.isSyntheticClass(clazz)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "CheckSyntheticClasses";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlySyntheticClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlySyntheticClasses.java
deleted file mode 100644
index fd1b2ab..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlySyntheticClasses.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2020, 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.horizontalclassmerging.policies;
-
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
-import com.android.tools.r8.synthesis.SyntheticItems;
-
-public class OnlySyntheticClasses extends SingleClassPolicy {
-
- private final SyntheticItems syntheticItems;
-
- public OnlySyntheticClasses(AppView<? extends AppInfoWithClassHierarchy> appView) {
- this.syntheticItems = appView.getSyntheticItems();
- }
-
- @Override
- public boolean canMerge(DexProgramClass clazz) {
- return syntheticItems.isSyntheticClass(clazz);
- }
-
- @Override
- public String getName() {
- return "OnlySyntheticClasses";
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 41a298a..a6f246f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -141,6 +141,11 @@
return !singleTarget.getDefinition().getOptimizationInfo().forceInline();
}
+ if (!appView.testing().allowInliningOfSynthetics
+ && appView.getSyntheticItems().isSyntheticClass(singleTarget.getHolder())) {
+ return true;
+ }
+
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index 014337c..989c188 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
-import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.value.ObjectState;
import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
@@ -328,11 +327,9 @@
private boolean verifyWasInstanceInitializer() {
VerticallyMergedClasses verticallyMergedClasses = appView.verticallyMergedClasses();
- HorizontallyMergedClasses horizontallyMergedClasses = appView.horizontallyMergedClasses();
assert verticallyMergedClasses != null;
- assert horizontallyMergedClasses != null;
assert verticallyMergedClasses.isMergeTarget(method.getHolderType())
- || horizontallyMergedClasses.isMergeTarget(method.getHolderType());
+ || appView.horizontallyMergedClasses().isMergeTarget(method.getHolderType());
assert appView
.dexItemFactory()
.isConstructor(appView.graphLens().getOriginalMethodSignature(method.getReference()));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index f7b79aa..c3c2307 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -300,6 +300,12 @@
if (clazz.classInitializationMayHaveSideEffects(appView)) {
return EligibilityStatus.NOT_ELIGIBLE;
}
+
+ if (!appView.testing().allowClassInliningOfSynthetics
+ && appView.getSyntheticItems().isSyntheticClass(clazz)) {
+ return EligibilityStatus.NOT_ELIGIBLE;
+ }
+
return EligibilityStatus.ELIGIBLE;
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 69c27c5..09fee09 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -31,6 +31,7 @@
import com.android.tools.r8.graph.FieldAccessInfoCollection;
import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl;
import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.InstantiatedSubTypeInfo;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
@@ -1094,7 +1095,26 @@
prunedTypes,
lens.rewriteFieldKeys(switchMaps),
lens.rewriteTypes(lockCandidates),
- lens.rewriteTypeKeys(initClassReferences));
+ rewriteInitClassReferences(lens));
+ }
+
+ public Map<DexType, Visibility> rewriteInitClassReferences(GraphLens lens) {
+ return lens.rewriteTypeKeys(
+ initClassReferences,
+ (minimumRequiredVisibilityForCurrentMethod,
+ otherMinimumRequiredVisibilityForCurrentMethod) -> {
+ assert !minimumRequiredVisibilityForCurrentMethod.isPrivate();
+ assert !otherMinimumRequiredVisibilityForCurrentMethod.isPrivate();
+ if (minimumRequiredVisibilityForCurrentMethod.isPublic()
+ || otherMinimumRequiredVisibilityForCurrentMethod.isPublic()) {
+ return Visibility.PUBLIC;
+ }
+ if (minimumRequiredVisibilityForCurrentMethod.isProtected()
+ || otherMinimumRequiredVisibilityForCurrentMethod.isProtected()) {
+ return Visibility.PROTECTED;
+ }
+ return Visibility.PACKAGE_PRIVATE;
+ });
}
/**
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 17b5a8f..a1f9b48 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -232,12 +232,10 @@
}
public boolean isEligibleForClassMerging(DexProgramClass clazz, HorizontalClassMerger.Mode mode) {
- // TODO(b/187496738): Allow merging all synthetics in the final round of class merging.
assert isSyntheticClass(clazz);
- return isSyntheticLambda(clazz);
+ return mode.isFinal() || isSyntheticLambda(clazz);
}
- // TODO(b/186211926): Allow merging of legacy synthetics.
private boolean isSyntheticLambda(DexProgramClass clazz) {
if (!isNonLegacySynthetic(clazz)) {
return false;
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 f9580b6..20d0405 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1212,6 +1212,7 @@
!Version.isDevelopmentVersion()
|| System.getProperty("com.android.tools.r8.disableHorizontalClassMerging") == null;
private boolean enableInterfaceMerging = false;
+ private boolean enableSyntheticMerging = true;
private boolean ignoreRuntimeTypeChecksForTesting = false;
private boolean restrictToSynthetics = false;
@@ -1221,6 +1222,10 @@
enable = false;
}
+ public void disableSyntheticMerging() {
+ enableSyntheticMerging = false;
+ }
+
public void enable() {
enable = true;
}
@@ -1249,8 +1254,7 @@
return enableInlining && isShrinking();
}
assert mode.isFinal();
- // TODO(b/187496738): Enable final class merging.
- return false;
+ return true;
}
public boolean isIgnoreRuntimeTypeChecksForTestingEnabled() {
@@ -1258,13 +1262,16 @@
}
public boolean isInterfaceMergingEnabled() {
- return isInterfaceMergingEnabled(HorizontalClassMerger.Mode.INITIAL)
- || isInterfaceMergingEnabled(HorizontalClassMerger.Mode.FINAL);
+ assert !isInterfaceMergingEnabled(HorizontalClassMerger.Mode.INITIAL);
+ return isInterfaceMergingEnabled(HorizontalClassMerger.Mode.FINAL);
+ }
+
+ public boolean isSyntheticMergingEnabled() {
+ return enableSyntheticMerging;
}
public boolean isInterfaceMergingEnabled(HorizontalClassMerger.Mode mode) {
- // TODO(b/187496738): Only run interface merging during final class merging.
- return enableInterfaceMerging;
+ return enableInterfaceMerging && mode.isFinal();
}
public boolean isRestrictedToSynthetics() {
@@ -1367,7 +1374,9 @@
public boolean addCallEdgesForLibraryInvokes = false;
public boolean allowCheckDiscardedErrors = false;
+ public boolean allowClassInliningOfSynthetics = true;
public boolean allowInjectedAnnotationMethods = false;
+ public boolean allowInliningOfSynthetics = true;
public boolean allowTypeErrors =
!Version.isDevelopmentVersion()
|| System.getProperty("com.android.tools.r8.allowTypeErrors") != null;
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 97cf033..d78af9d 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -444,6 +444,11 @@
return self();
}
+ public T noClassInliningOfSynthetics() {
+ return addOptionsModification(
+ options -> options.testing.allowClassInliningOfSynthetics = false);
+ }
+
public T noClassStaticizing() {
return noClassStaticizing(true);
}
@@ -467,8 +472,21 @@
}
public T noHorizontalClassMerging(Class<?> clazz) {
- return addKeepRules(
- "-" + NoHorizontalClassMergingRule.RULE_NAME + " class " + clazz.getTypeName());
+ return noHorizontalClassMerging(clazz.getTypeName());
+ }
+
+ public T noHorizontalClassMerging(String typeName) {
+ return addKeepRules("-" + NoHorizontalClassMergingRule.RULE_NAME + " class " + typeName)
+ .enableProguardTestOptions();
+ }
+
+ public T noHorizontalClassMergingOfSynthetics() {
+ return addOptionsModification(
+ options -> options.horizontalClassMergerOptions().disableSyntheticMerging());
+ }
+
+ public T noInliningOfSynthetics() {
+ return addOptionsModification(options -> options.testing.allowInliningOfSynthetics = false);
}
public T enableNoUnusedInterfaceRemovalAnnotations() {
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 121e7c4..40bbfd4 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.debug.DebugTestConfig;
-import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer;
import com.android.tools.r8.testing.AndroidBuildVersion;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
@@ -438,7 +437,7 @@
}
public T enableCoreLibraryDesugaring(
- AndroidApiLevel minApiLevel, KeepRuleConsumer keepRuleConsumer) {
+ AndroidApiLevel minApiLevel, StringConsumer keepRuleConsumer) {
return enableCoreLibraryDesugaring(
minApiLevel,
keepRuleConsumer,
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
index 5484fd8..817c786 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
@@ -30,6 +30,8 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector -> inspector.assertIsCompleteMergeGroup(A.class, B.class))
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
@@ -37,27 +39,25 @@
.assertSuccessWithOutputLines("42", "13", "21", "39", "print a", "print b")
.inspect(
codeInspector -> {
- ClassSubject aClassSubject = codeInspector.clazz(A.class);
- assertThat(aClassSubject, isPresent());
- FieldSubject classIdFieldSubject =
- aClassSubject.uniqueFieldWithName(ClassMerger.CLASS_ID_FIELD_NAME);
- assertThat(classIdFieldSubject, isPresent());
+ ClassSubject aClassSubject = codeInspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ FieldSubject classIdFieldSubject =
+ aClassSubject.uniqueFieldWithName(ClassMerger.CLASS_ID_FIELD_NAME);
+ assertThat(classIdFieldSubject, isPresent());
- MethodSubject firstInitSubject = aClassSubject.init("int");
- assertThat(firstInitSubject, isPresent());
- assertThat(
- firstInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
+ MethodSubject firstInitSubject = aClassSubject.init("int");
+ assertThat(firstInitSubject, isPresent());
+ assertThat(firstInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
- MethodSubject otherInitSubject = aClassSubject.init("long", "int");
- assertThat(otherInitSubject, isPresent());
- assertThat(
- otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
+ MethodSubject otherInitSubject = aClassSubject.init("long", "int");
+ assertThat(otherInitSubject, isPresent());
+ assertThat(otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
- MethodSubject printSubject = aClassSubject.method("void", "print$bridge");
- assertThat(printSubject, isPresent());
- assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
+ MethodSubject printSubject = aClassSubject.method("void", "print$bridge");
+ assertThat(printSubject, isPresent());
+ assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
- assertThat(codeInspector.clazz(B.class), not(isPresent()));
+ assertThat(codeInspector.clazz(B.class), not(isPresent()));
});
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java
index d74ae0a..580598b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.classmerging.horizontal.HorizontalClassMergingTestBase;
-import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import org.junit.Test;
public class OverrideDefaultMethodTest extends HorizontalClassMergingTestBase {
@@ -30,7 +30,17 @@
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.addHorizontallyMergedClassesInspector(
- HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ inspector -> {
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ inspector.assertNoClassesMerged();
+ } else {
+ inspector
+ .assertClassesNotMerged(A.class, B.class)
+ .assertIsCompleteMergeGroup(
+ SyntheticItemsTestUtils.syntheticCompanionClass(I.class),
+ SyntheticItemsTestUtils.syntheticCompanionClass(J.class));
+ }
+ })
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("I", "B", "J")
.inspect(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
index 95dc561..65ca772 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
@@ -13,7 +13,11 @@
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.classmerging.horizontal.HorizontalClassMergingTestBase;
-import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.android.tools.r8.classmerging.horizontal.dispatch.OverrideDefaultMethodTest.A;
+import com.android.tools.r8.classmerging.horizontal.dispatch.OverrideDefaultMethodTest.B;
+import com.android.tools.r8.classmerging.horizontal.dispatch.OverrideDefaultMethodTest.I;
+import com.android.tools.r8.classmerging.horizontal.dispatch.OverrideDefaultMethodTest.J;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import org.junit.Test;
public class OverrideDefaultOnSuperMethodTest extends HorizontalClassMergingTestBase {
@@ -32,7 +36,17 @@
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.addHorizontallyMergedClassesInspector(
- HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ inspector -> {
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ inspector.assertNoClassesMerged();
+ } else {
+ inspector
+ .assertClassesNotMerged(A.class, B.class)
+ .assertIsCompleteMergeGroup(
+ SyntheticItemsTestUtils.syntheticCompanionClass(I.class),
+ SyntheticItemsTestUtils.syntheticCompanionClass(J.class));
+ }
+ })
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("I", "B", "J")
.inspect(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java
index 097be15..1c5d915 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java
@@ -37,6 +37,7 @@
this.parameters = parameters;
}
+ // TODO(b/173990042): Disallow merging of A and B in the first round of class merging.
@Test
public void test() throws Exception {
testForR8(parameters.getBackend())
@@ -46,12 +47,20 @@
// contributes the default method K.m() to A, and the merging of J into I would contribute
// the default method J.m() to A.
.addHorizontallyMergedClassesInspector(
- inspector ->
+ inspector -> {
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ inspector
+ .assertIsCompleteMergeGroup(A.class, B.class)
+ .assertMergedInto(B.class, A.class)
+ .assertClassesNotMerged(I.class, J.class, K.class);
+ } else {
inspector
.assertIsCompleteMergeGroup(A.class, B.class)
.assertMergedInto(B.class, A.class)
.assertIsCompleteMergeGroup(I.class, J.class)
- .assertClassesNotMerged(K.class))
+ .assertClassesNotMerged(K.class);
+ }
+ })
.addOptionsModification(
options -> {
assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
@@ -73,15 +82,16 @@
ClassSubject bClassSubject = inspector.clazz(C.class);
assertThat(bClassSubject, isPresent());
- assertThat(bClassSubject, isImplementing(inspector.clazz(I.class)));
+ assertThat(
+ bClassSubject,
+ isImplementing(
+ inspector.clazz(
+ parameters.canUseDefaultAndStaticInterfaceMethods()
+ ? J.class
+ : I.class)));
})
.run(parameters.getRuntime(), Main.class)
- // TODO(b/173990042): Should succeed with "A", "K", "J".
- .applyIf(
- parameters.isCfRuntime() || parameters.canUseDefaultAndStaticInterfaceMethods(),
- builder -> builder.assertSuccessWithOutputLines("A", "J", "J"),
- builder ->
- builder.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class));
+ .assertSuccessWithOutputLines("A", "K", "J");
}
static class Main {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java
index e6ea690..d4f14bd 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java
@@ -18,7 +18,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -45,7 +44,13 @@
// I and J are not eligible for merging, since class A (implements I) inherits a default m()
// method from K, which is also on J.
.addHorizontallyMergedClassesInspector(
- HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ inspector -> {
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ inspector.assertNoClassesMerged();
+ } else {
+ inspector.assertIsCompleteMergeGroup(I.class, J.class);
+ }
+ })
.addOptionsModification(
options -> {
assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
@@ -67,7 +72,13 @@
ClassSubject bClassSubject = inspector.clazz(B.class);
assertThat(bClassSubject, isPresent());
- assertThat(bClassSubject, isImplementing(inspector.clazz(J.class)));
+ assertThat(
+ bClassSubject,
+ isImplementing(
+ inspector.clazz(
+ parameters.canUseDefaultAndStaticInterfaceMethods()
+ ? J.class
+ : I.class)));
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("K", "J");
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
index 979d57a..f9e85ba 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -78,7 +79,11 @@
// TODO(b/173990042): Should succeed with "K", "J".
.applyIf(
parameters.isCfRuntime(),
- builder -> builder.assertSuccessWithOutputLines("J", "J"),
+ builder ->
+ builder.assertFailureWithErrorThatThrows(
+ parameters.isCfRuntime(CfVm.JDK11)
+ ? AbstractMethodError.class
+ : IncompatibleClassChangeError.class),
builder -> builder.assertSuccessWithOutputLines("K", "J"));
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesMergingTest.java
index 384edae..7ca9cc5 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesMergingTest.java
@@ -43,6 +43,9 @@
})
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
+ .noClassInliningOfSynthetics()
+ .noHorizontalClassMergingOfSynthetics()
+ .noInliningOfSynthetics()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithIntersectionMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithIntersectionMergingTest.java
index 0aa2fba..dad75a4 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithIntersectionMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithIntersectionMergingTest.java
@@ -34,9 +34,8 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
- // TODO(b/173990042): We should be able to merge I and J.
.addHorizontallyMergedClassesInspector(
- inspector -> inspector.assertClassesNotMerged(I.class, J.class))
+ inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
.addOptionsModification(
options -> {
assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
@@ -44,6 +43,9 @@
})
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
+ .noClassInliningOfSynthetics()
+ .noHorizontalClassMergingOfSynthetics()
+ .noInliningOfSynthetics()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
@@ -53,7 +55,7 @@
static class Main {
public static void main(String[] args) {
- Object o = (I & J) () -> "I & J";
+ Object o = (I & J) () -> System.currentTimeMillis() > 0 ? "I & J" : null;
System.out.println(((I) o).f());
System.out.println(((J) o).f());
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentParametersMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentParametersMergingTest.java
index 97a279d..e58ca16 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentParametersMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentParametersMergingTest.java
@@ -45,6 +45,9 @@
})
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
+ .noClassInliningOfSynthetics()
+ .noHorizontalClassMergingOfSynthetics()
+ .noInliningOfSynthetics()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentReturnTypeMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentReturnTypeMergingTest.java
index 2b61fce..a877121 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentReturnTypeMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentReturnTypeMergingTest.java
@@ -45,6 +45,9 @@
})
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
+ .noClassInliningOfSynthetics()
+ .noHorizontalClassMergingOfSynthetics()
+ .noInliningOfSynthetics()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
@@ -54,8 +57,8 @@
static class Main {
public static void main(String[] args) {
- System.out.println(((I) () -> "I").f());
- System.out.println(((J) () -> "J").f());
+ System.out.println(((I) () -> System.currentTimeMillis() > 0 ? "I" : null).f());
+ System.out.println(((J) () -> System.currentTimeMillis() > 0 ? "J" : null).f());
}
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesMergingTest.java
index 9fe5cbb..2d264db 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesMergingTest.java
@@ -43,6 +43,9 @@
})
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
+ .noClassInliningOfSynthetics()
+ .noHorizontalClassMergingOfSynthetics()
+ .noInliningOfSynthetics()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesWithIntersectionMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesWithIntersectionMergingTest.java
index 4fa557e..23ed1ea 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesWithIntersectionMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesWithIntersectionMergingTest.java
@@ -34,9 +34,8 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
- // TODO(b/173990042): I and J should be merged.
.addHorizontallyMergedClassesInspector(
- inspector -> inspector.assertClassesNotMerged(I.class, J.class))
+ inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
.addOptionsModification(
options -> {
assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
@@ -44,6 +43,8 @@
})
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
+ .noClassInliningOfSynthetics()
+ .noInliningOfSynthetics()
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java
index 1773d26..6ad6ef1 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java
@@ -18,7 +18,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -44,7 +43,13 @@
.addKeepMainRule(Main.class)
// I and J are not eligible for merging, since they declare the same default method.
.addHorizontallyMergedClassesInspector(
- HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ inspector -> {
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ inspector.assertNoClassesMerged();
+ } else {
+ inspector.assertIsCompleteMergeGroup(I.class, J.class);
+ }
+ })
.addOptionsModification(
options -> {
assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
@@ -65,7 +70,13 @@
ClassSubject bClassSubject = inspector.clazz(B.class);
assertThat(bClassSubject, isPresent());
- assertThat(bClassSubject, isImplementing(inspector.clazz(J.class)));
+ assertThat(
+ bClassSubject,
+ isImplementing(
+ inspector.clazz(
+ parameters.canUseDefaultAndStaticInterfaceMethods()
+ ? J.class
+ : I.class)));
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("I", "J");
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerInterfaceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerInterfaceTest.java
index 53f5ae2..1c33206 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerInterfaceTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.classmerging.horizontalstatic;
import static com.android.tools.r8.synthesis.SyntheticItemsTestUtils.syntheticCompanionClass;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -12,9 +13,8 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -37,32 +37,41 @@
@Test
public void test() throws Exception {
String expectedOutput = StringUtils.lines("In I.a()", "In J.b()", "In A.c()");
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ // TODO(b/173990042): Extend horizontal class merging to interfaces.
+ .addHorizontallyMergedClassesInspector(
+ inspector -> {
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ inspector.assertNoClassesMerged();
+ } else {
+ inspector
+ .assertClassReferencesMerged(
+ SyntheticItemsTestUtils.syntheticCompanionClass(I.class),
+ SyntheticItemsTestUtils.syntheticCompanionClass(J.class))
+ .assertClassesNotMerged(I.class, J.class);
+ }
+ })
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expectedOutput)
+ .inspect(
+ inspector -> {
+ // We do not allow horizontal class merging of interfaces and classes. Therefore, A
+ // should remain in the output.
+ assertThat(inspector.clazz(A.class), isPresent());
- CodeInspector inspector =
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass())
- .addKeepMainRule(TestClass.class)
- // TODO(b/173990042): Extend horizontal class merging to interfaces.
- .addHorizontallyMergedClassesInspector(
- HorizontallyMergedClassesInspector::assertNoClassesMerged)
- .enableInliningAnnotations()
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(expectedOutput)
- .inspector();
-
- // We do not allow horizontal class merging of interfaces and classes. Therefore, A should
- // remain in the output.
- assertThat(inspector.clazz(A.class), isPresent());
-
- // TODO(b/173990042): I and J should be merged.
- if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
- assertThat(inspector.clazz(I.class), isPresent());
- assertThat(inspector.clazz(J.class), isPresent());
- } else {
- assertThat(inspector.clazz(syntheticCompanionClass(I.class)), isPresent());
- assertThat(inspector.clazz(syntheticCompanionClass(I.class)), isPresent());
- }
+ // TODO(b/173990042): I and J should be merged.
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ assertThat(inspector.clazz(I.class), isPresent());
+ assertThat(inspector.clazz(J.class), isPresent());
+ } else {
+ assertThat(inspector.clazz(syntheticCompanionClass(I.class)), isPresent());
+ assertThat(inspector.clazz(syntheticCompanionClass(J.class)), isAbsent());
+ }
+ });
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfaces.java b/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfaces.java
index 993b56f..2d396f7d 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfaces.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfaces.java
@@ -73,6 +73,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepAllClassesRule()
.addKeepAttributeInnerClassesAndEnclosingMethod()
+ .noHorizontalClassMergingOfSynthetics()
.compile()
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
@@ -125,7 +126,7 @@
static Callable<Class<?>> staticOuter() {
return new Callable<Class<?>>() {
@Override
- public Class<?> call() throws Exception {
+ public Class<?> call() {
return getClass().getEnclosingClass();
}
};
@@ -134,7 +135,7 @@
default Callable<Class<?>> defaultOuter() {
return new Callable<Class<?>>() {
@Override
- public Class<?> call() throws Exception {
+ public Class<?> call() {
return getClass().getEnclosingClass();
}
};
@@ -145,7 +146,7 @@
static Callable<Class<?>> staticOuter() {
class Local implements Callable<Class<?>> {
@Override
- public Class<?> call() throws Exception {
+ public Class<?> call() {
return getClass().getEnclosingClass();
}
}
@@ -155,7 +156,7 @@
default Callable<Class<?>> defaultOuter() {
class Local implements Callable<Class<?>> {
@Override
- public Class<?> call() throws Exception {
+ public Class<?> call() {
return getClass().getEnclosingClass();
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
index aac4a9e..1f15b61 100644
--- a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ZipUtils;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
@@ -102,9 +103,8 @@
// R8 will optimize the generated methods for the two cases below where the thrown
// exception is known or not, thus the synthetic methods will be 2.
int expectedSynthetics =
- parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport())
- ? 2
- : 0;
+ BooleanUtils.intValue(
+ parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport()));
List<FoundClassSubject> foundClassSubjects = inspector.allClasses();
assertEquals(INPUT_CLASSES + expectedSynthetics, foundClassSubjects.size());
});
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromStaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromStaticInterfaceMethodTest.java
index a86ee63..0531857 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromStaticInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromStaticInterfaceMethodTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import org.junit.Test;
@@ -49,8 +50,11 @@
options.outline.threshold = 2;
options.outline.minSize = 2;
})
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .noHorizontalClassMergingOfSynthetics()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b149971007/B149971007.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b149971007/B149971007.java
index dd6dace..0d1f637 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b149971007/B149971007.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b149971007/B149971007.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
@@ -25,7 +26,6 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Path;
-import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -66,18 +66,23 @@
return false;
}
- private void checkOutlineFromFeature(CodeInspector inspector) {
- // There are two expected outlines, each is currently in its own class.
- List<FoundMethodSubject> allMethods = new ArrayList<>();
- for (int i = 0; i < 2; i++) {
- ClassSubject clazz =
- inspector.clazz(SyntheticItemsTestUtils.syntheticOutlineClass(FeatureClass.class, i));
- assertThat(clazz, isPresent());
- assertEquals(1, clazz.allMethods().size());
- allMethods.addAll(clazz.allMethods());
- }
+ private ClassSubject checkOutlineFromFeature(CodeInspector inspector) {
+ // There are two expected outlines, in a single single class after horizontal class merging.
+ ClassSubject classSubject0 =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticOutlineClass(FeatureClass.class, 0));
+ ClassSubject classSubject1 =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticOutlineClass(FeatureClass.class, 1));
+ assertThat(classSubject1, notIf(isPresent(), classSubject0.isPresent()));
+
+ ClassSubject classSubject = classSubject0.isPresent() ? classSubject0 : classSubject1;
+
+ List<FoundMethodSubject> allMethods = classSubject.allMethods();
+ assertEquals(2, allMethods.size());
+
// One of the methods is StringBuilder the other references the feature.
assertTrue(allMethods.stream().anyMatch(this::referenceFeatureClass));
+
+ return classSubject;
}
@Test
@@ -89,33 +94,24 @@
.addKeepClassAndMembersRules(FeatureClass.class)
.setMinApi(parameters.getApiLevel())
.addOptionsModification(options -> options.outline.threshold = 2)
- .compile()
- .inspect(this::checkOutlineFromFeature);
+ .compile();
+
+ CodeInspector inspector = compileResult.inspector();
+ ClassSubject outlineClass = checkOutlineFromFeature(inspector);
// Check that parts of method1, ..., method4 in FeatureClass was outlined.
- ClassSubject featureClass = compileResult.inspector().clazz(FeatureClass.class);
+ ClassSubject featureClass = inspector.clazz(FeatureClass.class);
assertThat(featureClass, isPresent());
// Find the final names of the two outline classes.
- String outlineClassName0 =
- ClassNameMapper.mapperFromString(compileResult.getProguardMap())
- .getObfuscatedToOriginalMapping()
- .inverse
- .get(
- SyntheticItemsTestUtils.syntheticOutlineClass(FeatureClass.class, 0).getTypeName());
- String outlineClassName1 =
- ClassNameMapper.mapperFromString(compileResult.getProguardMap())
- .getObfuscatedToOriginalMapping()
- .inverse
- .get(
- SyntheticItemsTestUtils.syntheticOutlineClass(FeatureClass.class, 1).getTypeName());
+ String outlineClassName = outlineClass.getFinalName();
// Verify they are called from the feature methods.
// Note: should the choice of synthetic grouping change these expectations will too.
- assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method1"), outlineClassName0));
- assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method2"), outlineClassName0));
- assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method3"), outlineClassName1));
- assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method4"), outlineClassName1));
+ assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method1"), outlineClassName));
+ assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method2"), outlineClassName));
+ assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method3"), outlineClassName));
+ assertTrue(invokesOutline(featureClass.uniqueMethodWithName("method4"), outlineClassName));
compileResult.run(parameters.getRuntime(), TestClass.class).assertSuccessWithOutput("123456");
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
index d268888..5a60806 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.R8TestRunResult;
-import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -117,6 +116,7 @@
.addKeepAttributes("InnerClasses", "EnclosingMethod")
.addOptionsModification(this::configure)
.enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), main)
.assertSuccessWithOutput(EXPECTED);
@@ -124,10 +124,11 @@
@Test
public void testTrivial() throws Exception {
- SingleTestRunResult result =
+ R8TestRunResult result =
testForR8(parameters.getBackend())
.addProgramClasses(classes)
.enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.addKeepMainRule(main)
.noMinification()
.addKeepAttributes("InnerClasses", "EnclosingMethod")
@@ -241,7 +242,7 @@
HostOkFieldOnly.class,
CandidateOkFieldOnly.class
};
- SingleTestRunResult result =
+ R8TestRunResult result =
testForR8(parameters.getBackend())
.addProgramClasses(classes)
.enableInliningAnnotations()
@@ -283,6 +284,7 @@
.addProgramClasses(classes)
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.enableMemberValuePropagationAnnotations()
.addKeepMainRule(main)
.allowAccessModification()
@@ -405,7 +407,7 @@
Candidate.class
};
String javaOutput = runOnJava(main);
- SingleTestRunResult result =
+ R8TestRunResult result =
testForR8(parameters.getBackend())
.addProgramClasses(classes)
.enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InvokeStaticWithNullOutvalueTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InvokeStaticWithNullOutvalueTest.java
index 4e3f691..fe09d69 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InvokeStaticWithNullOutvalueTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/InvokeStaticWithNullOutvalueTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -45,6 +46,7 @@
.addKeepMainRule(MAIN)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("Companion#boo", "Companion#foo")
@@ -79,6 +81,7 @@
static class Host {
private static final Companion companion = new Companion();
+ @NoHorizontalClassMerging
static class Companion {
@NeverInline
private static Object boo() {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateConflictField.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateConflictField.java
index 295657a..8c0b01f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateConflictField.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/movetohost/CandidateConflictField.java
@@ -6,7 +6,9 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.NoHorizontalClassMerging;
+@NoHorizontalClassMerging
public class CandidateConflictField {
@NeverPropagateValue
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithGetter.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithGetter.java
index 6bc91b4..a743f11 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithGetter.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithGetter.java
@@ -5,7 +5,9 @@
package com.android.tools.r8.ir.optimize.staticizer.trivial;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+@NoHorizontalClassMerging
public class SimpleWithGetter {
private static SimpleWithGetter INSTANCE = new SimpleWithGetter();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithParams.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithParams.java
index 24edea4..b02dc73 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithParams.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithParams.java
@@ -5,7 +5,9 @@
package com.android.tools.r8.ir.optimize.staticizer.trivial;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+@NoHorizontalClassMerging
public class SimpleWithParams {
static SimpleWithParams INSTANCE = new SimpleWithParams(123);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithPhi.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithPhi.java
index f3c76cb..24786f4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithPhi.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/trivial/SimpleWithPhi.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.ir.optimize.staticizer.trivial;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+@NoHorizontalClassMerging
public class SimpleWithPhi {
public static class Companion {
@NeverInline
diff --git a/src/test/java/com/android/tools/r8/shaking/allowshrinking/ConditionalKeepClassMethodsAllowShrinkingCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/ConditionalKeepClassMethodsAllowShrinkingCompatibilityTest.java
index f7e50d9..abb4c9f 100644
--- a/src/test/java/com/android/tools/r8/shaking/allowshrinking/ConditionalKeepClassMethodsAllowShrinkingCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/ConditionalKeepClassMethodsAllowShrinkingCompatibilityTest.java
@@ -9,6 +9,7 @@
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
@@ -61,9 +62,12 @@
@Test
public void test() throws Exception {
if (shrinker.isR8()) {
- run(testForR8(parameters.getBackend()));
+ run(testForR8(parameters.getBackend()).enableNoHorizontalClassMergingAnnotations());
} else {
- run(testForProguard(shrinker.getProguardVersion()).addDontWarn(getClass()));
+ run(
+ testForProguard(shrinker.getProguardVersion())
+ .addDontWarn(getClass())
+ .addNoHorizontalClassMergingAnnotations());
}
}
@@ -113,6 +117,7 @@
}
}
+ @NoHorizontalClassMerging
static class B {
public String foo() {
return "B::foo";
diff --git a/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepAllowShrinkingCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepAllowShrinkingCompatibilityTest.java
index c9b3bc7..f198845 100644
--- a/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepAllowShrinkingCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepAllowShrinkingCompatibilityTest.java
@@ -9,6 +9,7 @@
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
@@ -64,9 +65,13 @@
run(
testForR8(parameters.getBackend())
// Allowing all of shrinking, optimization and obfuscation will amount to a nop rule.
- .allowUnusedProguardConfigurationRules(allowOptimization && allowObfuscation));
+ .allowUnusedProguardConfigurationRules(allowOptimization && allowObfuscation)
+ .enableNoHorizontalClassMergingAnnotations());
} else {
- run(testForProguard(shrinker.getProguardVersion()).addDontWarn(getClass()));
+ run(
+ testForProguard(shrinker.getProguardVersion())
+ .addDontWarn(getClass())
+ .addNoHorizontalClassMergingAnnotations());
}
}
@@ -107,6 +112,7 @@
}
}
+ @NoHorizontalClassMerging
static class B {
public String foo() {
return "B::foo";
diff --git a/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepClassMethodsAllowShrinkingCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepClassMethodsAllowShrinkingCompatibilityTest.java
index e7d5191..5a1d0f9 100644
--- a/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepClassMethodsAllowShrinkingCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/allowshrinking/KeepClassMethodsAllowShrinkingCompatibilityTest.java
@@ -9,6 +9,7 @@
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
@@ -64,9 +65,13 @@
run(
testForR8(parameters.getBackend())
// Allowing all of shrinking, optimization and obfuscation will amount to a nop rule.
- .allowUnusedProguardConfigurationRules(allowOptimization && allowObfuscation));
+ .allowUnusedProguardConfigurationRules(allowOptimization && allowObfuscation)
+ .enableNoHorizontalClassMergingAnnotations());
} else {
- run(testForProguard(shrinker.getProguardVersion()).addDontWarn(getClass()));
+ run(
+ testForProguard(shrinker.getProguardVersion())
+ .addDontWarn(getClass())
+ .addNoHorizontalClassMergingAnnotations());
}
}
@@ -116,6 +121,7 @@
}
}
+ @NoHorizontalClassMerging
static class B {
public String foo() {
return "B::foo";
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
index ae22c5e..0410bc0 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
@@ -106,7 +106,14 @@
}
public HorizontallyMergedClassesInspector assertNoClassesMerged() {
- assertTrue(horizontallyMergedClasses.getSources().isEmpty());
+ if (!horizontallyMergedClasses.getSources().isEmpty()) {
+ DexType source = horizontallyMergedClasses.getSources().iterator().next();
+ fail(
+ "Expected no classes to be merged, got: "
+ + source.getTypeName()
+ + " -> "
+ + getTarget(source).getTypeName());
+ }
return this;
}