Rewrite minifier to use DexClassAndMethod instead of DexEncodedMethod
Change-Id: If918ebf3ed3c2a2acd03d691626ea0694c1c373d
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 14c4975..ea46ea8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -298,6 +298,10 @@
return Iterables.filter(virtualMethods(), predicate::test);
}
+ public Iterable<DexClassAndMethod> virtualClassMethods() {
+ return Iterables.transform(virtualMethods(), method -> DexClassAndMethod.create(this, method));
+ }
+
public void addVirtualMethod(DexEncodedMethod method) {
methodCollection.addVirtualMethod(method);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexString.java b/src/main/java/com/android/tools/r8/graph/DexString.java
index 95be1fa..4ec188f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexString.java
+++ b/src/main/java/com/android/tools/r8/graph/DexString.java
@@ -30,6 +30,10 @@
return identical(this, other);
}
+ public final boolean isNotIdenticalTo(DexString other) {
+ return !isIdenticalTo(other);
+ }
+
public static final DexString[] EMPTY_ARRAY = {};
private static final int ARRAY_CHARACTER = '[';
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index 2f4b4b9..f15bba1 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexProgramClass;
@@ -126,13 +127,12 @@
}
ReservedFieldNamingState reservationState =
getOrCreateReservedFieldNamingState(frontier);
- for (DexEncodedField field : clazz.fields()) {
- DexString reservedName = strategy.getReservedName(field, clazz);
+ for (DexClassAndField field : clazz.classFields()) {
+ DexString reservedName = strategy.getReservedName(field);
if (reservedName != null) {
- reservationState.markReserved(
- reservedName, field.getReference().name, field.getReference().type);
+ reservationState.markReserved(reservedName, field);
// TODO(b/148846065): Consider lazily computing the renaming on actual lookups.
- if (reservedName != field.getReference().name) {
+ if (reservedName.isNotIdenticalTo(field.getName())) {
renaming.put(field.getReference(), reservedName);
}
}
@@ -214,16 +214,15 @@
});
}
- @SuppressWarnings("ReferenceEquality")
private void renameFieldsInUnrelatedClasspathClasses() {
if (appView.options().getProguardConfiguration().hasApplyMappingFile()) {
appView
.appInfo()
.forEachReferencedClasspathClass(
clazz -> {
- for (DexEncodedField field : clazz.fields()) {
- DexString reservedName = strategy.getReservedName(field, clazz);
- if (reservedName != null && reservedName != field.getReference().name) {
+ for (DexClassAndField field : clazz.classFields()) {
+ DexString reservedName = strategy.getReservedName(field);
+ if (reservedName != null && reservedName.isNotIdenticalTo(field.getName())) {
renaming.put(field.getReference(), reservedName);
}
}
@@ -267,8 +266,7 @@
.forEachProgramField(
field -> {
DexString newName = renameField(field, state);
- namesToBeReservedInImplementsSubclasses.markReserved(
- newName, field.getReference().name, field.getReference().type);
+ namesToBeReservedInImplementsSubclasses.markReserved(newName, field);
});
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNamingState.java b/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
index e507112..6490228 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
@@ -49,7 +49,7 @@
}
public DexString getOrCreateNameFor(ProgramField field) {
- DexString reservedName = strategy.getReservedName(field.getDefinition(), field.getHolder());
+ DexString reservedName = strategy.getReservedName(field);
if (reservedName != null) {
return reservedName;
}
@@ -57,10 +57,6 @@
return getOrCreateInternalState(field.getReference()).createNewName(field);
}
- public void includeReservations(ReservedFieldNamingState reservedNames) {
- this.reservedNames.includeReservations(reservedNames);
- }
-
@Override
public InternalState createInternalState() {
return new InternalState();
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNamingStateBase.java b/src/main/java/com/android/tools/r8/naming/FieldNamingStateBase.java
index 693f373..b9f8454 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNamingStateBase.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNamingStateBase.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.shaking.ProguardConfiguration;
import java.util.Map;
abstract class FieldNamingStateBase<T> {
@@ -36,11 +35,10 @@
return internalStates.computeIfAbsent(internalStateKey, key -> createInternalState());
}
+ @SuppressWarnings("UnusedVariable")
private DexType getInternalStateKey(DexType type) {
- ProguardConfiguration proguardConfiguration = appView.options().getProguardConfiguration();
- return proguardConfiguration.isOverloadAggressively()
- ? type
- : appView.dexItemFactory().voidType;
+ // Returning the given type instead of void will implement aggressive overloading.
+ return appView.dexItemFactory().voidType;
}
abstract T createInternalState();
diff --git a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
index 4f1544e..d3bfef8 100644
--- a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
@@ -3,9 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
@@ -16,11 +19,12 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.DisjointSets;
import com.android.tools.r8.utils.MethodJavaSignatureEquivalence;
-import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.collections.DexClassAndMethodMap;
+import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
import com.google.common.base.Equivalence;
import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.io.PrintStream;
import java.util.ArrayList;
@@ -110,11 +114,11 @@
this.iface = iface;
}
- DexString getReservedName(DexEncodedMethod method) {
+ DexString getReservedName(DexClassAndMethod method) {
// If an interface is kept and we are using applymapping, the renamed name for this method
// is tracked on this level.
if (appView.options().getProguardConfiguration().hasApplyMappingFile()) {
- DexString reservedName = minifierState.getReservedName(method, iface);
+ DexString reservedName = minifierState.getReservedName(method);
if (reservedName != null) {
return reservedName;
}
@@ -143,7 +147,7 @@
this.reservationTypes.add(type);
}
- void reserveName(DexString reservedName, DexEncodedMethod method) {
+ void reserveName(DexString reservedName, DexClassAndMethod method) {
forAll(
s -> {
s.reservationTypes.forEach(
@@ -154,7 +158,7 @@
});
}
- boolean isAvailable(DexString candidate, DexEncodedMethod method) {
+ boolean isAvailable(DexString candidate, DexClassAndMethod method) {
Boolean result =
forAny(
s -> {
@@ -169,14 +173,14 @@
return result == null || result;
}
- void addRenaming(DexString newName, DexEncodedMethod method) {
+ void addRenaming(DexString newName, DexClassAndMethod method) {
forAll(
s ->
s.reservationTypes.forEach(
resType -> minifierState.getNamingState(resType).addRenaming(newName, method)));
}
- <T> void forAll(Consumer<InterfaceReservationState> action) {
+ void forAll(Consumer<InterfaceReservationState> action) {
forAny(
s -> {
action.accept(s);
@@ -238,20 +242,19 @@
class InterfaceMethodGroupState implements Comparable<InterfaceMethodGroupState> {
private final Set<DexCallSite> callSites = new HashSet<>();
- private final Map<DexEncodedMethod, Set<InterfaceReservationState>> methodStates =
- new HashMap<>();
- private final List<DexEncodedMethod> callSiteCollidingMethods = new ArrayList<>();
+ private final DexClassAndMethodMap<Set<InterfaceReservationState>> methodStates =
+ DexClassAndMethodMap.create();
+ private final List<DexClassAndMethod> callSiteCollidingMethods = new ArrayList<>();
- void addState(DexEncodedMethod method, InterfaceReservationState interfaceState) {
- methodStates.computeIfAbsent(method, m -> new HashSet<>()).add(interfaceState);
+ void addState(DexClassAndMethod method, InterfaceReservationState interfaceState) {
+ methodStates.computeIfAbsent(method, ignoreKey(HashSet::new)).add(interfaceState);
}
void appendMethodGroupState(InterfaceMethodGroupState state) {
callSites.addAll(state.callSites);
callSiteCollidingMethods.addAll(state.callSiteCollidingMethods);
- for (DexEncodedMethod key : state.methodStates.keySet()) {
- methodStates.computeIfAbsent(key, k -> new HashSet<>()).addAll(state.methodStates.get(key));
- }
+ state.methodStates.forEach(
+ (key, value) -> methodStates.computeIfAbsent(key, ignoreKey(HashSet::new)).addAll(value));
}
void addCallSite(DexCallSite callSite) {
@@ -260,7 +263,6 @@
callSites.add(callSite);
}
- @SuppressWarnings("ReferenceEquality")
DexString getReservedName() {
if (methodStates.isEmpty()) {
return null;
@@ -268,13 +270,11 @@
// It is perfectly fine to have multiple reserved names inside a group. If we have an identity
// reservation, we have to prioritize that over the others, otherwise we just propose the
// first ordered reserved name since we do not allow overwriting the name.
- List<DexEncodedMethod> sortedMethods = Lists.newArrayList(methodStates.keySet());
- sortedMethods.sort((x, y) -> x.getReference().compareTo(y.getReference()));
DexString reservedName = null;
- for (DexEncodedMethod method : sortedMethods) {
+ for (DexClassAndMethod method : methodStates.getKeysSorted()) {
for (InterfaceReservationState state : methodStates.get(method)) {
DexString stateReserved = state.getReservedName(method);
- if (stateReserved == method.getName()) {
+ if (method.getName().isIdenticalTo(stateReserved)) {
return method.getName();
} else if (stateReserved != null) {
reservedName = stateReserved;
@@ -320,7 +320,7 @@
});
}
- void forEachState(BiConsumer<DexEncodedMethod, InterfaceReservationState> action) {
+ void forEachState(BiConsumer<DexClassAndMethod, InterfaceReservationState> action) {
forAnyState(
(s, i) -> {
action.accept(s, i);
@@ -328,21 +328,25 @@
});
}
- <T> T forAnyState(BiFunction<DexEncodedMethod, InterfaceReservationState, T> callback) {
- T returnValue;
- for (Map.Entry<DexEncodedMethod, Set<InterfaceReservationState>> entry :
- methodStates.entrySet()) {
- for (InterfaceReservationState state : entry.getValue()) {
- returnValue = callback.apply(entry.getKey(), state);
- if (returnValue != null) {
- return returnValue;
- }
- }
+ <T> T forAnyState(BiFunction<DexClassAndMethod, InterfaceReservationState, T> callback) {
+ TraversalContinuation<T, Void> traversalContinuation =
+ methodStates.traverse(
+ (key, value) -> {
+ for (InterfaceReservationState state : value) {
+ T returnValue = callback.apply(key, state);
+ if (returnValue != null) {
+ return TraversalContinuation.doBreak(returnValue);
+ }
+ }
+ return TraversalContinuation.doContinue();
+ });
+ if (traversalContinuation.isBreak()) {
+ return traversalContinuation.asBreak().getValue();
}
return null;
}
- boolean containsReservation(DexEncodedMethod method, DexType reservationType) {
+ boolean containsReservation(DexClassAndMethod method, DexType reservationType) {
Set<InterfaceReservationState> states = methodStates.get(method);
if (states != null) {
for (InterfaceReservationState state : states) {
@@ -361,15 +365,30 @@
}
}
+ // Replacing the use of MethodJavaSignatureEquivalence by MethodSignatureEquivalence implements
+ // aggressive overloading.
+ private static final Equivalence<DexClassAndMethod> equivalence =
+ new Equivalence<>() {
+
+ @Override
+ protected boolean doEquivalent(DexClassAndMethod method, DexClassAndMethod other) {
+ return MethodJavaSignatureEquivalence.get()
+ .equivalent(method.getReference(), other.getReference());
+ }
+
+ @Override
+ protected int doHash(DexClassAndMethod method) {
+ return MethodJavaSignatureEquivalence.get().hash(method.getReference());
+ }
+ };
+
private final AppView<AppInfoWithLiveness> appView;
private final SubtypingInfo subtypingInfo;
- private final Equivalence<DexMethod> equivalence;
- private final Equivalence<DexEncodedMethod> definitionEquivalence;
private final MethodNameMinifier.State minifierState;
/** A map from DexMethods to all the states linked to interfaces they appear in. */
- private final Map<Wrapper<DexEncodedMethod>, InterfaceMethodGroupState> globalStateMap =
- new HashMap<>();
+ private final DexClassAndMethodMap<InterfaceMethodGroupState> globalStateMap =
+ createDexClassAndMethodMap();
/** A map for caching all interface states. */
private final Map<DexType, InterfaceReservationState> interfaceStateMap = new HashMap<>();
@@ -379,25 +398,19 @@
this.appView = appView;
this.minifierState = minifierState;
this.subtypingInfo = subtypingInfo;
- this.equivalence =
- appView.options().getProguardConfiguration().isOverloadAggressively()
- ? MethodSignatureEquivalence.get()
- : MethodJavaSignatureEquivalence.get();
- this.definitionEquivalence =
- new Equivalence<>() {
- @Override
- protected boolean doEquivalent(DexEncodedMethod method, DexEncodedMethod other) {
- return equivalence.equivalent(method.getReference(), other.getReference());
- }
-
- @Override
- protected int doHash(DexEncodedMethod method) {
- return equivalence.hash(method.getReference());
- }
- };
}
- private Comparator<Wrapper<DexEncodedMethod>> getDefaultInterfaceMethodOrdering() {
+ private static <V> DexClassAndMethodMap<V> createDexClassAndMethodMap() {
+ return new DexClassAndMethodMap<>(new HashMap<>()) {
+
+ @Override
+ protected Wrapper<DexClassAndMethod> wrap(DexClassAndMethod method) {
+ return equivalence.wrap(method);
+ }
+ };
+ }
+
+ private Comparator<DexClassAndMethod> getDefaultInterfaceMethodOrdering() {
return Comparator.comparing(globalStateMap::get);
}
@@ -430,10 +443,9 @@
for (DexClass iface : interfaces) {
InterfaceReservationState inheritanceState = interfaceStateMap.get(iface.type);
assert inheritanceState != null;
- for (DexEncodedMethod method : iface.methods()) {
- Wrapper<DexEncodedMethod> key = definitionEquivalence.wrap(method);
+ for (DexClassAndMethod method : iface.classMethods()) {
globalStateMap
- .computeIfAbsent(key, k -> new InterfaceMethodGroupState())
+ .computeIfAbsent(method, ignoreKey(InterfaceMethodGroupState::new))
.addState(method, inheritanceState);
}
}
@@ -451,21 +463,21 @@
// Note that if the input does not use multi-interface lambdas unificationParent will remain
// empty.
timing.begin("Union-find");
- DisjointSets<Wrapper<DexEncodedMethod>> unification = new DisjointSets<>();
+ DisjointSets<Wrapper<DexClassAndMethod>> unification = new DisjointSets<>();
liveCallSites.forEach(
callSite -> {
- Set<Wrapper<DexEncodedMethod>> callSiteMethods = new HashSet<>();
+ Set<Wrapper<DexClassAndMethod>> callSiteMethods = new HashSet<>();
// Don't report errors, as the set of call sites is a conservative estimate, and can
// refer to interfaces which has been removed.
- Set<DexEncodedMethod> implementedMethods =
+ DexClassAndMethodSet implementedMethods =
appView.appInfo().lookupLambdaImplementedMethods(callSite, appView);
- for (DexEncodedMethod method : implementedMethods) {
- Wrapper<DexEncodedMethod> wrapped = definitionEquivalence.wrap(method);
- InterfaceMethodGroupState groupState = globalStateMap.get(wrapped);
- assert groupState != null : wrapped;
+ for (DexClassAndMethod method : implementedMethods) {
+ Wrapper<DexClassAndMethod> wrapper = equivalence.wrap(method);
+ InterfaceMethodGroupState groupState = globalStateMap.get(wrapper);
+ assert groupState != null : wrapper;
groupState.addCallSite(callSite);
- callSiteMethods.add(wrapped);
+ callSiteMethods.add(wrapper);
}
if (callSiteMethods.isEmpty()) {
return;
@@ -480,8 +492,8 @@
// name.
DexClass iface = appView.definitionFor(implementedInterfaces.get(i));
assert iface.isInterface();
- for (DexEncodedMethod implementedMethod : implementedMethods) {
- for (DexEncodedMethod virtualMethod : iface.virtualMethods()) {
+ for (DexClassAndMethod implementedMethod : implementedMethods) {
+ for (DexClassAndMethod virtualMethod : iface.virtualClassMethods()) {
boolean differentName = implementedMethod.getName() != virtualMethod.getName();
if (differentName
&& MethodJavaSignatureEquivalence.getEquivalenceIgnoreName()
@@ -489,8 +501,7 @@
implementedMethod.getReference(), virtualMethod.getReference())) {
InterfaceMethodGroupState interfaceMethodGroupState =
globalStateMap.computeIfAbsent(
- definitionEquivalence.wrap(implementedMethod),
- k -> new InterfaceMethodGroupState());
+ implementedMethod, ignoreKey(InterfaceMethodGroupState::new));
interfaceMethodGroupState.callSiteCollidingMethods.add(virtualMethod);
}
}
@@ -499,9 +510,9 @@
}
if (callSiteMethods.size() > 1) {
// Implemented interfaces have different protos. Unify them.
- Wrapper<DexEncodedMethod> mainKey = callSiteMethods.iterator().next();
- Wrapper<DexEncodedMethod> representative = unification.findOrMakeSet(mainKey);
- for (Wrapper<DexEncodedMethod> key : callSiteMethods) {
+ Wrapper<DexClassAndMethod> mainKey = callSiteMethods.iterator().next();
+ Wrapper<DexClassAndMethod> representative = unification.findOrMakeSet(mainKey);
+ for (Wrapper<DexClassAndMethod> key : callSiteMethods) {
unification.unionWithMakeSet(representative, key);
}
}
@@ -512,32 +523,32 @@
// We now have roots for all unions. Add all of the states for the groups to the method state
// for the unions to allow consistent naming across different protos.
timing.begin("States for union");
- Map<Wrapper<DexEncodedMethod>, Set<Wrapper<DexEncodedMethod>>> unions =
- unification.collectSets();
-
- for (Wrapper<DexEncodedMethod> wrapped : unions.keySet()) {
- InterfaceMethodGroupState groupState = globalStateMap.get(wrapped);
- assert groupState != null;
-
- for (Wrapper<DexEncodedMethod> groupedMethod : unions.get(wrapped)) {
- DexEncodedMethod method = groupedMethod.get();
- assert method != null;
- groupState.appendMethodGroupState(globalStateMap.get(groupedMethod));
- }
- }
+ DexClassAndMethodMap<Set<Wrapper<DexClassAndMethod>>> unions = createDexClassAndMethodMap();
+ unification.consumeSets(
+ (representative, element) ->
+ unions.computeIfAbsent(representative, ignoreKey(HashSet::new)).add(element));
+ unions.forEach(
+ (representative, elements) -> {
+ InterfaceMethodGroupState groupState = globalStateMap.get(representative);
+ assert groupState != null;
+ for (Wrapper<DexClassAndMethod> groupedMethod : elements) {
+ groupState.appendMethodGroupState(globalStateMap.get(groupedMethod));
+ }
+ });
timing.end();
timing.begin("Sort");
// Filter out the groups that is included both in the unification and in the map. We sort the
// methods by the number of dependent states, so that we use short names for method that are
// referenced in many places.
- List<Wrapper<DexEncodedMethod>> interfaceMethodGroups =
- globalStateMap.keySet().stream()
+ List<DexClassAndMethod> interfaceMethodGroups =
+ globalStateMap
+ .streamWrappedKeys()
.filter(unification::isRepresentativeOrNotPresent)
+ .map(Wrapper::get)
.sorted(
appView
- .options()
- .testing
+ .testing()
.minifier
.getInterfaceMethodOrderingOrDefault(getDefaultInterfaceMethodOrdering()))
.collect(Collectors.toList());
@@ -549,8 +560,8 @@
timing.begin("Reserve in groups");
// It is important that this entire phase is run before given new names, to ensure all
// reservations are propagated to all naming states.
- List<Wrapper<DexEncodedMethod>> nonReservedMethodGroups = new ArrayList<>();
- for (Wrapper<DexEncodedMethod> interfaceMethodGroup : interfaceMethodGroups) {
+ List<DexClassAndMethod> nonReservedMethodGroups = new ArrayList<>();
+ for (DexClassAndMethod interfaceMethodGroup : interfaceMethodGroups) {
InterfaceMethodGroupState groupState = globalStateMap.get(interfaceMethodGroup);
assert groupState != null;
DexString reservedName = groupState.getReservedName();
@@ -564,39 +575,43 @@
timing.end();
timing.begin("Rename in groups");
- for (Wrapper<DexEncodedMethod> interfaceMethodGroup : nonReservedMethodGroups) {
+ for (DexClassAndMethod interfaceMethodGroup : nonReservedMethodGroups) {
InterfaceMethodGroupState groupState = globalStateMap.get(interfaceMethodGroup);
assert groupState != null;
assert groupState.getReservedName() == null;
- DexString newName = assignNewName(interfaceMethodGroup.get(), groupState);
+ DexString newName = assignNewName(interfaceMethodGroup, groupState);
assert newName != null;
Set<String> loggingFilter = appView.options().extensiveInterfaceMethodMinifierLoggingFilter;
if (!loggingFilter.isEmpty()) {
- Set<DexEncodedMethod> sourceMethods = groupState.methodStates.keySet();
- if (sourceMethods.stream()
- .map(DexEncodedMethod::toSourceString)
+ if (groupState
+ .methodStates
+ .streamKeys()
+ .map(DexClassAndMethod::toSourceString)
.anyMatch(loggingFilter::contains)) {
- print(interfaceMethodGroup.get().getReference(), sourceMethods, System.out);
+ print(
+ interfaceMethodGroup.getReference(),
+ groupState.methodStates.getKeysSorted(),
+ System.out);
}
}
}
// After all naming is completed for callsites, we must ensure to rename all interface methods
// that can collide with the callsite method name.
- for (Wrapper<DexEncodedMethod> interfaceMethodGroup : nonReservedMethodGroups) {
+ for (DexClassAndMethod interfaceMethodGroup : nonReservedMethodGroups) {
InterfaceMethodGroupState groupState = globalStateMap.get(interfaceMethodGroup);
if (groupState.callSiteCollidingMethods.isEmpty()) {
continue;
}
- DexEncodedMethod key = interfaceMethodGroup.get();
- MethodNamingState<?> keyNamingState = minifierState.getNamingState(key.getHolderType());
- DexString existingRenaming = keyNamingState.newOrReservedNameFor(key);
+ MethodNamingState<?> keyNamingState =
+ minifierState.getNamingState(interfaceMethodGroup.getHolderType());
+ DexString existingRenaming = keyNamingState.newOrReservedNameFor(interfaceMethodGroup);
assert existingRenaming != null;
- for (DexEncodedMethod collidingMethod : groupState.callSiteCollidingMethods) {
+ for (DexClassAndMethod collidingMethod : groupState.callSiteCollidingMethods) {
DexString newNameInGroup = newNameInGroup(collidingMethod, keyNamingState, groupState);
minifierState.putRenaming(collidingMethod, newNameInGroup);
MethodNamingState<?> methodNamingState =
- minifierState.getNamingState(collidingMethod.getReference().holder);
+ minifierState.getNamingState(collidingMethod.getHolderType());
methodNamingState.addRenaming(newNameInGroup, collidingMethod);
keyNamingState.addRenaming(newNameInGroup, collidingMethod);
}
@@ -606,7 +621,7 @@
timing.end(); // end compute timing
}
- private DexString assignNewName(DexEncodedMethod method, InterfaceMethodGroupState groupState) {
+ private DexString assignNewName(DexClassAndMethod method, InterfaceMethodGroupState groupState) {
assert groupState.getReservedName() == null;
assert groupState.methodStates.containsKey(method);
assert groupState.containsReservation(method, method.getHolderType());
@@ -620,7 +635,7 @@
}
private DexString newNameInGroup(
- DexEncodedMethod method,
+ DexClassAndMethod method,
MethodNamingState<?> namingState,
InterfaceMethodGroupState groupState) {
// Check if the name is available in all states.
@@ -663,50 +678,52 @@
}));
}
- private boolean verifyAllCallSitesAreRepresentedIn(List<Wrapper<DexEncodedMethod>> groups) {
- Set<Wrapper<DexEncodedMethod>> unifiedMethods = new HashSet<>(groups);
+ private boolean verifyAllCallSitesAreRepresentedIn(List<DexClassAndMethod> groups) {
+ Set<Wrapper<DexClassAndMethod>> unifiedMethods = new HashSet<>(groups.size());
+ groups.forEach(group -> unifiedMethods.add(equivalence.wrap(group)));
Set<DexCallSite> unifiedSeen = new HashSet<>();
Set<DexCallSite> seen = new HashSet<>();
- for (Map.Entry<Wrapper<DexEncodedMethod>, InterfaceMethodGroupState> state :
- globalStateMap.entrySet()) {
- for (DexCallSite callSite : state.getValue().callSites) {
- seen.add(callSite);
- if (unifiedMethods.contains(state.getKey())) {
- boolean added = unifiedSeen.add(callSite);
- assert added;
- }
- }
- }
+ globalStateMap.forEach(
+ (key, value) -> {
+ for (DexCallSite callSite : value.callSites) {
+ seen.add(callSite);
+ if (unifiedMethods.contains(equivalence.wrap(key))) {
+ boolean added = unifiedSeen.add(callSite);
+ assert added;
+ }
+ }
+ });
assert seen.size() == unifiedSeen.size();
assert unifiedSeen.containsAll(seen);
return true;
}
- private boolean verifyAllMethodsAreRepresentedIn(List<Wrapper<DexEncodedMethod>> groups) {
- Set<Wrapper<DexEncodedMethod>> unifiedMethods = new HashSet<>(groups);
+ private boolean verifyAllMethodsAreRepresentedIn(List<DexClassAndMethod> groups) {
+ Set<Wrapper<DexClassAndMethod>> unifiedMethods = new HashSet<>(groups.size());
+ groups.forEach(group -> unifiedMethods.add(equivalence.wrap(group)));
Set<DexEncodedMethod> unifiedSeen = Sets.newIdentityHashSet();
Set<DexEncodedMethod> seen = Sets.newIdentityHashSet();
- for (Map.Entry<Wrapper<DexEncodedMethod>, InterfaceMethodGroupState> state :
- globalStateMap.entrySet()) {
- for (DexEncodedMethod method : state.getValue().methodStates.keySet()) {
- seen.add(method);
- if (unifiedMethods.contains(state.getKey())) {
- boolean added = unifiedSeen.add(method);
- assert added;
- }
- }
- }
+ globalStateMap.forEach(
+ (representative, value) ->
+ value.methodStates.forEachKey(
+ method -> {
+ seen.add(method.getDefinition());
+ if (unifiedMethods.contains(equivalence.wrap(representative))) {
+ boolean added = unifiedSeen.add(method.getDefinition());
+ assert added;
+ }
+ }));
assert seen.size() == unifiedSeen.size();
assert unifiedSeen.containsAll(seen);
return true;
}
- private void print(DexMethod method, Set<DexEncodedMethod> sourceMethods, PrintStream out) {
+ private void print(DexMethod method, List<DexClassAndMethod> sourceMethods, PrintStream out) {
out.println("-----------------------------------------------------------------------");
out.println("assignNameToInterfaceMethod(`" + method.toSourceString() + "`)");
out.println("-----------------------------------------------------------------------");
out.println("Source methods:");
- for (DexEncodedMethod sourceMethod : sourceMethods) {
+ for (DexClassAndMethod sourceMethod : sourceMethods) {
out.println(" " + sourceMethod.toSourceString());
}
out.println("States:");
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNamingStrategy.java b/src/main/java/com/android/tools/r8/naming/MemberNamingStrategy.java
index 58a4dc6..15b2aae 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNamingStrategy.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNamingStrategy.java
@@ -5,8 +5,9 @@
package com.android.tools.r8.naming;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndField;
+import com.android.tools.r8.graph.DexClassAndMember;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.ProgramField;
@@ -15,7 +16,7 @@
public interface MemberNamingStrategy {
DexString next(
- DexEncodedMethod method,
+ DexClassAndMethod method,
InternalNamingState internalState,
BiPredicate<DexString, DexMethod> isAvailable);
@@ -24,9 +25,13 @@
InternalNamingState internalState,
BiPredicate<DexString, ProgramField> isAvailable);
- DexString getReservedName(DexEncodedMethod method, DexClass holder);
+ DexString getReservedName(DexClassAndMethod method);
- DexString getReservedName(DexEncodedField field, DexClass holder);
+ DexString getReservedName(DexClassAndField field);
- boolean allowMemberRenaming(DexClass holder);
+ boolean allowMemberRenaming(DexClass clazz);
+
+ default boolean allowMemberRenaming(DexClassAndMember<?, ?> member) {
+ return allowMemberRenaming(member.getHolder());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index 4dd3d15..fa5089d 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -8,6 +8,8 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMember;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
@@ -19,15 +21,17 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.WorkList;
+import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
@@ -105,9 +109,8 @@
// from the method name minifier to the interface method name minifier.
class State {
- @SuppressWarnings("ReferenceEquality")
- void putRenaming(DexEncodedMethod key, DexString newName) {
- if (newName != key.getName()) {
+ void putRenaming(DexClassAndMethod key, DexString newName) {
+ if (newName.isNotIdenticalTo(key.getName())) {
renaming.put(key.getReference(), newName);
}
}
@@ -129,8 +132,8 @@
return frontiers.getOrDefault(type, type);
}
- DexString getReservedName(DexEncodedMethod method, DexClass holder) {
- return strategy.getReservedName(method, holder);
+ DexString getReservedName(DexClassAndMethod method) {
+ return strategy.getReservedName(method);
}
}
@@ -163,14 +166,9 @@
}
private Function<DexMethod, ?> getReservationKeyTransform() {
- if (appView.options().getProguardConfiguration().isOverloadAggressively()
- && appView.options().isGeneratingClassFiles()) {
- // Use the full proto as key, hence reuse names based on full signature.
- return method -> method.proto;
- } else {
- // Only use the parameters as key, hence do not reuse names on return type.
- return method -> method.proto.parameters;
- }
+ // Only use the parameters as key, hence do not reuse names on return type. Returning the full
+ // proto here implements aggressive overloading.
+ return DexMethod::getParameters;
}
private Function<DexMethod, ?> getNamingKeyTransform() {
@@ -244,23 +242,27 @@
.getOrDefault(clazz.superType, rootNamingState)
.createChild(reservationState));
if (strategy.allowMemberRenaming(clazz)) {
- for (DexEncodedMethod method : clazz.allMethodsSorted()) {
- assignNameToMethod(clazz, method, namingState);
+ List<DexClassAndMethod> allMethodsSorted =
+ ListUtils.sort(
+ clazz.classMethods(),
+ Comparator.comparing(DexClassAndMember::getReference),
+ clazz.getMethodCollection().size());
+ for (DexClassAndMethod method : allMethodsSorted) {
+ assignNameToMethod(method, namingState);
}
}
});
}
- @SuppressWarnings("ReferenceEquality")
private void renameMethodsInUnrelatedClasspathClasses() {
if (appView.options().getProguardConfiguration().hasApplyMappingFile()) {
appView
.appInfo()
.forEachReferencedClasspathClass(
clazz -> {
- for (DexEncodedMethod method : clazz.methods()) {
- DexString reservedName = strategy.getReservedName(method, clazz);
- if (reservedName != null && reservedName != method.getReference().name) {
+ for (DexClassAndMethod method : clazz.classMethods()) {
+ DexString reservedName = strategy.getReservedName(method);
+ if (reservedName != null && reservedName.isNotIdenticalTo(method.getName())) {
renaming.put(method.getReference(), reservedName);
}
}
@@ -268,20 +270,18 @@
}
}
- @SuppressWarnings("ReferenceEquality")
- private void assignNameToMethod(
- DexClass holder, DexEncodedMethod method, MethodNamingState<?> state) {
- if (method.isInitializer()) {
+ private void assignNameToMethod(DexClassAndMethod method, MethodNamingState<?> state) {
+ if (method.getDefinition().isInitializer()) {
return;
}
// The strategy may have an explicit naming for this member which we query first. It may be that
// the strategy will return the identity name, for which we have to look into a previous
// renaming tracked by the state.
- DexString newName = strategy.getReservedName(method, holder);
- if (newName == null || newName == method.getName()) {
+ DexString newName = strategy.getReservedName(method);
+ if (newName == null || newName.isIdenticalTo(method.getName())) {
newName = state.newOrReservedNameFor(method);
}
- if (method.getName() != newName) {
+ if (newName.isNotIdenticalTo(method.getName())) {
renaming.put(method.getReference(), newName);
}
state.addRenaming(newName, method);
@@ -335,20 +335,22 @@
// have to do byte-code rewriting against a mapping file to observe the issue. Doing that they
// may as well just adjust the keep rules to keep the targets of bridges.
// See b/290711987 for an actual issue regarding this.
- Set<DexEncodedMethod> bridgeMethodCandidates = Sets.newIdentityHashSet();
- Iterable<DexEncodedMethod> methods = shuffleMethods(holder.methods(), appView.options());
- for (DexEncodedMethod method : methods) {
- DexString reservedName = strategy.getReservedName(method, holder);
+ DexClassAndMethodSet bridgeMethodCandidates = DexClassAndMethodSet.create();
+ Iterable<DexClassAndMethod> methods =
+ shuffleMethods(holder.classMethods(), appView.options());
+ for (DexClassAndMethod method : methods) {
+ DexString reservedName = strategy.getReservedName(method);
if (reservedName != null) {
state.reserveName(reservedName, method);
- } else if (appView.options().isGeneratingClassFiles() && method.isSyntheticBridgeMethod()) {
+ } else if (appView.options().isGeneratingClassFiles()
+ && method.getDefinition().isSyntheticBridgeMethod()) {
bridgeMethodCandidates.add(method);
}
}
Map<DexString, Set<Integer>> methodNamesToReserve =
computeBridgesThatAreReserved(holder, bridgeMethodCandidates);
if (!methodNamesToReserve.isEmpty()) {
- for (DexEncodedMethod method : methods) {
+ for (DexClassAndMethod method : methods) {
if (methodNamesToReserve
.getOrDefault(method.getName(), Collections.emptySet())
.contains(method.getProto().getArity())) {
@@ -360,7 +362,7 @@
}
private Map<DexString, Set<Integer>> computeBridgesThatAreReserved(
- DexClass holder, Set<DexEncodedMethod> methods) {
+ DexClass holder, DexClassAndMethodSet methods) {
if (methods.isEmpty()) {
return Collections.emptyMap();
}
@@ -495,8 +497,8 @@
// Shuffles the given methods if assertions are enabled and deterministic debugging is disabled.
// Used to ensure that the generated output is deterministic.
- private static Iterable<DexEncodedMethod> shuffleMethods(
- Iterable<DexEncodedMethod> methods, InternalOptions options) {
- return options.testing.irOrdering.order(methods);
+ private static Iterable<DexClassAndMethod> shuffleMethods(
+ Iterable<DexClassAndMethod> methods, InternalOptions options) {
+ return options.testing.irOrdering.orderClassMethods(methods);
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNamingState.java b/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
index 922c062..7a38768 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.naming.MethodNamingState.InternalNewNameState;
@@ -45,12 +45,12 @@
this, this.keyTransform, this.namingStrategy, frontierReservationState);
}
- DexString newOrReservedNameFor(DexEncodedMethod method) {
+ DexString newOrReservedNameFor(DexClassAndMethod method) {
return newOrReservedNameFor(method, this::isAvailable);
}
DexString newOrReservedNameFor(
- DexEncodedMethod method, BiPredicate<DexString, DexMethod> isAvailable) {
+ DexClassAndMethod method, BiPredicate<DexString, DexMethod> isAvailable) {
DexString newName = getAssignedName(method.getReference());
if (newName != null) {
return newName;
@@ -67,14 +67,14 @@
return nextName(method, isAvailable);
}
- DexString nextName(DexEncodedMethod method, BiPredicate<DexString, DexMethod> isAvailable) {
+ DexString nextName(DexClassAndMethod method, BiPredicate<DexString, DexMethod> isAvailable) {
InternalNewNameState internalState = getOrCreateInternalState(method.getReference());
DexString newName = namingStrategy.next(method, internalState, isAvailable);
assert newName != null;
return newName;
}
- void addRenaming(DexString newName, DexEncodedMethod method) {
+ void addRenaming(DexString newName, DexClassAndMethod method) {
InternalNewNameState internalState = getOrCreateInternalState(method.getReference());
internalState.addRenaming(newName, method.getReference());
}
diff --git a/src/main/java/com/android/tools/r8/naming/MethodReservationState.java b/src/main/java/com/android/tools/r8/naming/MethodReservationState.java
index 61aed28..68d0378 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodReservationState.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodReservationState.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.naming;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.naming.MethodReservationState.InternalReservationState;
@@ -37,7 +37,7 @@
return new MethodReservationState<>(this, this.keyTransform);
}
- void reserveName(DexString reservedName, DexEncodedMethod method) {
+ void reserveName(DexString reservedName, DexClassAndMethod method) {
try {
getOrCreateInternalState(method.getReference()).reserveName(method, reservedName);
} catch (AssertionError err) {
@@ -92,7 +92,7 @@
return originalToReservedNames.get(MethodSignatureEquivalence.get().wrap(method));
}
- void reserveName(DexEncodedMethod method, DexString name) {
+ void reserveName(DexClassAndMethod method, DexString name) {
if (reservedNames == null) {
assert originalToReservedNames == null;
originalToReservedNames = new HashMap<>();
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 96275ab..6c08cef 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -3,19 +3,22 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.utils.StringUtils.EMPTY_CHAR_ARRAY;
import static com.android.tools.r8.utils.SymbolGenerationUtils.RESERVED_NAMES;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexClassAndField;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.naming.ClassNameMinifier.ClassNamingStrategy;
import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming;
@@ -206,8 +209,9 @@
@Override
public DexString reservedDescriptor(DexType type) {
- if (!appView.appInfo().isMinificationAllowed(type)) {
- return type.descriptor;
+ DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(type));
+ if (clazz == null || !appView.getKeepInfo(clazz).isMinificationAllowed(appView.options())) {
+ return type.getDescriptor();
}
return null;
}
@@ -277,10 +281,14 @@
@Override
public DexString next(
- DexEncodedMethod method,
+ DexClassAndMethod method,
InternalNamingState internalState,
BiPredicate<DexString, DexMethod> isAvailable) {
- assert checkAllowMemberRenaming(method.getHolderType());
+ if (!method.isProgramMethod()) {
+ assert isAvailable.test(method.getName(), method.getReference());
+ return method.getName();
+ }
+ assert allowMemberRenaming(method);
DexString candidate;
do {
candidate = getNextName(internalState);
@@ -293,7 +301,7 @@
ProgramField field,
InternalNamingState internalState,
BiPredicate<DexString, ProgramField> isAvailable) {
- assert checkAllowMemberRenaming(field.getHolderType());
+ assert allowMemberRenaming(field);
DexString candidate;
do {
candidate = getNextName(internalState);
@@ -306,40 +314,39 @@
}
@Override
- public DexString getReservedName(DexEncodedMethod method, DexClass holder) {
- if (!allowMemberRenaming(holder)
- || holder.accessFlags.isAnnotation()
- || method.accessFlags.isConstructor()
- || !appView.appInfo().isMinificationAllowed(method)) {
- return method.getReference().name;
+ public DexString getReservedName(DexClassAndMethod method) {
+ if (!allowMemberRenaming(method)) {
+ return method.getName();
+ }
+ assert method.isProgramMethod();
+ ProgramMethod programMethod = method.asProgramMethod();
+ if (method.getHolder().isAnnotation()
+ || method.getAccessFlags().isConstructor()
+ || !appView.getKeepInfo(programMethod).isMinificationAllowed(appView.options())) {
+ return method.getName();
}
if (desugaredLibraryRenaming
- && method.isLibraryMethodOverride().isTrue()
- && appView.typeRewriter.hasRewrittenTypeInSignature(
- method.getReference().proto, appView)) {
+ && method.getDefinition().isLibraryMethodOverride().isTrue()
+ && appView.typeRewriter.hasRewrittenTypeInSignature(method.getProto(), appView)) {
// With desugared library, call-backs names are reserved here.
- return method.getReference().name;
+ return method.getName();
}
return null;
}
@Override
- public DexString getReservedName(DexEncodedField field, DexClass holder) {
- if (holder.isLibraryClass() || !appView.appInfo().isMinificationAllowed(field)) {
- return field.getReference().name;
+ public DexString getReservedName(DexClassAndField field) {
+ ProgramField programField = field.asProgramField();
+ if (programField == null
+ || !appView.getKeepInfo(programField).isMinificationAllowed(appView.options())) {
+ return field.getName();
}
return null;
}
@Override
- public boolean allowMemberRenaming(DexClass holder) {
- return holder.isProgramClass();
- }
-
- public boolean checkAllowMemberRenaming(DexType holder) {
- DexClass clazz = appView.definitionFor(holder);
- assert clazz != null && allowMemberRenaming(clazz);
- return true;
+ public boolean allowMemberRenaming(DexClass clazz) {
+ return clazz.isProgramClass();
}
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/NamingLens.java b/src/main/java/com/android/tools/r8/naming/NamingLens.java
index ed4bbbb..3e551cf 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingLens.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -20,6 +21,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
import com.google.common.collect.Sets;
import java.util.Arrays;
import java.util.Set;
@@ -59,7 +61,7 @@
return callSite.methodName;
}
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
- Set<DexEncodedMethod> lambdaImplementedMethods =
+ DexClassAndMethodSet lambdaImplementedMethods =
appViewWithLiveness.appInfo().lookupLambdaImplementedMethods(callSite, appViewWithLiveness);
if (lambdaImplementedMethods.isEmpty()) {
return callSite.methodName;
@@ -70,7 +72,7 @@
lookupMethod(lambdaImplementedMethodReference, appView.dexItemFactory()).getName();
// Verify that all lambda implemented methods are renamed consistently.
assert lambdaImplementedMethods.stream()
- .map(DexEncodedMethod::getReference)
+ .map(DexClassAndMethod::getReference)
.map(reference -> lookupMethod(reference, appView.dexItemFactory()))
.map(DexMethod::getName)
.allMatch(name -> name == renamedMethodName);
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index 588666a..ddb57d6 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.naming;
import static com.android.tools.r8.graph.DexApplication.classesWithDeterministicOrder;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass;
import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.getInterfaceClassType;
import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.isCompanionClassType;
@@ -13,12 +14,15 @@
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.DexDefinition;
+import com.android.tools.r8.graph.DexClassAndField;
+import com.android.tools.r8.graph.DexClassAndMember;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
@@ -187,10 +191,9 @@
memberNaming -> addMemberNamings(type, memberNaming, nonPrivateMembers, false));
} else {
// We have to ensure we do not rename to an existing member, that cannot be renamed.
- if (clazz == null || !appView.options().isMinifying()) {
- notMappedReferences.add(type);
- } else if (appView.options().isMinifying()
- && !appView.appInfo().isMinificationAllowed(type)) {
+ DexProgramClass programClass = asProgramClassOrNull(clazz);
+ if (programClass == null
+ || !appView.getKeepInfo(programClass).isMinificationAllowed(appView.options())) {
notMappedReferences.add(type);
}
}
@@ -400,7 +403,9 @@
public DexString next(
DexType type, char[] packagePrefix, InternalNamingState state, Predicate<String> isUsed) {
assert !mappings.containsKey(type);
- assert appView.appInfo().isMinificationAllowed(type);
+ assert appView
+ .getKeepInfo(appView.definitionFor(type).asProgramClass())
+ .isMinificationAllowed(appView.options());
return super.next(
type,
packagePrefix,
@@ -420,19 +425,21 @@
// members that can be reserved differently in the hierarchy.
DexClass clazz = appView.appInfo().definitionForWithoutExistenceAssert(type);
if (clazz == null) {
- return type.descriptor;
- }
- if (clazz.isNotProgramClass() && mappings.containsKey(type)) {
- return mappings.get(type);
+ return type.getDescriptor();
}
if (clazz.isProgramClass()) {
- if (appView.appInfo().isMinificationAllowed(clazz.asProgramClass())) {
+ DexProgramClass programClass = clazz.asProgramClass();
+ if (appView.getKeepInfo(programClass).isMinificationAllowed(appView.options())) {
return mappings.get(type);
}
// TODO(b/136694827): Report a warning here if in the mapping since the user may find this
// non intuitive.
+ } else {
+ if (mappings.containsKey(type)) {
+ return mappings.get(type);
+ }
}
- return type.descriptor;
+ return type.getDescriptor();
}
@Override
@@ -456,13 +463,11 @@
@Override
@SuppressWarnings("ReferenceEquality")
public DexString next(
- DexEncodedMethod method,
+ DexClassAndMethod method,
InternalNamingState internalState,
BiPredicate<DexString, DexMethod> isAvailable) {
DexMethod reference = method.getReference();
- DexClass holder = appView.definitionForHolder(reference);
- assert holder != null;
- DexString reservedName = getReservedName(method, reference.name, holder);
+ DexString reservedName = getReservedName(method, reference.getName());
DexString nextName;
if (reservedName != null) {
if (!isAvailable.test(reservedName, reference)) {
@@ -471,11 +476,14 @@
nextName = reservedName;
} else {
assert !mappedNames.containsKey(reference);
- assert appView.appInfo().isMinificationAllowed(method);
+ assert !method.isProgramMethod()
+ || appView
+ .getKeepInfo(method.asProgramMethod())
+ .isMinificationAllowed(appView.options());
nextName = super.next(method, internalState, isAvailable);
}
- assert nextName == reference.name || !method.isInitializer();
- assert nextName == reference.name || !holder.isAnnotation();
+ assert nextName.isIdenticalTo(reference.getName()) || !method.getDefinition().isInitializer();
+ assert nextName.isIdenticalTo(reference.getName()) || !method.getHolder().isAnnotation();
return nextName;
}
@@ -485,8 +493,7 @@
InternalNamingState internalState,
BiPredicate<DexString, ProgramField> isAvailable) {
DexField reference = field.getReference();
- DexString reservedName =
- getReservedName(field.getDefinition(), reference.name, field.getHolder());
+ DexString reservedName = getReservedName(field, reference.getName());
if (reservedName != null) {
if (!isAvailable.test(reservedName, field)) {
reportReservationError(reference, reservedName);
@@ -494,35 +501,34 @@
return reservedName;
}
assert !mappedNames.containsKey(reference);
- assert appView.appInfo().isMinificationAllowed(field);
+ assert appView.getKeepInfo(field).isMinificationAllowed(appView.options());
return super.next(field, internalState, isAvailable);
}
@Override
- public DexString getReservedName(DexEncodedMethod method, DexClass holder) {
- return getReservedName(method, method.getReference().name, holder);
+ public DexString getReservedName(DexClassAndMethod method) {
+ return getReservedName(method, method.getName());
}
@Override
- public DexString getReservedName(DexEncodedField field, DexClass holder) {
- return getReservedName(field, field.getReference().name, holder);
+ public DexString getReservedName(DexClassAndField field) {
+ return getReservedName(field, field.getName());
}
- private DexString getReservedName(DexDefinition definition, DexString name, DexClass holder) {
- assert definition.isDexEncodedMethod() || definition.isDexEncodedField();
+ private DexString getReservedName(DexClassAndMember<?, ?> definition, DexString name) {
// Always consult the mapping for renamed members that are not on program path.
DexReference reference = definition.getReference();
- if (holder.isNotProgramClass()) {
+ if (definition.getHolder().isNotProgramClass()) {
if (mappedNames.containsKey(reference)) {
return factory.createString(mappedNames.get(reference).getRenamedName());
}
return name;
}
- assert holder.isProgramClass();
+ assert definition.isProgramMember();
DexString reservedName =
- definition.isDexEncodedMethod()
- ? super.getReservedName(definition.asDexEncodedMethod(), holder)
- : super.getReservedName(definition.asDexEncodedField(), holder);
+ definition.isMethod()
+ ? super.getReservedName(definition.asMethod())
+ : super.getReservedName(definition.asField());
if (reservedName != null) {
return reservedName;
}
@@ -533,7 +539,7 @@
}
@Override
- public boolean allowMemberRenaming(DexClass holder) {
+ public boolean allowMemberRenaming(DexClass clazz) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java b/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java
index 2b66d18..fe4799b 100644
--- a/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.ReservedFieldNamingState.InternalState;
@@ -43,8 +44,8 @@
return internalState == null ? null : internalState.getReservedByName(name);
}
- void markReserved(DexString name, DexString originalName, DexType type) {
- getOrCreateInternalState(type).markReserved(name, originalName);
+ void markReserved(DexString name, DexClassAndField field) {
+ getOrCreateInternalState(field.getType()).markReserved(name, field.getName());
}
void includeReservations(ReservedFieldNamingState reservedNames) {
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 e222e20..6fb0c2f 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -42,7 +42,6 @@
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
-import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
@@ -71,6 +70,7 @@
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.Visibility;
import com.android.tools.r8.utils.WorkList;
+import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.android.tools.r8.utils.collections.ThrowingSet;
import com.android.tools.r8.utils.structural.Ordered;
@@ -764,14 +764,14 @@
* @return Methods implemented by the lambda expression that created the {@code callSite}.
*/
@SuppressWarnings("ReferenceEquality")
- public Set<DexEncodedMethod> lookupLambdaImplementedMethods(
+ public DexClassAndMethodSet lookupLambdaImplementedMethods(
DexCallSite callSite, AppView<AppInfoWithLiveness> appView) {
assert checkIfObsolete();
List<DexType> callSiteInterfaces = LambdaDescriptor.getInterfaces(callSite, appView);
if (callSiteInterfaces == null || callSiteInterfaces.isEmpty()) {
- return Collections.emptySet();
+ return DexClassAndMethodSet.empty();
}
- Set<DexEncodedMethod> result = Sets.newIdentityHashSet();
+ DexClassAndMethodSet result = DexClassAndMethodSet.create();
Deque<DexType> worklist = new ArrayDeque<>(callSiteInterfaces);
Set<DexType> visited = Sets.newIdentityHashSet();
while (!worklist.isEmpty()) {
@@ -794,8 +794,9 @@
continue;
}
assert clazz.isInterface();
- for (DexEncodedMethod method : clazz.virtualMethods()) {
- if (method.getReference().name == callSite.methodName && method.accessFlags.isAbstract()) {
+ for (DexClassAndMethod method : clazz.virtualClassMethods()) {
+ if (method.getName().isIdenticalTo(callSite.methodName)
+ && method.getAccessFlags().isAbstract()) {
result.add(method);
}
}
@@ -1009,30 +1010,6 @@
return this;
}
- @Deprecated
- public boolean isMinificationAllowed(DexProgramClass clazz) {
- return options().isMinificationEnabled()
- && keepInfo.getInfo(clazz).isMinificationAllowed(options());
- }
-
- @Deprecated
- public boolean isMinificationAllowed(ProgramDefinition definition) {
- return options().isMinificationEnabled()
- && keepInfo.getInfo(definition).isMinificationAllowed(options());
- }
-
- @Deprecated
- public boolean isMinificationAllowed(DexDefinition definition) {
- return options().isMinificationEnabled()
- && keepInfo.getInfo(definition, this).isMinificationAllowed(options());
- }
-
- @Deprecated
- public boolean isMinificationAllowed(DexType reference) {
- return options().isMinificationEnabled()
- && keepInfo.getClassInfo(reference, this).isMinificationAllowed(options());
- }
-
public boolean isRepackagingAllowed(DexProgramClass clazz, AppView<?> appView) {
if (!keepInfo.getInfo(clazz).isRepackagingAllowed(options())) {
return false;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index d7e4ff2..98bb147 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -609,10 +609,6 @@
return rules;
}
- public boolean isOverloadAggressively() {
- return false;
- }
-
public List<String> getObfuscationDictionary() {
return obfuscationDictionary;
}
diff --git a/src/main/java/com/android/tools/r8/utils/DexClassAndMethodEquivalence.java b/src/main/java/com/android/tools/r8/utils/DexClassAndMethodEquivalence.java
new file mode 100644
index 0000000..acb221d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/DexClassAndMethodEquivalence.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.google.common.base.Equivalence;
+
+public class DexClassAndMethodEquivalence extends Equivalence<DexClassAndMethod> {
+
+ private static final DexClassAndMethodEquivalence INSTANCE = new DexClassAndMethodEquivalence();
+
+ private DexClassAndMethodEquivalence() {}
+
+ public static DexClassAndMethodEquivalence get() {
+ return INSTANCE;
+ }
+
+ @Override
+ protected boolean doEquivalent(DexClassAndMethod method, DexClassAndMethod other) {
+ return method.getDefinition() == other.getDefinition();
+ }
+
+ @Override
+ protected int doHash(DexClassAndMethod method) {
+ return method.getReference().hashCode();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/DisjointSets.java b/src/main/java/com/android/tools/r8/utils/DisjointSets.java
index d8e42b9..bc5e129 100644
--- a/src/main/java/com/android/tools/r8/utils/DisjointSets.java
+++ b/src/main/java/com/android/tools/r8/utils/DisjointSets.java
@@ -4,10 +4,13 @@
package com.android.tools.r8.utils;
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.function.BiConsumer;
/**
* Disjoint sets of instances of type T. Each of the sets will be represented by one of the
@@ -134,12 +137,18 @@
/** Returns the sets currently represented. */
public Map<T, Set<T>> collectSets() {
Map<T, Set<T>> unification = new HashMap<>();
+ consumeSets(
+ (representative, element) ->
+ unification.computeIfAbsent(representative, ignoreKey(HashSet::new)).add(element));
+ return unification;
+ }
+
+ public void consumeSets(BiConsumer<T, T> consumer) {
for (T element : parent.keySet()) {
// Find root with path-compression.
T representative = findSet(element);
- unification.computeIfAbsent(representative, k -> new HashSet<>()).add(element);
+ consumer.accept(representative, element);
}
- return unification;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/utils/IROrdering.java b/src/main/java/com/android/tools/r8/utils/IROrdering.java
index db97cfa..4d1508d 100644
--- a/src/main/java/com/android/tools/r8/utils/IROrdering.java
+++ b/src/main/java/com/android/tools/r8/utils/IROrdering.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.google.common.collect.Lists;
import java.util.Collection;
@@ -16,6 +17,8 @@
Iterable<DexEncodedMethod> order(Iterable<DexEncodedMethod> methods);
+ Iterable<DexClassAndMethod> orderClassMethods(Iterable<DexClassAndMethod> methods);
+
Collection<DexEncodedMethod> order(Collection<DexEncodedMethod> methods);
Set<DexEncodedMethod> order(Set<DexEncodedMethod> methods);
@@ -36,6 +39,11 @@
}
@Override
+ public Iterable<DexClassAndMethod> orderClassMethods(Iterable<DexClassAndMethod> methods) {
+ return methods;
+ }
+
+ @Override
public Collection<DexEncodedMethod> order(Collection<DexEncodedMethod> methods) {
return methods;
}
@@ -64,6 +72,13 @@
}
@Override
+ public List<DexClassAndMethod> orderClassMethods(Iterable<DexClassAndMethod> methods) {
+ List<DexClassAndMethod> toShuffle = Lists.newArrayList(methods);
+ Collections.shuffle(toShuffle);
+ return toShuffle;
+ }
+
+ @Override
public List<DexEncodedMethod> order(Collection<DexEncodedMethod> methods) {
return order((Iterable<DexEncodedMethod>) methods);
}
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 24a5e7e..e38cfe8 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -51,6 +51,7 @@
import com.android.tools.r8.graph.AppView.WholeProgramOptimizations;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItem;
@@ -114,7 +115,6 @@
import com.android.tools.r8.verticalclassmerging.VerticalClassMergerOptions;
import com.android.tools.r8.verticalclassmerging.VerticallyMergedClasses;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Equivalence.Wrapper;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -2502,11 +2502,10 @@
public Comparator<DexMethod> interfaceMethodOrdering = null;
- public Comparator<Wrapper<DexEncodedMethod>> getInterfaceMethodOrderingOrDefault(
- Comparator<Wrapper<DexEncodedMethod>> comparator) {
+ public Comparator<? super DexClassAndMethod> getInterfaceMethodOrderingOrDefault(
+ Comparator<DexClassAndMethod> comparator) {
if (interfaceMethodOrdering != null) {
- return (a, b) ->
- interfaceMethodOrdering.compare(a.get().getReference(), b.get().getReference());
+ return (a, b) -> interfaceMethodOrdering.compare(a.getReference(), b.getReference());
}
return comparator;
}
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index 98402c3..bbec355 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -297,6 +298,13 @@
void accept(T item, int index);
}
+ public static <T> List<T> sort(Iterable<T> items, Comparator<T> comparator, int numberOfItems) {
+ List<T> sorted = new ArrayList<>(numberOfItems);
+ Iterables.addAll(sorted, items);
+ sorted.sort(comparator);
+ return sorted;
+ }
+
public static <T> List<T> sort(Collection<T> items, Comparator<T> comparator) {
List<T> sorted = new ArrayList<>(items);
sorted.sort(comparator);
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramMethodEquivalence.java b/src/main/java/com/android/tools/r8/utils/ProgramMethodEquivalence.java
index cdb98b8..91553b8 100644
--- a/src/main/java/com/android/tools/r8/utils/ProgramMethodEquivalence.java
+++ b/src/main/java/com/android/tools/r8/utils/ProgramMethodEquivalence.java
@@ -18,7 +18,6 @@
}
@Override
- @SuppressWarnings("ReferenceEquality")
protected boolean doEquivalent(ProgramMethod method, ProgramMethod other) {
return method.getDefinition() == other.getDefinition();
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndFieldMapBase.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndFieldMapBase.java
index 522ff17..ae458e4 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndFieldMapBase.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndFieldMapBase.java
@@ -18,7 +18,7 @@
}
@Override
- Wrapper<K> wrap(K field) {
+ protected Wrapper<K> wrap(K field) {
return DexClassAndFieldEquivalence.get().wrap(field);
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMemberMap.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMemberMap.java
index eb747e4..6dd73cf 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMemberMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMemberMap.java
@@ -5,8 +5,12 @@
package com.android.tools.r8.utils.collections;
import com.android.tools.r8.graph.DexClassAndMember;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.TraversalContinuation;
import com.android.tools.r8.utils.TriPredicate;
import com.google.common.base.Equivalence.Wrapper;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.BiConsumer;
@@ -15,6 +19,7 @@
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
+import java.util.stream.Stream;
public abstract class DexClassAndMemberMap<K extends DexClassAndMember<?, ?>, V> {
@@ -37,7 +42,11 @@
}
public V computeIfAbsent(K member, Function<K, V> fn) {
- return backing.computeIfAbsent(wrap(member), key -> fn.apply(key.get()));
+ return computeIfAbsent(wrap(member), fn);
+ }
+
+ public V computeIfAbsent(Wrapper<K> wrapper, Function<K, V> fn) {
+ return backing.computeIfAbsent(wrapper, key -> fn.apply(key.get()));
}
public boolean containsKey(K member) {
@@ -48,6 +57,10 @@
backing.forEach((wrapper, value) -> consumer.accept(wrapper.get(), value));
}
+ public void forEachKey(Consumer<K> consumer) {
+ backing.keySet().forEach(wrapper -> consumer.accept(wrapper.get()));
+ }
+
public void forEachValue(Consumer<V> consumer) {
backing.values().forEach(consumer);
}
@@ -56,6 +69,16 @@
return backing.get(wrap(member));
}
+ public V get(Wrapper<K> wrapper) {
+ return backing.get(wrapper);
+ }
+
+ public List<K> getKeysSorted() {
+ List<K> keys = new ArrayList<>(size());
+ backing.keySet().forEach(key -> keys.add(key.get()));
+ return ListUtils.sort(keys, (x, y) -> x.getReference().compareTo(y.getReference()));
+ }
+
public V getOrDefault(K member, V defaultValue) {
return backing.getOrDefault(wrap(member), defaultValue);
}
@@ -94,5 +117,25 @@
return backing.size();
}
- abstract Wrapper<K> wrap(K member);
+ public Stream<K> streamKeys() {
+ return streamWrappedKeys().map(Wrapper::get);
+ }
+
+ public Stream<Wrapper<K>> streamWrappedKeys() {
+ return backing.keySet().stream();
+ }
+
+ public <TB, TC> TraversalContinuation<TB, TC> traverse(
+ BiFunction<K, V, TraversalContinuation<TB, TC>> fn) {
+ for (Entry<Wrapper<K>, V> entry : backing.entrySet()) {
+ TraversalContinuation<TB, TC> traversalContinuation =
+ fn.apply(entry.getKey().get(), entry.getValue());
+ if (traversalContinuation.shouldBreak()) {
+ return traversalContinuation;
+ }
+ }
+ return TraversalContinuation.doContinue();
+ }
+
+ protected abstract Wrapper<K> wrap(K member);
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodMap.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodMap.java
new file mode 100644
index 0000000..0728fc0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodMap.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.collections;
+
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.utils.DexClassAndMethodEquivalence;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.ImmutableMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+public class DexClassAndMethodMap<V> extends DexClassAndMemberMap<DexClassAndMethod, V> {
+
+ private static final DexClassAndMethodMap<?> EMPTY = new DexClassAndMethodMap<>(ImmutableMap::of);
+
+ private DexClassAndMethodMap(Supplier<Map<Wrapper<DexClassAndMethod>, V>> backingFactory) {
+ super(backingFactory);
+ }
+
+ protected DexClassAndMethodMap(Map<Wrapper<DexClassAndMethod>, V> backing) {
+ super(backing);
+ }
+
+ public static <V> DexClassAndMethodMap<V> create() {
+ return new DexClassAndMethodMap<>(HashMap::new);
+ }
+
+ public static <V> DexClassAndMethodMap<V> create(int capacity) {
+ return new DexClassAndMethodMap<>(new HashMap<>(capacity));
+ }
+
+ public static <V> DexClassAndMethodMap<V> createConcurrent() {
+ return new DexClassAndMethodMap<>(ConcurrentHashMap::new);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <V> DexClassAndMethodMap<V> empty() {
+ return (DexClassAndMethodMap<V>) EMPTY;
+ }
+
+ @Override
+ protected Wrapper<DexClassAndMethod> wrap(DexClassAndMethod method) {
+ return DexClassAndMethodEquivalence.get().wrap(method);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
index 007aa51..9b77365 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
@@ -42,7 +42,6 @@
@Override
public boolean add(T method) {
T existing = backing.put(method.getReference(), method);
- assert existing == null || existing.isStructurallyEqualTo(method);
return existing == null;
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodMap.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodMap.java
index b5c3c26..5a0a814 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodMap.java
@@ -43,7 +43,7 @@
}
@Override
- Wrapper<ProgramMethod> wrap(ProgramMethod method) {
+ protected Wrapper<ProgramMethod> wrap(ProgramMethod method) {
return ProgramMethodEquivalence.get().wrap(method);
}
}