Revert "Enable interface merging in second round of class merging"
This reverts commit 86a5ef0da87c5952e08c7e5e3afaa0f51a729dd0.
Reason for revert: Breaks compose sample app
Change-Id: I0fe46f1ba82c6d0c3f6cd1e33df5a1f109c7c501
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 4ef90be..bf21997 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
import com.android.tools.r8.ir.optimize.library.LibraryMemberOptimizer;
import com.android.tools.r8.ir.optimize.library.LibraryMethodSideEffectModelCollection;
+import com.android.tools.r8.optimize.MemberRebindingLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.shaking.LibraryModeledPredicate;
@@ -687,26 +688,13 @@
}
// Insert a member rebinding lens above the first unapplied lens.
- // TODO(b/182129249): Once the member rebinding phase has been removed, the MemberRebindingLens
- // should be removed and all uses of FieldRebindingIdentityLens should be replaced by
- // MemberRebindingIdentityLens.
- NonIdentityGraphLens appliedMemberRebindingLens =
- firstUnappliedLens.findPrevious(
- previous ->
- previous.isMemberRebindingLens() || previous.isMemberRebindingIdentityLens());
- GraphLens newMemberRebindingLens;
- if (appliedMemberRebindingLens != null) {
- newMemberRebindingLens =
- appliedMemberRebindingLens.isMemberRebindingLens()
- ? appliedMemberRebindingLens
- .asMemberRebindingLens()
- .toRewrittenFieldRebindingLens(appView, appliedLens)
- : appliedMemberRebindingLens
- .asMemberRebindingIdentityLens()
- .toRewrittenMemberRebindingIdentityLens(appView, appliedLens);
- } else {
- newMemberRebindingLens = GraphLens.getIdentityLens();
- }
+ MemberRebindingLens appliedMemberRebindingLens =
+ firstUnappliedLens.findPrevious(GraphLens::isMemberRebindingLens);
+ GraphLens newMemberRebindingLens =
+ appliedMemberRebindingLens != null
+ ? appliedMemberRebindingLens.toRewrittenFieldRebindingLens(
+ appView.dexItemFactory(), appliedLens)
+ : GraphLens.getIdentityLens();
firstUnappliedLens.withAlternativeParentLens(
newMemberRebindingLens,
diff --git a/src/main/java/com/android/tools/r8/graph/BottomUpClassHierarchyTraversal.java b/src/main/java/com/android/tools/r8/graph/BottomUpClassHierarchyTraversal.java
index 685188e..1c7bf88 100644
--- a/src/main/java/com/android/tools/r8/graph/BottomUpClassHierarchyTraversal.java
+++ b/src/main/java/com/android/tools/r8/graph/BottomUpClassHierarchyTraversal.java
@@ -4,19 +4,17 @@
package com.android.tools.r8.graph;
-import java.util.function.Function;
-
public class BottomUpClassHierarchyTraversal<T extends DexClass>
extends ClassHierarchyTraversal<T, BottomUpClassHierarchyTraversal<T>> {
- private final Function<DexType, Iterable<DexType>> immediateSubtypesProvider;
+ private final SubtypingInfo subtypingInfo;
private BottomUpClassHierarchyTraversal(
AppView<? extends AppInfoWithClassHierarchy> appView,
- Function<DexType, Iterable<DexType>> immediateSubtypesProvider,
+ SubtypingInfo subtypingInfo,
Scope scope) {
super(appView, scope);
- this.immediateSubtypesProvider = immediateSubtypesProvider;
+ this.subtypingInfo = subtypingInfo;
}
/**
@@ -25,8 +23,7 @@
*/
public static BottomUpClassHierarchyTraversal<DexClass> forAllClasses(
AppView<? extends AppInfoWithClassHierarchy> appView, SubtypingInfo subtypingInfo) {
- return new BottomUpClassHierarchyTraversal<>(
- appView, subtypingInfo::allImmediateSubtypes, Scope.ALL_CLASSES);
+ return new BottomUpClassHierarchyTraversal<>(appView, subtypingInfo, Scope.ALL_CLASSES);
}
/**
@@ -35,18 +32,8 @@
*/
public static BottomUpClassHierarchyTraversal<DexProgramClass> forProgramClasses(
AppView<? extends AppInfoWithClassHierarchy> appView, SubtypingInfo subtypingInfo) {
- return forProgramClasses(appView, subtypingInfo::allImmediateSubtypes);
- }
-
- /**
- * Returns a visitor that can be used to visit all the program classes that are reachable from a
- * given set of sources.
- */
- public static BottomUpClassHierarchyTraversal<DexProgramClass> forProgramClasses(
- AppView<? extends AppInfoWithClassHierarchy> appView,
- Function<DexType, Iterable<DexType>> immediateSubtypesProvider) {
return new BottomUpClassHierarchyTraversal<>(
- appView, immediateSubtypesProvider, Scope.ONLY_PROGRAM_CLASSES);
+ appView, subtypingInfo, Scope.ONLY_PROGRAM_CLASSES);
}
@Override
@@ -70,7 +57,7 @@
worklist.addFirst(clazzWithTypeT);
// Add subtypes to worklist.
- for (DexType subtype : immediateSubtypesProvider.apply(clazz.getType())) {
+ for (DexType subtype : subtypingInfo.allImmediateSubtypes(clazz.type)) {
DexClass definition = appView.definitionFor(subtype);
if (definition != null) {
if (scope != Scope.ONLY_PROGRAM_CLASSES || definition.isProgramClass()) {
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 8abd2ec..ff2fd57 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -9,8 +9,6 @@
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor.InterfaceProcessorNestedGraphLens;
-import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
-import com.android.tools.r8.optimize.MemberRebindingLens;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.IterableUtils;
@@ -459,18 +457,6 @@
return false;
}
- public MemberRebindingLens asMemberRebindingLens() {
- return null;
- }
-
- public boolean isMemberRebindingIdentityLens() {
- return false;
- }
-
- public MemberRebindingIdentityLens asMemberRebindingIdentityLens() {
- return null;
- }
-
public abstract boolean isNonIdentityLens();
public NonIdentityGraphLens asNonIdentityLens() {
@@ -685,8 +671,7 @@
}
@SuppressWarnings("unchecked")
- public final <T extends NonIdentityGraphLens> T findPrevious(
- Predicate<NonIdentityGraphLens> predicate) {
+ public final <T extends GraphLens> T findPrevious(Predicate<NonIdentityGraphLens> predicate) {
GraphLens current = getPrevious();
while (current.isNonIdentityLens()) {
NonIdentityGraphLens nonIdentityGraphLens = current.asNonIdentityLens();
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 4b02558..2b65950 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -270,14 +270,15 @@
}
private void mergeInterfaces() {
- Set<DexType> interfaces = Sets.newLinkedHashSet();
+ DexTypeList previousInterfaces = group.getTarget().getInterfaces();
+ Set<DexType> interfaces = Sets.newLinkedHashSet(previousInterfaces);
if (group.isInterfaceGroup()) {
// Add all implemented interfaces from the merge group to the target class, ignoring
// implemented interfaces that are part of the merge group.
Set<DexType> groupTypes =
SetUtils.newImmutableSet(
builder -> group.forEach(clazz -> builder.accept(clazz.getType())));
- group.forEach(
+ group.forEachSource(
clazz -> {
for (DexType itf : clazz.getInterfaces()) {
if (!groupTypes.contains(itf)) {
@@ -287,7 +288,7 @@
});
} else {
// Add all implemented interfaces from the merge group to the target class.
- group.forEach(clazz -> Iterables.addAll(interfaces, clazz.getInterfaces()));
+ group.forEachSource(clazz -> Iterables.addAll(interfaces, clazz.getInterfaces()));
}
group.getTarget().setInterfaces(DexTypeList.create(interfaces));
}
@@ -354,8 +355,7 @@
target = current;
}
}
- group.setTarget(
- appView.testing().horizontalClassMergingTarget.apply(appView, candidates, target));
+ group.setTarget(appView.testing().horizontalClassMergingTarget.apply(candidates, target));
}
private ClassInitializerSynthesizedCode createClassInitializerMerger() {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java
index ba5d775..b226b72 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java
@@ -140,10 +140,6 @@
return classes.isEmpty();
}
- public boolean isClassGroup() {
- return !isInterfaceGroup();
- }
-
public boolean isInterfaceGroup() {
assert !isEmpty();
assert IterableUtils.allIdentical(getClasses(), DexClass::isInterface);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
index a2362bd..b091242 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
@@ -22,13 +22,11 @@
import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicyWithPreprocessing;
import com.android.tools.r8.horizontalclassmerging.policies.NoDefaultInterfaceMethodCollisions.InterfaceInfo;
-import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.MapUtils;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.collections.DexMethodSignatureSet;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.Collections;
@@ -36,7 +34,6 @@
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
-import java.util.function.Function;
/**
* This policy prevents that interface merging changes semantics of invoke-interface/invoke-virtual
@@ -156,7 +153,7 @@
Map<DexType, Map<DexMethodSignature, Set<DexMethod>>>
defaultMethodsInheritedBySubclassesPerClass =
computeDefaultMethodsInheritedBySubclassesPerProgramClass(
- classesOfInterest, inheritedDefaultMethodsPerClass, groups, subtypingInfo);
+ classesOfInterest, inheritedDefaultMethodsPerClass, subtypingInfo);
// Store the computed information for each interface that is subject to merging.
Map<DexType, InterfaceInfo> infos = new IdentityHashMap<>();
@@ -273,16 +270,7 @@
computeDefaultMethodsInheritedBySubclassesPerProgramClass(
Collection<DexProgramClass> classesOfInterest,
Map<DexType, Map<DexMethodSignature, Set<DexMethod>>> inheritedDefaultMethodsPerClass,
- Collection<MergeGroup> groups,
SubtypingInfo subtypingInfo) {
- // Build a mapping from class types to their merge group.
- Map<DexType, Iterable<DexProgramClass>> classGroupsByType =
- MapUtils.newIdentityHashMap(
- builder ->
- Iterables.filter(groups, MergeGroup::isClassGroup)
- .forEach(
- group -> group.forEach(clazz -> builder.accept(clazz.getType(), group))));
-
// Copy the map from classes to their inherited default methods.
Map<DexType, Map<DexMethodSignature, Set<DexMethod>>>
defaultMethodsInheritedBySubclassesPerClass =
@@ -291,28 +279,7 @@
new HashMap<>(),
outerValue ->
MapUtils.clone(outerValue, new HashMap<>(), SetUtils::newIdentityHashSet));
-
- // Propagate data upwards. If classes A and B are in a merge group, we need to push the state
- // for A to all of B's supertypes, and the state for B to all of A's supertypes.
- //
- // Therefore, it is important that we don't process any of A's supertypes until B has been
- // processed, since that would lead to inadequate upwards propagation. To achieve this, we
- // simulate that both A and B are subtypes of A's and B's supertypes.
- Function<DexType, Iterable<DexType>> immediateSubtypesProvider =
- type -> {
- Set<DexType> immediateSubtypesAfterClassMerging = Sets.newIdentityHashSet();
- for (DexType immediateSubtype : subtypingInfo.allImmediateSubtypes(type)) {
- Iterable<DexProgramClass> group = classGroupsByType.get(immediateSubtype);
- if (group != null) {
- group.forEach(member -> immediateSubtypesAfterClassMerging.add(member.getType()));
- } else {
- immediateSubtypesAfterClassMerging.add(immediateSubtype);
- }
- }
- return immediateSubtypesAfterClassMerging;
- };
-
- BottomUpClassHierarchyTraversal.forProgramClasses(appView, immediateSubtypesProvider)
+ BottomUpClassHierarchyTraversal.forProgramClasses(appView, subtypingInfo)
.visit(
classesOfInterest,
clazz -> {
@@ -320,20 +287,16 @@
Map<DexMethodSignature, Set<DexMethod>> defaultMethodsToPropagate =
defaultMethodsInheritedBySubclassesPerClass.getOrDefault(
clazz.getType(), emptyMap());
- Iterable<DexProgramClass> group =
- classGroupsByType.getOrDefault(clazz.getType(), IterableUtils.singleton(clazz));
- for (DexProgramClass member : group) {
- for (DexType supertype : member.allImmediateSupertypes()) {
- Map<DexMethodSignature, Set<DexMethod>>
- defaultMethodsInheritedBySubclassesForSupertype =
- defaultMethodsInheritedBySubclassesPerClass.computeIfAbsent(
- supertype, ignore -> new HashMap<>());
- defaultMethodsToPropagate.forEach(
- (signature, methods) ->
- defaultMethodsInheritedBySubclassesForSupertype
- .computeIfAbsent(signature, ignore -> Sets.newIdentityHashSet())
- .addAll(methods));
- }
+ for (DexType supertype : clazz.allImmediateSupertypes()) {
+ Map<DexMethodSignature, Set<DexMethod>>
+ defaultMethodsInheritedBySubclassesForSupertype =
+ defaultMethodsInheritedBySubclassesPerClass.computeIfAbsent(
+ supertype, ignore -> new HashMap<>());
+ defaultMethodsToPropagate.forEach(
+ (signature, methods) ->
+ defaultMethodsInheritedBySubclassesForSupertype
+ .computeIfAbsent(signature, ignore -> Sets.newIdentityHashSet())
+ .addAll(methods));
}
});
defaultMethodsInheritedBySubclassesPerClass
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
index fc457f7..d9e6cd1 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
@@ -8,26 +8,25 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.MergeGroup;
-import com.android.tools.r8.horizontalclassmerging.MultiClassPolicyWithPreprocessing;
-import com.android.tools.r8.utils.SetUtils;
+import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import java.util.ArrayList;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Sets;
import java.util.Collection;
+import java.util.Collections;
import java.util.IdentityHashMap;
+import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.function.Function;
+import java.util.function.Consumer;
/**
* This policy ensures that we do not create cycles in the class hierarchy as a result of interface
@@ -54,15 +53,11 @@
* interface J extends IK, ... {}
* </pre>
*/
-public class OnlyDirectlyConnectedOrUnrelatedInterfaces
- extends MultiClassPolicyWithPreprocessing<SubtypingInfo> {
+public class OnlyDirectlyConnectedOrUnrelatedInterfaces extends MultiClassPolicy {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final Mode mode;
- // The interface merge groups that this policy has committed to so far.
- private final Map<DexProgramClass, MergeGroup> committed = new IdentityHashMap<>();
-
public OnlyDirectlyConnectedOrUnrelatedInterfaces(
AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
this.appView = appView;
@@ -70,84 +65,116 @@
}
@Override
- public Collection<MergeGroup> apply(MergeGroup group, SubtypingInfo subtypingInfo) {
+ public Collection<MergeGroup> apply(MergeGroup group) {
if (!group.isInterfaceGroup()) {
return ImmutableList.of(group);
}
- List<MergeGroupWithInfo> newGroupsWithInfo = new ArrayList<>();
- for (DexProgramClass clazz : group) {
- Set<DexProgramClass> superInterfaces = computeSuperInterfaces(clazz);
- Set<DexProgramClass> subInterfaces = computeSubInterfaces(clazz, subtypingInfo);
-
- MergeGroupWithInfo newGroup = null;
- for (MergeGroupWithInfo candidateGroup : newGroupsWithInfo) {
- // Check if adding `clazz` to `candidateGroup` would introduce a super interface that is
- // also a sub interface. In that case we must abort since merging would lead to a cycle in
- // the class hierarchy.
- if (candidateGroup.isSafeToAddSubAndSuperInterfaces(
- clazz, subInterfaces, superInterfaces)) {
- newGroup = candidateGroup;
- break;
- }
- }
-
- if (newGroup != null) {
- newGroup.add(clazz, superInterfaces, subInterfaces);
- } else {
- newGroupsWithInfo.add(new MergeGroupWithInfo(clazz, superInterfaces, subInterfaces));
- }
+ Set<DexProgramClass> classes = new LinkedHashSet<>(group.getClasses());
+ Map<DexProgramClass, Set<DexProgramClass>> ineligibleForMerging =
+ computeIneligibleForMergingGraph(classes);
+ if (ineligibleForMerging.isEmpty()) {
+ return ImmutableList.of(group);
}
+ // Extract sub-merge groups from the graph in such a way that all pairs of interfaces in each
+ // merge group are not connected by an edge in the graph.
List<MergeGroup> newGroups = new LinkedList<>();
- for (MergeGroupWithInfo newGroupWithInfo : newGroupsWithInfo) {
- MergeGroup newGroup = newGroupWithInfo.getGroup();
+ while (!classes.isEmpty()) {
+ Iterator<DexProgramClass> iterator = classes.iterator();
+ MergeGroup newGroup = new MergeGroup(iterator.next());
+ Iterators.addAll(
+ newGroup,
+ Iterators.filter(
+ iterator,
+ candidate -> !isConnectedToGroup(candidate, newGroup, ineligibleForMerging)));
if (!newGroup.isTrivial()) {
newGroups.add(newGroup);
- newGroup.forEach(clazz -> committed.put(clazz, newGroup));
}
+ classes.removeAll(newGroup.getClasses());
}
return newGroups;
}
- private Set<DexProgramClass> computeSuperInterfaces(DexProgramClass clazz) {
- return computeTransitiveSubOrSuperInterfaces(clazz, DexClass::getInterfaces);
+ /**
+ * Computes an undirected graph, where the nodes are the interfaces from the merge group, and an
+ * edge I <-> J represents that I and J are not eligible for merging.
+ *
+ * <p>We will insert an edge I <-> J, if interface I inherits from interface J, and the path from
+ * I to J in the class hierarchy includes an interface K that is outside the merge group. Note
+ * that if I extends J directly we will not insert an edge I <-> J (unless there are multiple
+ * paths in the class hierarchy from I to J, and one of the paths goes through an interface
+ * outside the merge group).
+ */
+ private Map<DexProgramClass, Set<DexProgramClass>> computeIneligibleForMergingGraph(
+ Set<DexProgramClass> classes) {
+ Map<DexProgramClass, Set<DexProgramClass>> ineligibleForMerging = new IdentityHashMap<>();
+ for (DexProgramClass clazz : classes) {
+ forEachIndirectlyReachableInterfaceInMergeGroup(
+ clazz,
+ classes,
+ other ->
+ ineligibleForMerging
+ .computeIfAbsent(clazz, ignore -> Sets.newIdentityHashSet())
+ .add(other));
+ }
+ return ineligibleForMerging;
}
- private Set<DexProgramClass> computeSubInterfaces(
- DexProgramClass clazz, SubtypingInfo subtypingInfo) {
- return computeTransitiveSubOrSuperInterfaces(
- clazz, definition -> subtypingInfo.allImmediateExtendsSubtypes(definition.getType()));
- }
-
- private Set<DexProgramClass> computeTransitiveSubOrSuperInterfaces(
- DexProgramClass clazz,
- Function<DexProgramClass, Iterable<DexType>> immediateSubOrSuperInterfacesProvider) {
- WorkList<DexProgramClass> workList = WorkList.newWorkList(new LinkedHashSet<>());
- // Intentionally not marking `clazz` as seen, since we only want the strict sub/super types.
- workList.addIgnoringSeenSet(clazz);
+ private void forEachIndirectlyReachableInterfaceInMergeGroup(
+ DexProgramClass clazz, Set<DexProgramClass> classes, Consumer<DexProgramClass> consumer) {
+ // First find the set of interfaces that can be reached via paths in the class hierarchy from
+ // the given interface, without visiting any interfaces outside the merge group.
+ WorkList<DexType> workList = WorkList.newIdentityWorkList(clazz.getInterfaces());
while (workList.hasNext()) {
- DexProgramClass interfaceDefinition = workList.next();
- MergeGroup group = committed.get(interfaceDefinition);
- if (group != null) {
- workList.addIfNotSeen(group);
+ DexProgramClass directlyReachableInterface =
+ asProgramClassOrNull(appView.definitionFor(workList.next()));
+ if (directlyReachableInterface == null) {
+ continue;
}
- for (DexType immediateSubOrSuperInterfaceType :
- immediateSubOrSuperInterfacesProvider.apply(interfaceDefinition)) {
- DexProgramClass immediateSubOrSuperInterface =
- asProgramClassOrNull(appView.definitionFor(immediateSubOrSuperInterfaceType));
- if (immediateSubOrSuperInterface != null) {
- workList.addIfNotSeen(immediateSubOrSuperInterface);
- }
+ // If the implemented interface is a member of the merge group, then include it's interfaces.
+ if (classes.contains(directlyReachableInterface)) {
+ workList.addIfNotSeen(directlyReachableInterface.getInterfaces());
}
}
- assert !workList.isSeen(clazz);
- return workList.getMutableSeenSet();
+
+ // Initialize a new worklist with the first layer of indirectly reachable interface types.
+ Set<DexType> directlyReachableInterfaceTypes = workList.getSeenSet();
+ workList = WorkList.newIdentityWorkList();
+ for (DexType directlyReachableInterfaceType : directlyReachableInterfaceTypes) {
+ DexProgramClass directlyReachableInterface =
+ asProgramClassOrNull(appView.definitionFor(directlyReachableInterfaceType));
+ if (directlyReachableInterface != null) {
+ workList.addIfNotSeen(directlyReachableInterface.getInterfaces());
+ }
+ }
+
+ // Report all interfaces from the merge group that are reachable in the class hierarchy from the
+ // worklist.
+ while (workList.hasNext()) {
+ DexProgramClass indirectlyReachableInterface =
+ asProgramClassOrNull(appView.definitionFor(workList.next()));
+ if (indirectlyReachableInterface == null) {
+ continue;
+ }
+ if (classes.contains(indirectlyReachableInterface)) {
+ consumer.accept(indirectlyReachableInterface);
+ }
+ workList.addIfNotSeen(indirectlyReachableInterface.getInterfaces());
+ }
}
- @Override
- public void clear() {
- committed.clear();
+ private boolean isConnectedToGroup(
+ DexProgramClass clazz,
+ MergeGroup group,
+ Map<DexProgramClass, Set<DexProgramClass>> ineligibleForMerging) {
+ for (DexProgramClass member : group) {
+ if (ineligibleForMerging.getOrDefault(clazz, Collections.emptySet()).contains(member)
+ || ineligibleForMerging.getOrDefault(member, Collections.emptySet()).contains(clazz)) {
+ return true;
+ }
+ }
+ return false;
}
@Override
@@ -156,80 +183,7 @@
}
@Override
- public SubtypingInfo preprocess(Collection<MergeGroup> groups) {
- return new SubtypingInfo(appView);
- }
-
- @Override
public boolean shouldSkipPolicy() {
return !appView.options().horizontalClassMergerOptions().isInterfaceMergingEnabled(mode);
}
-
- static class MergeGroupWithInfo {
-
- private final MergeGroup group;
- private final Set<DexProgramClass> members;
- private final Set<DexProgramClass> superInterfaces;
- private final Set<DexProgramClass> subInterfaces;
-
- MergeGroupWithInfo(
- DexProgramClass clazz,
- Set<DexProgramClass> superInterfaces,
- Set<DexProgramClass> subInterfaces) {
- this.group = new MergeGroup(clazz);
- this.members = SetUtils.newIdentityHashSet(clazz);
- this.superInterfaces = superInterfaces;
- this.subInterfaces = subInterfaces;
- }
-
- void add(
- DexProgramClass clazz,
- Set<DexProgramClass> newSuperInterfaces,
- Set<DexProgramClass> newSubInterfaces) {
- group.add(clazz);
- members.add(clazz);
- Iterables.addAll(
- superInterfaces,
- Iterables.filter(
- newSuperInterfaces, superInterface -> !members.contains(superInterface)));
- superInterfaces.remove(clazz);
- Iterables.addAll(
- subInterfaces,
- Iterables.filter(newSubInterfaces, subInterface -> !members.contains(subInterface)));
- subInterfaces.remove(clazz);
- }
-
- MergeGroup getGroup() {
- return group;
- }
-
- boolean isSafeToAddSubAndSuperInterfaces(
- DexProgramClass clazz,
- Set<DexProgramClass> newSubInterfaces,
- Set<DexProgramClass> newSuperInterfaces) {
- // Check that adding the new sub and super interfaces to the group is safe.
- for (DexProgramClass newSubInterface : newSubInterfaces) {
- if (!group.contains(newSubInterface) && superInterfaces.contains(newSubInterface)) {
- return false;
- }
- }
- for (DexProgramClass newSuperInterface : newSuperInterfaces) {
- if (!group.contains(newSuperInterface) && subInterfaces.contains(newSuperInterface)) {
- return false;
- }
- }
- // Check that adding the sub and super interfaces of the group to the current class is safe.
- for (DexProgramClass subInterface : subInterfaces) {
- if (subInterface != clazz && newSuperInterfaces.contains(subInterface)) {
- return false;
- }
- }
- for (DexProgramClass superInterface : superInterfaces) {
- if (superInterface != clazz && newSubInterfaces.contains(superInterface)) {
- return false;
- }
- }
- return true;
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
index 0fdc499..3f77dbb 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -41,12 +41,7 @@
}
public static Builder builder(AppView<? extends AppInfoWithClassHierarchy> appView) {
- return builder(appView, appView.graphLens());
- }
-
- public static Builder builder(
- AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens previousLens) {
- return new Builder(appView, previousLens);
+ return new Builder(appView);
}
@Override
@@ -134,55 +129,16 @@
return getPrevious().isContextFreeForMethods();
}
- @Override
- public boolean isMemberRebindingIdentityLens() {
- return true;
- }
-
- @Override
- public MemberRebindingIdentityLens asMemberRebindingIdentityLens() {
- return this;
- }
-
- public MemberRebindingIdentityLens toRewrittenMemberRebindingIdentityLens(
- AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens lens) {
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- Builder builder = builder(appView, getIdentityLens());
- nonReboundFieldReferenceToDefinitionMap.forEach(
- (nonReboundFieldReference, reboundFieldReference) -> {
- DexField rewrittenReboundFieldReference = lens.lookupField(reboundFieldReference);
- DexField rewrittenNonReboundFieldReference =
- rewrittenReboundFieldReference.withHolder(
- lens.lookupType(nonReboundFieldReference.getHolderType()), dexItemFactory);
- builder.recordNonReboundFieldAccess(
- rewrittenNonReboundFieldReference, rewrittenReboundFieldReference);
- });
- nonReboundMethodReferenceToDefinitionMap.forEach(
- (nonReboundMethodReference, reboundMethodReference) -> {
- DexMethod rewrittenReboundMethodReference =
- lens.getRenamedMethodSignature(reboundMethodReference);
- DexMethod rewrittenNonReboundMethodReference =
- rewrittenReboundMethodReference.withHolder(
- lens.lookupType(nonReboundMethodReference.getHolderType()), dexItemFactory);
- builder.recordNonReboundMethodAccess(
- rewrittenNonReboundMethodReference, rewrittenReboundMethodReference);
- });
- return builder.build();
- }
-
public static class Builder {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
- private final GraphLens previousLens;
-
private final Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap =
new IdentityHashMap<>();
private final Map<DexMethod, DexMethod> nonReboundMethodReferenceToDefinitionMap =
new IdentityHashMap<>();
- private Builder(AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens previousLens) {
+ private Builder(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
- this.previousLens = previousLens;
}
void recordNonReboundFieldAccesses(FieldAccessInfo fieldAccessInfo) {
@@ -196,12 +152,6 @@
nonReboundFieldReferenceToDefinitionMap.put(nonReboundFieldReference, reboundFieldReference);
}
- private void recordNonReboundMethodAccess(
- DexMethod nonReboundMethodReference, DexMethod reboundMethodReference) {
- nonReboundMethodReferenceToDefinitionMap.put(
- nonReboundMethodReference, reboundMethodReference);
- }
-
void recordMethodAccess(DexMethod reference) {
if (reference.getHolderType().isArrayType()) {
return;
@@ -211,7 +161,7 @@
SingleResolutionResult resolutionResult =
appView.appInfo().resolveMethodOn(holder, reference).asSingleResolution();
if (resolutionResult != null && resolutionResult.getResolvedHolder() != holder) {
- recordNonReboundMethodAccess(
+ nonReboundMethodReferenceToDefinitionMap.put(
reference, resolutionResult.getResolvedMethod().getReference());
}
}
@@ -225,7 +175,7 @@
nonReboundFieldReferenceToDefinitionMap,
nonReboundMethodReferenceToDefinitionMap,
appView.dexItemFactory(),
- previousLens);
+ appView.graphLens());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
index d202af8..4310236 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.graph.NestedGraphLens.mapVirtualInterfaceInvocationTypes;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
@@ -48,11 +47,6 @@
}
@Override
- public MemberRebindingLens asMemberRebindingLens() {
- return this;
- }
-
- @Override
public DexType getOriginalType(DexType type) {
return getPrevious().getOriginalType(type);
}
@@ -137,8 +131,7 @@
}
public FieldRebindingIdentityLens toRewrittenFieldRebindingLens(
- AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens lens) {
- DexItemFactory dexItemFactory = appView.dexItemFactory();
+ DexItemFactory dexItemFactory, GraphLens lens) {
FieldRebindingIdentityLens.Builder builder = FieldRebindingIdentityLens.builder();
nonReboundFieldReferenceToDefinitionMap.forEach(
(nonReboundFieldReference, reboundFieldReference) -> {
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 836c9c4..849d641 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -85,6 +85,7 @@
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -1214,7 +1215,7 @@
private boolean enable =
!Version.isDevelopmentVersion()
|| System.getProperty("com.android.tools.r8.disableHorizontalClassMerging") == null;
- private boolean enableInterfaceMergingInInitial = false;
+ private boolean enableInterfaceMerging = false;
private boolean enableSyntheticMerging = true;
private boolean ignoreRuntimeTypeChecksForTesting = false;
private boolean restrictToSynthetics = false;
@@ -1237,6 +1238,10 @@
this.enable = enable;
}
+ public void enableInterfaceMerging() {
+ enableInterfaceMerging = true;
+ }
+
public int getMaxGroupSize() {
return maxGroupSize;
}
@@ -1260,26 +1265,23 @@
return ignoreRuntimeTypeChecksForTesting;
}
+ public boolean isInterfaceMergingEnabled() {
+ assert !isInterfaceMergingEnabled(HorizontalClassMerger.Mode.INITIAL);
+ return isInterfaceMergingEnabled(HorizontalClassMerger.Mode.FINAL);
+ }
+
public boolean isSyntheticMergingEnabled() {
return enableSyntheticMerging;
}
public boolean isInterfaceMergingEnabled(HorizontalClassMerger.Mode mode) {
- if (mode.isInitial()) {
- return enableInterfaceMergingInInitial;
- }
- assert mode.isFinal();
- return true;
+ return enableInterfaceMerging && mode.isFinal();
}
public boolean isRestrictedToSynthetics() {
return restrictToSynthetics || !isOptimizing() || !isShrinking();
}
- public void setEnableInterfaceMergingInInitial() {
- enableInterfaceMergingInInitial = true;
- }
-
public void setIgnoreRuntimeTypeChecksForTesting() {
ignoreRuntimeTypeChecksForTesting = true;
}
@@ -1349,8 +1351,8 @@
public BiConsumer<DexItemFactory, HorizontallyMergedClasses> horizontallyMergedClassesConsumer =
ConsumerUtils.emptyBiConsumer();
- public TriFunction<AppView<?>, Iterable<DexProgramClass>, DexProgramClass, DexProgramClass>
- horizontalClassMergingTarget = (appView, candidates, target) -> target;
+ public BiFunction<Iterable<DexProgramClass>, DexProgramClass, DexProgramClass>
+ horizontalClassMergingTarget = (candidates, target) -> target;
public BiConsumer<DexItemFactory, EnumDataMap> unboxedEnumsConsumer =
ConsumerUtils.emptyBiConsumer();
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index fc39146..63e881e 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.utils.StringUtils.BraceType;
-import java.util.IdentityHashMap;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -51,12 +50,6 @@
return result;
}
- public static <K, V> IdentityHashMap<K, V> newIdentityHashMap(BiForEachable<K, V> forEachable) {
- IdentityHashMap<K, V> map = new IdentityHashMap<>();
- forEachable.forEach(map::put);
- return map;
- }
-
public static <T> void removeIdentityMappings(Map<T, T> map) {
map.entrySet().removeIf(entry -> entry.getKey() == entry.getValue());
}
diff --git a/src/main/java/com/android/tools/r8/utils/WorkList.java b/src/main/java/com/android/tools/r8/utils/WorkList.java
index 67e4148..4279cb7 100644
--- a/src/main/java/com/android/tools/r8/utils/WorkList.java
+++ b/src/main/java/com/android/tools/r8/utils/WorkList.java
@@ -48,10 +48,6 @@
return workList;
}
- public static <T> WorkList<T> newWorkList(Set<T> seen) {
- return new WorkList<>(seen);
- }
-
private WorkList(EqualityTest equalityTest) {
this(equalityTest == EqualityTest.HASH ? new HashSet<>() : Sets.newIdentityHashSet());
}
@@ -60,10 +56,6 @@
this.seen = seen;
}
- public void addIgnoringSeenSet(T item) {
- workingList.addLast(item);
- }
-
public void addAllIgnoringSeenSet(Iterable<T> items) {
items.forEach(workingList::addLast);
}
@@ -94,10 +86,6 @@
return !hasNext();
}
- public boolean isSeen(T item) {
- return seen.contains(item);
- }
-
public void markAsSeen(T item) {
seen.add(item);
}
@@ -115,10 +103,6 @@
return Collections.unmodifiableSet(seen);
}
- public Set<T> getMutableSeenSet() {
- return seen;
- }
-
public enum EqualityTest {
HASH,
IDENTITY
diff --git a/src/test/examples/classmerging/NoHorizontalClassMerging.java b/src/test/examples/classmerging/NoHorizontalClassMerging.java
deleted file mode 100644
index 3b7a831..0000000
--- a/src/test/examples/classmerging/NoHorizontalClassMerging.java
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright (c) 2021, 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 classmerging;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Target;
-
-@Target({ElementType.TYPE})
-public @interface NoHorizontalClassMerging {}
diff --git a/src/test/examples/classmerging/SimpleInterfaceAccessTest.java b/src/test/examples/classmerging/SimpleInterfaceAccessTest.java
index 804b4ed..6fde90e 100644
--- a/src/test/examples/classmerging/SimpleInterfaceAccessTest.java
+++ b/src/test/examples/classmerging/SimpleInterfaceAccessTest.java
@@ -32,14 +32,12 @@
}
// Should only be merged into OtherSimpleInterfaceImpl if access modifications are allowed.
- @NoHorizontalClassMerging
public interface SimpleInterface {
void foo();
}
// Should only be merged into OtherSimpleInterfaceImpl if access modifications are allowed.
- @NoHorizontalClassMerging
public interface OtherSimpleInterface {
void bar();
diff --git a/src/test/examples/classmerging/keep-rules.txt b/src/test/examples/classmerging/keep-rules.txt
index 46fcb91..64ce874 100644
--- a/src/test/examples/classmerging/keep-rules.txt
+++ b/src/test/examples/classmerging/keep-rules.txt
@@ -72,4 +72,3 @@
-neverinline class * {
@classmerging.NeverInline <methods>;
}
--nohorizontalclassmerging @classmerging.NoHorizontalClassMerging class *
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InnerOuterClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InnerOuterClassesTest.java
index 6fad862..8a80d6e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InnerOuterClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InnerOuterClassesTest.java
@@ -41,7 +41,7 @@
.addOptionsModification(
options ->
options.testing.horizontalClassMergingTarget =
- (appView, candidates, target) -> candidates.iterator().next())
+ (candidates, target) -> candidates.iterator().next())
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("a", "b", "c", "d")
.inspect(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassMergingTestRunner.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassMergingTestRunner.java
index 0844722..bf50ab1 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassMergingTestRunner.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassMergingTestRunner.java
@@ -111,7 +111,7 @@
.addOptionsModification(
options -> {
options.testing.horizontalClassMergingTarget =
- (appView, canditates, target) -> {
+ (canditates, target) -> {
Set<ClassReference> candidateClassReferences =
Streams.stream(canditates)
.map(DexClass::getClassReference)
@@ -164,7 +164,7 @@
.addOptionsModification(
options -> {
options.testing.horizontalClassMergingTarget =
- (appView, canditates, target) -> {
+ (canditates, target) -> {
Set<ClassReference> candidateClassReferences =
Streams.stream(canditates)
.map(DexClass::getClassReference)
@@ -217,7 +217,7 @@
.addOptionsModification(
options -> {
options.testing.horizontalClassMergingTarget =
- (appView, canditates, target) -> {
+ (canditates, target) -> {
Set<ClassReference> candidateClassReferences =
Streams.stream(canditates)
.map(DexClass::getClassReference)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
index 955ff04..3787201 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.classmerging.horizontal.dispatch;
-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;
@@ -13,6 +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 org.junit.Test;
public class OverrideAbstractMethodWithDefaultTest extends HorizontalClassMergingTestBase {
@@ -31,14 +31,13 @@
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.addHorizontallyMergedClassesInspector(
- inspector ->
- inspector.assertIsCompleteMergeGroup(I.class, J.class).assertNoOtherClassesMerged())
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("J", "B2")
.inspect(
codeInspector -> {
assertThat(codeInspector.clazz(I.class), isPresent());
- assertThat(codeInspector.clazz(J.class), isAbsent());
+ assertThat(codeInspector.clazz(J.class), isPresent());
assertThat(codeInspector.clazz(A.class), isPresent());
assertThat(codeInspector.clazz(B1.class), isPresent());
assertThat(codeInspector.clazz(B2.class), 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 0af1e85..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
@@ -5,7 +5,6 @@
package com.android.tools.r8.classmerging.horizontal.dispatch;
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 com.android.tools.r8.NeverClassInline;
@@ -37,11 +36,9 @@
} else {
inspector
.assertClassesNotMerged(A.class, B.class)
- .assertIsCompleteMergeGroup(I.class, J.class)
.assertIsCompleteMergeGroup(
SyntheticItemsTestUtils.syntheticCompanionClass(I.class),
- SyntheticItemsTestUtils.syntheticCompanionClass(J.class))
- .assertNoOtherClassesMerged();
+ SyntheticItemsTestUtils.syntheticCompanionClass(J.class));
}
})
.run(parameters.getRuntime(), Main.class)
@@ -49,9 +46,7 @@
.inspect(
codeInspector -> {
assertThat(codeInspector.clazz(I.class), isPresent());
- assertThat(
- codeInspector.clazz(J.class),
- onlyIf(parameters.canUseDefaultAndStaticInterfaceMethods(), isPresent()));
+ assertThat(codeInspector.clazz(J.class), isPresent());
assertThat(codeInspector.clazz(A.class), isPresent());
assertThat(codeInspector.clazz(B.class), isPresent());
assertThat(codeInspector.clazz(C.class), isPresent());
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 7b5437e..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
@@ -5,7 +5,6 @@
package com.android.tools.r8.classmerging.horizontal.dispatch;
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 com.android.tools.r8.NeverClassInline;
@@ -43,11 +42,9 @@
} else {
inspector
.assertClassesNotMerged(A.class, B.class)
- .assertIsCompleteMergeGroup(I.class, J.class)
.assertIsCompleteMergeGroup(
SyntheticItemsTestUtils.syntheticCompanionClass(I.class),
- SyntheticItemsTestUtils.syntheticCompanionClass(J.class))
- .assertNoOtherClassesMerged();
+ SyntheticItemsTestUtils.syntheticCompanionClass(J.class));
}
})
.run(parameters.getRuntime(), Main.class)
@@ -55,9 +52,7 @@
.inspect(
codeInspector -> {
assertThat(codeInspector.clazz(I.class), isPresent());
- assertThat(
- codeInspector.clazz(J.class),
- onlyIf(parameters.canUseDefaultAndStaticInterfaceMethods(), isPresent()));
+ assertThat(codeInspector.clazz(J.class), isPresent());
assertThat(codeInspector.clazz(Parent.class), isPresent());
assertThat(codeInspector.clazz(A.class), isPresent());
assertThat(codeInspector.clazz(B.class), isPresent());
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/ClassHierarchyCycleAfterMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/ClassHierarchyCycleAfterMergingTest.java
index 4d81f1a..650d804 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/ClassHierarchyCycleAfterMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/ClassHierarchyCycleAfterMergingTest.java
@@ -44,6 +44,11 @@
// hierarchy.
.addHorizontallyMergedClassesInspector(
HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .addOptionsModification(
+ options -> {
+ assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+ options.horizontalClassMergerOptions().enableInterfaceMerging();
+ })
.enableNoHorizontalClassMergingAnnotations()
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
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 cc19dd2..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
@@ -7,6 +7,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
@@ -15,9 +16,8 @@
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -25,21 +25,19 @@
@RunWith(Parameterized.class)
public class CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest extends TestBase {
- private final boolean enableInterfaceMergingInInitial;
private final TestParameters parameters;
- @Parameterized.Parameters(name = "{1}, enableInterfaceMergingInInitial: {0}")
- public static List<Object[]> data() {
- return buildParameters(
- BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest(
- boolean enableInterfaceMergingInInitial, TestParameters parameters) {
- this.enableInterfaceMergingInInitial = enableInterfaceMergingInInitial;
+ TestParameters parameters) {
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())
@@ -50,22 +48,23 @@
// the default method J.m() to A.
.addHorizontallyMergedClassesInspector(
inspector -> {
- inspector
- .assertIsCompleteMergeGroup(A.class, B.class)
- .assertMergedInto(B.class, A.class);
if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
- inspector.assertClassesNotMerged(I.class, J.class, K.class);
+ 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);
}
})
.addOptionsModification(
options -> {
- if (enableInterfaceMergingInInitial) {
- options.horizontalClassMergerOptions().setEnableInterfaceMergingInInitial();
- }
+ assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+ options.horizontalClassMergerOptions().enableInterfaceMerging();
})
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
@@ -81,10 +80,10 @@
assertThat(aClassSubject, isImplementing(inspector.clazz(I.class)));
assertThat(aClassSubject, isImplementing(inspector.clazz(K.class)));
- ClassSubject cClassSubject = inspector.clazz(C.class);
- assertThat(cClassSubject, isPresent());
+ ClassSubject bClassSubject = inspector.clazz(C.class);
+ assertThat(bClassSubject, isPresent());
assertThat(
- cClassSubject,
+ bClassSubject,
isImplementing(
inspector.clazz(
parameters.canUseDefaultAndStaticInterfaceMethods()
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 dca511f..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
@@ -51,6 +51,11 @@
inspector.assertIsCompleteMergeGroup(I.class, J.class);
}
})
+ .addOptionsModification(
+ options -> {
+ assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+ options.horizontalClassMergerOptions().enableInterfaceMerging();
+ })
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
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 62cf256..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
@@ -52,6 +52,12 @@
inspector.assertNoClassesMerged();
}
})
+ .addOptionsModification(
+ options -> {
+ assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+ options.horizontalClassMergerOptions().enableInterfaceMerging();
+ options.horizontalClassMergerOptions().setIgnoreRuntimeTypeChecksForTesting();
+ })
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
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 e8bd34f..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
@@ -36,6 +36,11 @@
.addKeepMainRule(Main.class)
.addHorizontallyMergedClassesInspector(
inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+ .addOptionsModification(
+ options -> {
+ assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+ options.horizontalClassMergerOptions().enableInterfaceMerging();
+ })
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
.noClassInliningOfSynthetics()
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 a88f6c1..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
@@ -36,6 +36,11 @@
.addKeepMainRule(Main.class)
.addHorizontallyMergedClassesInspector(
inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+ .addOptionsModification(
+ options -> {
+ assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+ options.horizontalClassMergerOptions().enableInterfaceMerging();
+ })
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
.noClassInliningOfSynthetics()
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 dae6f36..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
@@ -38,6 +38,11 @@
.addKeepMainRule(Main.class)
.addHorizontallyMergedClassesInspector(
inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+ .addOptionsModification(
+ options -> {
+ assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+ options.horizontalClassMergerOptions().enableInterfaceMerging();
+ })
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
.noClassInliningOfSynthetics()
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 e002263..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
@@ -38,6 +38,11 @@
.addKeepMainRule(Main.class)
.addHorizontallyMergedClassesInspector(
inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+ .addOptionsModification(
+ options -> {
+ assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+ options.horizontalClassMergerOptions().enableInterfaceMerging();
+ })
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
.noClassInliningOfSynthetics()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java
index ef6ac67..0807547 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java
@@ -42,6 +42,11 @@
.addKeepMainRule(Main.class)
.addHorizontallyMergedClassesInspector(
inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+ .addOptionsModification(
+ options -> {
+ assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+ options.horizontalClassMergerOptions().enableInterfaceMerging();
+ })
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoUnusedInterfaceRemovalAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithoutDefaultMethodsMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithoutDefaultMethodsMergingTest.java
index f33ec1c..5f30eff 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithoutDefaultMethodsMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithoutDefaultMethodsMergingTest.java
@@ -42,6 +42,11 @@
.addKeepMainRule(Main.class)
.addHorizontallyMergedClassesInspector(
inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+ .addOptionsModification(
+ options -> {
+ assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+ options.horizontalClassMergerOptions().enableInterfaceMerging();
+ })
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoUnusedInterfaceRemovalAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfaceChainMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfaceChainMergingTest.java
deleted file mode 100644
index 80160a4..0000000
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfaceChainMergingTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (c) 2021, 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.classmerging.horizontal.interfaces;
-
-import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import com.android.tools.r8.NoUnusedInterfaceRemoval;
-import com.android.tools.r8.NoVerticalClassMerging;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class EmptyInterfaceChainMergingTest extends TestBase {
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
- }
-
- public EmptyInterfaceChainMergingTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- @Test
- public void test() throws Exception {
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass())
- .addKeepMainRule(Main.class)
- .addHorizontallyMergedClassesInspector(
- inspector ->
- inspector
- .assertIsCompleteMergeGroup(I.class, J.class, K.class)
- .assertNoOtherClassesMerged())
- .enableNoUnusedInterfaceRemovalAnnotations()
- .enableNoVerticalClassMergingAnnotations()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspect(
- inspector -> {
- ClassSubject aClassSubject = inspector.clazz(A.class);
- assertThat(aClassSubject, isPresent());
- assertThat(aClassSubject, isImplementing(inspector.clazz(I.class)));
- })
- .run(parameters.getRuntime(), Main.class)
- .assertSuccess();
- }
-
- static class Main {
-
- public static void main(String[] args) {
- System.out.println(A.class);
- }
- }
-
- @NoUnusedInterfaceRemoval
- @NoVerticalClassMerging
- interface I {}
-
- @NoUnusedInterfaceRemoval
- @NoVerticalClassMerging
- interface J extends I {}
-
- @NoUnusedInterfaceRemoval
- @NoVerticalClassMerging
- interface K extends J {}
-
- static class A implements K {}
-}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfacesMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfacesMergingTest.java
index 7cbdad2..24e5de8 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfacesMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfacesMergingTest.java
@@ -40,6 +40,11 @@
.addKeepMainRule(Main.class)
.addHorizontallyMergedClassesInspector(
inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+ .addOptionsModification(
+ options -> {
+ assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+ options.horizontalClassMergerOptions().enableInterfaceMerging();
+ })
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
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 f13b7e0..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
@@ -36,6 +36,11 @@
.addKeepMainRule(Main.class)
.addHorizontallyMergedClassesInspector(
inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+ .addOptionsModification(
+ options -> {
+ assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+ options.horizontalClassMergerOptions().enableInterfaceMerging();
+ })
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
.noClassInliningOfSynthetics()
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 d529fc9..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
@@ -36,6 +36,11 @@
.addKeepMainRule(Main.class)
.addHorizontallyMergedClassesInspector(
inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+ .addOptionsModification(
+ options -> {
+ assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+ options.horizontalClassMergerOptions().enableInterfaceMerging();
+ })
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
.noClassInliningOfSynthetics()
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 e8630bb..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
@@ -50,6 +50,11 @@
inspector.assertIsCompleteMergeGroup(I.class, J.class);
}
})
+ .addOptionsModification(
+ options -> {
+ assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+ options.horizontalClassMergerOptions().enableInterfaceMerging();
+ })
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
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 6905202..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
@@ -40,17 +40,17 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(TestClass.class)
+ // TODO(b/173990042): Extend horizontal class merging to interfaces.
.addHorizontallyMergedClassesInspector(
inspector -> {
if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
- inspector.assertIsCompleteMergeGroup(I.class, J.class).assertNoOtherClassesMerged();
+ inspector.assertNoClassesMerged();
} else {
inspector
- .assertClassesNotMerged(I.class, J.class)
.assertClassReferencesMerged(
SyntheticItemsTestUtils.syntheticCompanionClass(I.class),
SyntheticItemsTestUtils.syntheticCompanionClass(J.class))
- .assertNoOtherClassesMerged();
+ .assertClassesNotMerged(I.class, J.class);
}
})
.enableInliningAnnotations()
@@ -62,9 +62,11 @@
// 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), isAbsent());
+ assertThat(inspector.clazz(J.class), isPresent());
} else {
assertThat(inspector.clazz(syntheticCompanionClass(I.class)), isPresent());
assertThat(inspector.clazz(syntheticCompanionClass(J.class)), isAbsent());
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index 2f157d0..5468cae 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -1052,8 +1052,7 @@
CF_DIR.resolve("pkg/SimpleInterfaceImplRetriever.class"),
CF_DIR.resolve("pkg/SimpleInterfaceImplRetriever$SimpleInterfaceImpl.class"),
CF_DIR.resolve("pkg/SimpleInterfaceImplRetriever$1.class"),
- CF_DIR.resolve("NeverInline.class"),
- CF_DIR.resolve("NoHorizontalClassMerging.class")
+ CF_DIR.resolve("NeverInline.class")
};
// SimpleInterface cannot be merged into SimpleInterfaceImpl because SimpleInterfaceImpl
// is in a different package and is not public.
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
index 71ddab8..1bfbaac 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
@@ -63,7 +63,7 @@
.addOptionsModification(
options ->
options.testing.horizontalClassMergingTarget =
- (appView, candidates, target) -> candidates.iterator().next())
+ (candidates, target) -> candidates.iterator().next())
.addHorizontallyMergedClassesInspector(
inspector ->
inspector.assertMergedInto(BaseWithStatic.class, AFeatureWithStatic.class))
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java
index 12bc414..9d11655 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java
@@ -85,7 +85,6 @@
void foo();
}
- @NoHorizontalClassMerging
@NoVerticalClassMerging
interface J {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
index 6bc0862..768fbd6 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -43,7 +42,6 @@
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
- .enableNoHorizontalClassMergingAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
@@ -88,7 +86,6 @@
void m();
}
- @NoHorizontalClassMerging
@NoVerticalClassMerging
interface J extends I {
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
index de5d7f0..550356e 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
@@ -12,26 +12,21 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.R8;
-import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.android.tools.r8.utils.graphinspector.GraphInspector;
-import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
@@ -209,7 +204,6 @@
.addKeepRules("-keepclassmembers class * { @" + PRESENT_ANNOTATION + " *** *(...); }")
.addDontWarnGoogle()
.addDontWarnJavaxNullableAnnotation()
- .apply(this::configureHorizontalClassMerging)
.compile()
.graphInspector();
@@ -228,7 +222,6 @@
+ " *** *(...); }")
.addDontWarnGoogle()
.addDontWarnJavaxNullableAnnotation()
- .apply(this::configureHorizontalClassMerging)
.compile()
.graphInspector();
assertRetainedClassesEqual(referenceInspector, ifThenKeepClassMembersInspector);
@@ -248,7 +241,6 @@
+ " *** *(...); }")
.addDontWarnGoogle()
.addDontWarnJavaxNullableAnnotation()
- .apply(this::configureHorizontalClassMerging)
.compile()
.graphInspector();
assertRetainedClassesEqual(referenceInspector, ifThenKeepClassesWithMembersInspector);
@@ -270,27 +262,11 @@
+ " *** <2>(...); }")
.addDontWarnGoogle()
.addDontWarnJavaxNullableAnnotation()
- .apply(this::configureHorizontalClassMerging)
.compile()
.graphInspector();
assertRetainedClassesEqual(referenceInspector, ifHasMemberThenKeepClassInspector);
}
- private void configureHorizontalClassMerging(R8FullTestBuilder testBuilder) {
- // Attempt to ensure similar class merging across different builds by choosing the merge target
- // as the class with the lexicographically smallest original name.
- testBuilder.addOptionsModification(
- options ->
- options.testing.horizontalClassMergingTarget =
- (appView, candidates, target) -> {
- List<DexProgramClass> classes = Lists.newArrayList(candidates);
- classes.sort(
- Comparator.comparing(
- clazz -> appView.graphLens().getOriginalType(clazz.getType())));
- return ListUtils.first(classes);
- });
- }
-
private void assertRetainedClassesEqual(
GraphInspector referenceResult, GraphInspector conditionalResult) {
assertRetainedClassesEqual(referenceResult, conditionalResult, false, false);
diff --git a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
index 7f6323a..69b0457 100644
--- a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
@@ -11,11 +11,10 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import com.android.tools.r8.NoHorizontalClassMerging;
-import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.jasmin.JasminBuilder;
@@ -34,17 +33,14 @@
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
-@NoHorizontalClassMerging
interface B112452064SuperInterface1 {
void foo();
}
-@NoHorizontalClassMerging
interface B112452064SuperInterface2 {
void bar();
}
-@NoHorizontalClassMerging
interface B112452064SubInterface extends B112452064SuperInterface1, B112452064SuperInterface2 {}
class B112452064TestMain {
@@ -85,8 +81,7 @@
@Parameters(name = "{1}, argument removal: {0}")
public static List<Object[]> data() {
- return buildParameters(
- BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
}
public ParameterTypeTest(boolean enableArgumentRemoval, TestParameters parameters) {
@@ -132,8 +127,7 @@
options.enableUnusedInterfaceRemoval = enableUnusedInterfaceRemoval;
options.enableVerticalClassMerging = enableVerticalClassMerging;
})
- .enableNoHorizontalClassMergingAnnotations()
- .setMinApi(parameters.getApiLevel())
+ .setMinApi(parameters.getRuntime())
.compile()
.run(parameters.getRuntime(), B112452064TestMain.class)
.assertSuccessWithOutput(javaResult.stdout)
@@ -291,6 +285,7 @@
"return");
final String mainClassName = mainClass.name;
+ String proguardConfig = keepMainProguardConfiguration(mainClassName, false, false);
// Run input program on java.
Path outputDirectory = temp.newFolder().toPath();
@@ -300,34 +295,36 @@
assertThat(javaResult.stdout, containsString(bar.name));
assertEquals(-1, javaResult.stderr.indexOf("ClassNotFoundException"));
- testForR8(parameters.getBackend())
- .addProgramClassFileData(jasminBuilder.buildClasses())
- .addKeepMainRule(mainClassName)
- .addOptionsModification(
+ AndroidApp processedApp =
+ compileWithR8(
+ jasminBuilder.build(),
+ proguardConfig,
options -> {
// Disable inlining to avoid the (short) tested method from being inlined and removed.
options.enableInlining = false;
options.enableArgumentRemoval = enableArgumentRemoval;
- })
- .noMinification()
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspect(
- inspector -> {
- ClassSubject subSubject = inspector.clazz(sub.name);
- assertNotEquals(enableArgumentRemoval, subSubject.isPresent());
- })
- .run(parameters.getRuntime(), mainClassName)
- .applyIf(
- enableArgumentRemoval || parameters.isCfRuntime(),
- SingleTestRunResult::assertSuccess,
- result ->
- result.assertFailureWithErrorThatMatches(
- containsString(
- parameters.getDexRuntimeVersion().isNewerThan(Version.V4_4_4)
- ? "type Precise Reference: Foo[] but expected Reference: SubInterface[]"
- : "[LFoo; is not instance of [LSubInterface;")))
- .assertStderrMatches(not(containsString("ClassNotFoundException")));
+ });
+
+ // Run processed (output) program on ART
+ ProcessResult artResult = runOnArtRaw(processedApp, mainClassName);
+ if (enableArgumentRemoval) {
+ assertEquals(0, artResult.exitCode);
+ } else {
+ assertNotEquals(0, artResult.exitCode);
+
+ DexVm.Version currentVersion = ToolHelper.getDexVm().getVersion();
+ String errorMessage =
+ currentVersion.isNewerThan(Version.V4_4_4)
+ ? "type Precise Reference: Foo[] but expected Reference: SubInterface[]"
+ : "[LFoo; is not instance of [LSubInterface;";
+ assertThat(artResult.stderr, containsString(errorMessage));
+ }
+
+ assertEquals(-1, artResult.stderr.indexOf("ClassNotFoundException"));
+
+ CodeInspector inspector = new CodeInspector(processedApp);
+ ClassSubject subSubject = inspector.clazz(sub.name);
+ assertNotEquals(enableArgumentRemoval, subSubject.isPresent());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java
index b427736..c6e5eff 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoUnusedInterfaceRemoval;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
@@ -44,7 +43,6 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
- .enableNoHorizontalClassMergingAnnotations()
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
@@ -81,7 +79,6 @@
}
}
- @NoHorizontalClassMerging
@NoUnusedInterfaceRemoval
@NoVerticalClassMerging
interface I {
@@ -97,7 +94,6 @@
}
}
- @NoHorizontalClassMerging
@NoUnusedInterfaceRemoval
@NoVerticalClassMerging
interface J extends I {}
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 b39fd97..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
@@ -21,7 +21,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
@@ -33,8 +32,6 @@
private final DexItemFactory dexItemFactory;
private final HorizontallyMergedClasses horizontallyMergedClasses;
- private final Set<ClassReference> seen = new HashSet<>();
-
public HorizontallyMergedClassesInspector(
DexItemFactory dexItemFactory, HorizontallyMergedClasses horizontallyMergedClasses) {
this.dexItemFactory = dexItemFactory;
@@ -105,7 +102,6 @@
+ StringUtils.join(", ", unmerged, DexType::getTypeName),
0,
unmerged.size());
- seen.addAll(types.stream().map(DexType::asClassReference).collect(Collectors.toList()));
return this;
}
@@ -121,17 +117,6 @@
return this;
}
- public HorizontallyMergedClassesInspector assertNoOtherClassesMerged() {
- horizontallyMergedClasses.forEachMergeGroup(
- (sources, target) -> {
- for (DexType source : sources) {
- assertTrue(source.getTypeName(), seen.contains(source.asClassReference()));
- }
- assertTrue(target.getTypeName(), seen.contains(target.asClassReference()));
- });
- return this;
- }
-
public HorizontallyMergedClassesInspector assertClassesNotMerged(Class<?>... classes) {
return assertClassesNotMerged(Arrays.asList(classes));
}
@@ -160,7 +145,6 @@
assertTrue(type.isClassType());
assertFalse(horizontallyMergedClasses.hasBeenMergedOrIsMergeTarget(type));
}
- seen.addAll(types.stream().map(DexType::asClassReference).collect(Collectors.toList()));
return this;
}
@@ -220,7 +204,6 @@
classReferences.size() - 1,
sources.size());
assertTrue(types.containsAll(sources));
- seen.addAll(classReferences);
return this;
}