Change field map in graph lenses to be bidirectional
This simplifies the lenses and makes the 'originalFieldSignatures' obsolete.
Change-Id: Idf76b5a6c140a2b01486e5826159b374c2e368d7
diff --git a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
index 887f10d..559d2f2 100644
--- a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
@@ -6,7 +6,8 @@
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.utils.MapUtils;
-import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableList;
@@ -25,8 +26,8 @@
*/
public final class AppliedGraphLens extends NonIdentityGraphLens {
- private final BidirectionalManyToOneMap<DexType, DexType> renamedTypeNames =
- new BidirectionalManyToOneMap<>();
+ private final MutableBidirectionalManyToOneMap<DexType, DexType> renamedTypeNames =
+ new BidirectionalManyToOneHashMap<>();
private final BiMap<DexField, DexField> originalFieldSignatures = HashBiMap.create();
private final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
@@ -102,11 +103,8 @@
@Override
public Iterable<DexType> getOriginalTypes(DexType type) {
- Set<DexType> originalTypes = renamedTypeNames.getKeysOrNull(type);
- if (originalTypes == null) {
- return ImmutableList.of(type);
- }
- return originalTypes;
+ Set<DexType> originalTypes = renamedTypeNames.getKeys(type);
+ return originalTypes.isEmpty() ? ImmutableList.of(type) : originalTypes;
}
@Override
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 5a10844..b2d04e8 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -15,10 +15,12 @@
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneRepresentativeMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
@@ -32,6 +34,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
/**
* A GraphLens implements a virtual view on top of the graph, used to delay global rewrites until
@@ -65,6 +68,10 @@
return reference;
}
+ public R getRewrittenReference(BidirectionalManyToOneRepresentativeMap<R, R> rewritings) {
+ return rewritings.getOrDefault(reference, reference);
+ }
+
public R getRewrittenReference(Map<R, R> rewritings) {
return rewritings.getOrDefault(reference, reference);
}
@@ -77,6 +84,11 @@
return reboundReference;
}
+ public R getRewrittenReboundReference(
+ BidirectionalManyToOneRepresentativeMap<R, R> rewritings) {
+ return rewritings.getOrDefault(reboundReference, reboundReference);
+ }
+
public R getRewrittenReboundReference(Map<R, R> rewritings) {
return rewritings.getOrDefault(reboundReference, reboundReference);
}
@@ -228,10 +240,10 @@
protected final Map<DexType, DexType> typeMap = new IdentityHashMap<>();
protected final Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
- protected final Map<DexField, DexField> fieldMap = new IdentityHashMap<>();
+ protected final MutableBidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap =
+ new BidirectionalManyToOneRepresentativeHashMap<>();
- protected final BiMap<DexField, DexField> originalFieldSignatures = HashBiMap.create();
- protected final BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures =
+ protected final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures =
new BidirectionalOneToOneHashMap<>();
public void map(DexType from, DexType to) {
@@ -248,13 +260,6 @@
methodMap.put(from, to);
}
- public void map(DexField from, DexField to) {
- if (from == to) {
- return;
- }
- fieldMap.put(from, to);
- }
-
public void move(DexMethod from, DexMethod to) {
if (from == to) {
return;
@@ -268,7 +273,6 @@
return;
}
fieldMap.put(from, to);
- originalFieldSignatures.put(to, from);
}
public GraphLens build(DexItemFactory dexItemFactory) {
@@ -283,7 +287,6 @@
typeMap,
methodMap,
fieldMap,
- originalFieldSignatures,
originalMethodSignatures,
previousLens,
dexItemFactory);
@@ -963,11 +966,10 @@
protected final Map<DexType, DexType> typeMap;
protected final Map<DexMethod, DexMethod> methodMap;
- protected final Map<DexField, DexField> fieldMap;
+ protected final BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap;
- // Maps that store the original signature of fields and methods that have been affected, for
- // example, by vertical class merging. Needed to generate a correct Proguard map in the end.
- protected final BiMap<DexField, DexField> originalFieldSignatures;
+ // Map that store the original signature of methods that have been affected, for example, by
+ // vertical class merging. Needed to generate a correct Proguard map in the end.
protected BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod>
originalMethodSignatures;
@@ -980,8 +982,7 @@
public NestedGraphLens(
Map<DexType, DexType> typeMap,
Map<DexMethod, DexMethod> methodMap,
- Map<DexField, DexField> fieldMap,
- BiMap<DexField, DexField> originalFieldSignatures,
+ BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap,
BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod> originalMethodSignatures,
GraphLens previousLens,
DexItemFactory dexItemFactory) {
@@ -993,7 +994,6 @@
this.typeMap = typeMap.isEmpty() ? null : typeMap;
this.methodMap = methodMap;
this.fieldMap = fieldMap;
- this.originalFieldSignatures = originalFieldSignatures;
this.originalMethodSignatures = originalMethodSignatures;
this.dexItemFactory = dexItemFactory;
}
@@ -1022,10 +1022,7 @@
@Override
public DexField getOriginalFieldSignature(DexField field) {
- DexField originalField =
- originalFieldSignatures != null
- ? originalFieldSignatures.getOrDefault(field, field)
- : field;
+ DexField originalField = fieldMap.getRepresentativeKeyOrDefault(field, field);
return getPrevious().getOriginalFieldSignature(originalField);
}
@@ -1038,9 +1035,7 @@
@Override
public DexField getRenamedFieldSignature(DexField originalField) {
DexField renamedField = getPrevious().getRenamedFieldSignature(originalField);
- return originalFieldSignatures != null
- ? originalFieldSignatures.inverse().getOrDefault(renamedField, renamedField)
- : renamedField;
+ return fieldMap.getOrDefault(renamedField, renamedField);
}
@Override
@@ -1221,10 +1216,15 @@
builder.append(entry.getKey().toSourceString()).append(" -> ");
builder.append(entry.getValue().toSourceString()).append(System.lineSeparator());
}
- for (Map.Entry<DexField, DexField> entry : fieldMap.entrySet()) {
- builder.append(entry.getKey().toSourceString()).append(" -> ");
- builder.append(entry.getValue().toSourceString()).append(System.lineSeparator());
- }
+ fieldMap.forEachManyToOneMapping(
+ (keys, value) -> {
+ builder.append(
+ keys.stream()
+ .map(DexField::toSourceString)
+ .collect(Collectors.joining("," + System.lineSeparator())));
+ builder.append(" -> ");
+ builder.append(value.toSourceString()).append(System.lineSeparator());
+ });
builder.append(getPrevious().toString());
return builder.toString();
}
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java
index 0b5bc41..89f34d7 100644
--- a/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java
@@ -8,7 +8,9 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.optimize.lambda.LambdaGroup;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
@@ -19,8 +21,10 @@
private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses;
public HorizontallyMergedLambdaClasses(Map<DexType, LambdaGroup> lambdas) {
- this.mergedClasses = new BidirectionalManyToOneMap<>();
+ MutableBidirectionalManyToOneMap<DexType, DexType> mergedClasses =
+ new BidirectionalManyToOneHashMap<>();
lambdas.forEach((lambda, group) -> mergedClasses.put(lambda, group.getGroupClassType()));
+ this.mergedClasses = mergedClasses;
}
public static HorizontallyMergedLambdaClasses empty() {
@@ -29,7 +33,7 @@
@Override
public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) {
- mergedClasses.forEach(consumer);
+ mergedClasses.forEachManyToOneMapping(consumer);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/StaticallyMergedClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/StaticallyMergedClasses.java
index 79e4ef7..0e1dbe5 100644
--- a/src/main/java/com/android/tools/r8/graph/classmerging/StaticallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/StaticallyMergedClasses.java
@@ -8,7 +8,10 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
+import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap;
import java.util.Set;
import java.util.function.BiConsumer;
@@ -21,7 +24,7 @@
}
public static StaticallyMergedClasses empty() {
- return new StaticallyMergedClasses(BidirectionalManyToOneMap.empty());
+ return new StaticallyMergedClasses(new EmptyBidirectionalOneToOneMap<>());
}
public static Builder builder() {
@@ -30,7 +33,7 @@
@Override
public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) {
- mergedClasses.forEach(consumer);
+ mergedClasses.forEachManyToOneMapping(consumer);
}
@Override
@@ -45,8 +48,8 @@
public static class Builder {
- private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses =
- new BidirectionalManyToOneMap<>();
+ private final MutableBidirectionalManyToOneMap<DexType, DexType> mergedClasses =
+ new BidirectionalManyToOneHashMap<>();
private Builder() {}
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
index 6ba32e9..b9ab790 100644
--- a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
+import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
@@ -22,12 +23,12 @@
}
public static VerticallyMergedClasses empty() {
- return new VerticallyMergedClasses(BidirectionalManyToOneMap.empty());
+ return new VerticallyMergedClasses(new EmptyBidirectionalOneToOneMap<>());
}
@Override
public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) {
- mergedClasses.forEach(consumer);
+ mergedClasses.forEachManyToOneMapping(consumer);
}
public Map<DexType, DexType> getForwardMap() {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
index 26ed21c..96282ee 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
@@ -5,11 +5,12 @@
package com.android.tools.r8.horizontalclassmerging;
import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerGraphLens.Builder;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ListUtils;
+import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
@@ -50,20 +51,19 @@
}
}
- private void mergeField(DexEncodedField oldField, DexEncodedField newField) {
- if (newField.isFinal() && !oldField.isFinal()) {
+ private void fixAccessFlags(DexEncodedField newField, Collection<DexEncodedField> oldFields) {
+ if (newField.isFinal() && Iterables.any(oldFields, oldField -> !oldField.isFinal())) {
newField.getAccessFlags().demoteFromFinal();
}
- lensBuilder.moveField(oldField.field, newField.field);
}
- private void mergeFields(DexEncodedField newField, Collection<DexEncodedField> oldFields) {
- DexField newFieldReference = newField.getReference();
-
- lensBuilder.moveField(newFieldReference, newFieldReference);
- lensBuilder.setRepresentativeField(newFieldReference, newFieldReference);
-
- oldFields.forEach(oldField -> mergeField(oldField, newField));
+ private void mergeFields(DexEncodedField newField, List<DexEncodedField> oldFields) {
+ fixAccessFlags(newField, oldFields);
+ lensBuilder.recordNewFieldSignature(
+ Iterables.transform(
+ IterableUtils.append(oldFields, newField), DexEncodedField::getReference),
+ newField.getReference(),
+ newField.getReference());
}
public void merge() {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java
index 7bc85d1..80180f6 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java
@@ -33,7 +33,7 @@
this.dexItemFactory = appView.dexItemFactory();
}
- private final boolean isFresh(DexField fieldReference) {
+ private boolean isFresh(DexField fieldReference) {
return !targetFields.containsKey(fieldReference);
}
@@ -48,7 +48,7 @@
field = field.toTypeSubstitutedField(newFieldReference);
targetFields.put(newFieldReference, field);
- lensBuilder.moveField(oldFieldReference, newFieldReference);
+ lensBuilder.recordNewFieldSignature(oldFieldReference, newFieldReference);
}
public void addFields(DexProgramClass toMerge) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
index 16f853e..9b472a4 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -13,8 +13,13 @@
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.conversion.ExtraParameter;
import com.android.tools.r8.utils.IterableUtils;
-import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneRepresentativeMap;
import com.google.common.collect.BiMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Streams;
import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
@@ -28,29 +33,24 @@
private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters;
private final Map<DexMethod, DexMethod> extraOriginalMethodSignatures;
private final HorizontallyMergedClasses mergedClasses;
- private final Map<DexField, DexField> extraOriginalFieldSignatures;
private HorizontalClassMergerGraphLens(
AppView<?> appView,
HorizontallyMergedClasses mergedClasses,
Map<DexMethod, List<ExtraParameter>> methodExtraParameters,
- Map<DexField, DexField> fieldMap,
+ BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap,
Map<DexMethod, DexMethod> methodMap,
- BiMap<DexField, DexField> originalFieldSignatures,
- BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures,
+ BidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures,
Map<DexMethod, DexMethod> extraOriginalMethodSignatures,
- Map<DexField, DexField> extraOriginalFieldSignatures,
GraphLens previousLens) {
super(
mergedClasses.getForwardMap(),
methodMap,
fieldMap,
- originalFieldSignatures,
originalMethodSignatures,
previousLens,
appView.dexItemFactory());
this.methodExtraParameters = methodExtraParameters;
- this.extraOriginalFieldSignatures = extraOriginalFieldSignatures;
this.extraOriginalMethodSignatures = extraOriginalMethodSignatures;
this.mergedClasses = mergedClasses;
}
@@ -82,15 +82,6 @@
return getPrevious().getOriginalMethodSignature(originalConstructor);
}
- @Override
- public DexField getOriginalFieldSignature(DexField field) {
- DexField originalField = extraOriginalFieldSignatures.get(field);
- if (originalField == null) {
- return super.getOriginalFieldSignature(field);
- }
- return getPrevious().getOriginalFieldSignature(originalField);
- }
-
/**
* If an overloaded constructor is requested, add the constructor id as a parameter to the
* constructor. Otherwise return the lookup on the underlying graph lens.
@@ -111,7 +102,8 @@
}
public static class Builder {
- private ManyToOneMap<DexField, DexField> fieldMap = new ManyToOneMap<>();
+ private MutableBidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap =
+ new BidirectionalManyToOneRepresentativeHashMap<>();
private ManyToOneMap<DexMethod, DexMethod> methodMap = new ManyToOneMap<>();
private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters =
new IdentityHashMap<>();
@@ -127,24 +119,14 @@
assert false;
return group.iterator().next();
});
- ManyToOneInverseMap<DexField, DexField> inverseFieldMap =
- fieldMap.inverse(
- group -> {
- // Every group should have a representative. Fail in debug mode.
- assert false;
- return group.iterator().next();
- });
-
return new HorizontalClassMergerGraphLens(
appView,
mergedClasses,
methodExtraParameters,
- fieldMap.getForwardMap(),
+ fieldMap,
methodMap.getForwardMap(),
- inverseFieldMap.getBiMap().getForwardBacking(),
inverseMethodMap.getBiMap(),
inverseMethodMap.getExtraMap(),
- inverseFieldMap.getExtraMap(),
appView.graphLens());
}
@@ -152,39 +134,51 @@
methodMap = methodMap.remap(remapMethods, Function.identity(), Function.identity());
}
- public void remapFields(BiMap<DexField, DexField> remapFields) {
- fieldMap = fieldMap.remap(remapFields, Function.identity(), Function.identity());
- }
-
- public Builder moveField(DexField from, DexField to) {
- fieldMap.put(from, to);
- fieldMap.putInverse(from, to);
+ Builder recordNewFieldSignature(DexField oldFieldSignature, DexField newFieldSignature) {
+ Set<DexField> originalFieldSignatures = fieldMap.removeValue(oldFieldSignature);
+ if (originalFieldSignatures.isEmpty()) {
+ fieldMap.put(oldFieldSignature, newFieldSignature);
+ } else if (originalFieldSignatures.size() == 1) {
+ fieldMap.put(originalFieldSignatures.iterator().next(), newFieldSignature);
+ } else {
+ for (DexField originalFieldSignature : originalFieldSignatures) {
+ fieldMap.put(originalFieldSignature, newFieldSignature);
+ }
+ DexField representative = fieldMap.removeRepresentativeFor(oldFieldSignature);
+ assert representative != null;
+ fieldMap.setRepresentative(newFieldSignature, representative);
+ }
return this;
}
- public Builder setRepresentativeField(DexField from, DexField to) {
- fieldMap.setRepresentative(from, to);
+ Builder recordNewFieldSignature(
+ Iterable<DexField> oldFieldSignatures,
+ DexField newFieldSignature,
+ DexField representative) {
+ assert Streams.stream(oldFieldSignatures).noneMatch(fieldMap::containsValue);
+ assert Iterables.contains(oldFieldSignatures, representative);
+ for (DexField oldFieldSignature : oldFieldSignatures) {
+ fieldMap.put(oldFieldSignature, newFieldSignature);
+ }
+ fieldMap.setRepresentative(newFieldSignature, representative);
return this;
}
/** Unidirectional mapping from one method to another. */
public Builder recordExtraOriginalSignature(DexMethod from, DexMethod to) {
methodMap.setRepresentative(from, to);
-
return this;
}
/** Unidirectional mapping from one method to another. */
public Builder mapMethod(DexMethod from, DexMethod to) {
methodMap.put(from, to);
-
return this;
}
/** Unidirectional mapping from one method to another. */
public Builder mapMethodInverse(DexMethod from, DexMethod to) {
methodMap.putInverse(from, to);
-
return this;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
index 4d6d93f..ff108a4 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
@@ -8,7 +8,10 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.classmerging.MergedClasses;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
+import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
@@ -22,12 +25,12 @@
}
public static HorizontallyMergedClasses empty() {
- return new HorizontallyMergedClasses(new BidirectionalManyToOneMap<>());
+ return new HorizontallyMergedClasses(new EmptyBidirectionalOneToOneMap<>());
}
@Override
public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) {
- mergedClasses.forEach(consumer);
+ mergedClasses.forEachManyToOneMapping(consumer);
}
public DexType getMergeTargetOrDefault(DexType type) {
@@ -44,11 +47,11 @@
@Override
public boolean hasBeenMergedIntoDifferentType(DexType type) {
- return mergedClasses.hasKey(type);
+ return mergedClasses.containsKey(type);
}
public boolean isMergeTarget(DexType type) {
- return mergedClasses.hasValue(type);
+ return mergedClasses.containsValue(type);
}
public boolean hasBeenMergedOrIsMergeTarget(DexType type) {
@@ -71,8 +74,8 @@
}
public static class Builder {
- private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses =
- new BidirectionalManyToOneMap<>();
+ private final MutableBidirectionalManyToOneMap<DexType, DexType> mergedClasses =
+ new BidirectionalManyToOneHashMap<>();
public HorizontallyMergedClasses build() {
return new HorizontallyMergedClasses(mergedClasses);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneInverseMap.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneInverseMap.java
index 973162f..8cf8b2a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneInverseMap.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneInverseMap.java
@@ -4,20 +4,20 @@
package com.android.tools.r8.horizontalclassmerging;
-import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
import java.util.Map;
/** The inverse of a {@link ManyToOneMap} used for generating graph lens maps. */
public class ManyToOneInverseMap<K, V> {
- private final BidirectionalOneToOneHashMap<V, K> biMap;
+ private final BidirectionalOneToOneMap<V, K> biMap;
private final Map<V, K> extraMap;
- ManyToOneInverseMap(BidirectionalOneToOneHashMap<V, K> biMap, Map<V, K> extraMap) {
+ ManyToOneInverseMap(BidirectionalOneToOneMap<V, K> biMap, Map<V, K> extraMap) {
this.biMap = biMap;
this.extraMap = extraMap;
}
- public BidirectionalOneToOneHashMap<V, K> getBiMap() {
+ public BidirectionalOneToOneMap<V, K> getBiMap() {
return biMap;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneMap.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneMap.java
index 5204021..f51dfd2 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneMap.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneMap.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.horizontalclassmerging;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import java.util.HashMap;
@@ -50,7 +51,7 @@
}
public ManyToOneInverseMap<K, V> inverse(Function<Set<K>, K> pickRepresentative) {
- BidirectionalOneToOneHashMap<V, K> biMap = new BidirectionalOneToOneHashMap<>();
+ MutableBidirectionalOneToOneMap<V, K> biMap = new BidirectionalOneToOneHashMap<>();
Map<V, K> extraMap = new HashMap<>();
for (Entry<V, Set<K>> entry : inverseMap.entrySet()) {
K representative = representativeMap.get(entry.getKey());
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index da61076..045a8b1 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -46,7 +46,6 @@
private final AppView<AppInfoWithLiveness> appView;
private final DexItemFactory dexItemFactory;
private final BiMap<DexMethod, DexMethod> movedMethods = HashBiMap.create();
- private final BiMap<DexField, DexField> movedFields = HashBiMap.create();
private final SyntheticArgumentClass syntheticArgumentClass;
private final BiMap<DexMethodSignature, DexMethodSignature> reservedInterfaceSignatures =
HashBiMap.create();
@@ -130,7 +129,6 @@
}
lensBuilder.remapMethods(movedMethods);
- lensBuilder.remapFields(movedFields);
HorizontalClassMergerGraphLens lens = lensBuilder.build(appView, mergedClasses);
fieldAccessChangesBuilder.build(this::fixupMethodReference).modify(appView);
@@ -365,26 +363,26 @@
Set<DexField> existingFields = Sets.newIdentityHashSet();
for (int i = 0; i < fields.size(); i++) {
- DexEncodedField encodedField = fields.get(i);
- DexField field = encodedField.field;
- DexField newField = fixupFieldReference(field);
+ DexEncodedField oldField = fields.get(i);
+ DexField oldFieldReference = oldField.getReference();
+ DexField newFieldReference = fixupFieldReference(oldFieldReference);
// Rename the field if it already exists.
- if (!existingFields.add(newField)) {
- DexField template = newField;
- newField =
+ if (!existingFields.add(newFieldReference)) {
+ DexField template = newFieldReference;
+ newFieldReference =
dexItemFactory.createFreshMember(
tryName ->
Optional.of(template.withName(tryName, dexItemFactory))
.filter(tryMethod -> !existingFields.contains(tryMethod)),
- newField.name.toSourceString());
- boolean added = existingFields.add(newField);
+ newFieldReference.name.toSourceString());
+ boolean added = existingFields.add(newFieldReference);
assert added;
}
- if (newField != encodedField.field) {
- movedFields.put(field, newField);
- setter.setField(i, encodedField.toTypeSubstitutedField(newField));
+ if (newFieldReference != oldFieldReference) {
+ lensBuilder.recordNewFieldSignature(oldFieldReference, newFieldReference);
+ setter.setField(i, oldField.toTypeSubstitutedField(newFieldReference));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index abe232f..d73a7c1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -49,8 +49,10 @@
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
-import com.google.common.collect.BiMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
import com.google.common.collect.ImmutableList;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -521,17 +523,15 @@
public InterfaceProcessorNestedGraphLens(
Map<DexType, DexType> typeMap,
Map<DexMethod, DexMethod> methodMap,
- Map<DexField, DexField> fieldMap,
- BiMap<DexField, DexField> originalFieldSignatures,
- BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures,
- BidirectionalOneToOneHashMap<DexMethod, DexMethod> extraOriginalMethodSignatures,
+ BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap,
+ BidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures,
+ BidirectionalOneToOneMap<DexMethod, DexMethod> extraOriginalMethodSignatures,
GraphLens previousLens,
DexItemFactory dexItemFactory) {
super(
typeMap,
methodMap,
fieldMap,
- originalFieldSignatures,
originalMethodSignatures,
previousLens,
dexItemFactory);
@@ -601,7 +601,7 @@
public static class Builder extends NestedGraphLens.Builder {
- private final BidirectionalOneToOneHashMap<DexMethod, DexMethod>
+ private final MutableBidirectionalOneToOneMap<DexMethod, DexMethod>
extraOriginalMethodSignatures = new BidirectionalOneToOneHashMap<>();
public void recordCodeMovedToCompanionClass(DexMethod from, DexMethod to) {
@@ -613,7 +613,7 @@
@Override
public InterfaceProcessorNestedGraphLens build(
DexItemFactory dexItemFactory, GraphLens previousLens) {
- if (originalFieldSignatures.isEmpty()
+ if (fieldMap.isEmpty()
&& originalMethodSignatures.isEmpty()
&& extraOriginalMethodSignatures.isEmpty()) {
return null;
@@ -622,7 +622,6 @@
typeMap,
methodMap,
fieldMap,
- originalFieldSignatures,
originalMethodSignatures,
extraOriginalMethodSignatures,
previousLens,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index e4d6f65..fd4eede 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -42,10 +42,10 @@
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.google.common.base.Suppliers;
-import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.ArrayList;
@@ -439,16 +439,14 @@
LambdaRewriterLens(
Map<DexType, DexType> typeMap,
Map<DexMethod, DexMethod> methodMap,
- Map<DexField, DexField> fieldMap,
- BiMap<DexField, DexField> originalFieldSignatures,
- BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures,
+ BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap,
+ BidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures,
GraphLens previousLens,
DexItemFactory dexItemFactory) {
super(
typeMap,
methodMap,
fieldMap,
- originalFieldSignatures,
originalMethodSignatures,
previousLens,
dexItemFactory);
@@ -479,7 +477,6 @@
typeMap,
methodMap,
fieldMap,
- originalFieldSignatures,
originalMethodSignatures,
previousLens,
dexItemFactory);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
index 8889ec2..12418b3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
+import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap;
import com.google.common.collect.ImmutableMap;
import java.util.IdentityHashMap;
import java.util.Map;
@@ -34,9 +34,8 @@
super(
ImmutableMap.of(),
methodMap,
- ImmutableMap.of(),
- null,
- BidirectionalManyToManyRepresentativeMap.empty(),
+ new EmptyBidirectionalOneToOneMap<>(),
+ new EmptyBidirectionalOneToOneMap<>(),
previousLens,
appView.dexItemFactory());
// No concurrent maps here, we do not want synchronization overhead.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index 48002cc..c681eaa 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -29,6 +29,9 @@
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableMap;
@@ -54,14 +57,13 @@
private final Map<DexMethod, ArgumentInfoCollection> removedArgumentsInfoPerMethod;
UninstantiatedTypeOptimizationGraphLens(
- BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMap,
+ BidirectionalOneToOneMap<DexMethod, DexMethod> methodMap,
Map<DexMethod, ArgumentInfoCollection> removedArgumentsInfoPerMethod,
AppView<?> appView) {
super(
ImmutableMap.of(),
- methodMap,
- ImmutableMap.of(),
- null,
+ methodMap.getForwardMap(),
+ new EmptyBidirectionalOneToOneMap<>(),
methodMap.getInverseOneToOneMap(),
appView.graphLens(),
appView.dexItemFactory());
@@ -129,7 +131,7 @@
}
Map<Wrapper<DexMethod>, Set<DexType>> changedVirtualMethods = new HashMap<>();
- BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping =
+ MutableBidirectionalOneToOneMap<DexMethod, DexMethod> methodMapping =
new BidirectionalOneToOneHashMap<>();
Map<DexMethod, ArgumentInfoCollection> removedArgumentsInfoPerMethod = new IdentityHashMap<>();
@@ -140,7 +142,7 @@
processClass(
clazz,
changedVirtualMethods,
- methodMapping.getForwardBacking(),
+ methodMapping.getForwardMap(),
methodPoolCollection,
removedArgumentsInfoPerMethod));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
index 16fb558..ec1b904 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ArgumentUse;
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;
@@ -27,9 +26,10 @@
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Streams;
import java.util.BitSet;
@@ -48,7 +48,7 @@
private final AppView<AppInfoWithLiveness> appView;
private final MethodPoolCollection methodPoolCollection;
- private final BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping =
+ private final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> methodMapping =
new BidirectionalOneToOneHashMap<>();
private final Map<DexMethod, ArgumentInfoCollection> removedArguments = new IdentityHashMap<>();
@@ -57,19 +57,15 @@
private final Map<DexMethod, ArgumentInfoCollection> removedArguments;
UnusedArgumentsGraphLens(
- Map<DexType, DexType> typeMap,
Map<DexMethod, DexMethod> methodMap,
- Map<DexField, DexField> fieldMap,
- BiMap<DexField, DexField> originalFieldSignatures,
- BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures,
+ BidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures,
GraphLens previousLens,
DexItemFactory dexItemFactory,
Map<DexMethod, ArgumentInfoCollection> removedArguments) {
super(
- typeMap,
+ ImmutableMap.of(),
methodMap,
- fieldMap,
- originalFieldSignatures,
+ new EmptyBidirectionalOneToOneMap<>(),
originalMethodSignatures,
previousLens,
dexItemFactory);
@@ -108,10 +104,7 @@
if (!methodMapping.isEmpty()) {
return new UnusedArgumentsGraphLens(
- ImmutableMap.of(),
- methodMapping,
- ImmutableMap.of(),
- ImmutableBiMap.of(),
+ methodMapping.getForwardMap(),
methodMapping.getInverseOneToOneMap(),
appView.graphLens(),
appView.dexItemFactory(),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index b6660fc..f230dda 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -13,8 +13,8 @@
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.IdentityHashMap;
@@ -29,9 +29,8 @@
EnumUnboxingLens(
Map<DexType, DexType> typeMap,
Map<DexMethod, DexMethod> methodMap,
- Map<DexField, DexField> fieldMap,
- BiMap<DexField, DexField> originalFieldSignatures,
- BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures,
+ BidirectionalOneToOneMap<DexField, DexField> fieldMap,
+ BidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures,
GraphLens previousLens,
DexItemFactory dexItemFactory,
Map<DexMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod,
@@ -40,7 +39,6 @@
typeMap,
methodMap,
fieldMap,
- originalFieldSignatures,
originalMethodSignatures,
previousLens,
dexItemFactory);
@@ -76,8 +74,9 @@
static class Builder {
protected final Map<DexType, DexType> typeMap = new IdentityHashMap<>();
- protected final BiMap<DexField, DexField> originalFieldSignatures = HashBiMap.create();
- protected final BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures =
+ protected final MutableBidirectionalOneToOneMap<DexField, DexField> newFieldSignatures =
+ new BidirectionalOneToOneHashMap<>();
+ protected final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures =
new BidirectionalOneToOneHashMap<>();
private Map<DexMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod =
@@ -94,7 +93,7 @@
if (from == to) {
return;
}
- originalFieldSignatures.put(to, from);
+ newFieldSignatures.put(from, to);
}
public void move(DexMethod from, DexMethod to, boolean fromStatic, boolean toStatic) {
@@ -143,16 +142,13 @@
public EnumUnboxingLens build(
DexItemFactory dexItemFactory, GraphLens previousLens, Set<DexType> unboxedEnums) {
- if (typeMap.isEmpty()
- && originalFieldSignatures.isEmpty()
- && originalMethodSignatures.isEmpty()) {
+ if (typeMap.isEmpty() && newFieldSignatures.isEmpty() && originalMethodSignatures.isEmpty()) {
return null;
}
return new EnumUnboxingLens(
typeMap,
- originalMethodSignatures.getInverseOneToOneMap(),
- originalFieldSignatures.inverse(),
- originalFieldSignatures,
+ originalMethodSignatures.getInverseOneToOneMap().getForwardMap(),
+ newFieldSignatures,
originalMethodSignatures,
previousLens,
dexItemFactory,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLens.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLens.java
index fbc4c12..75a78ab 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLens.java
@@ -9,21 +9,19 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
-import com.google.common.collect.BiMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
import com.google.common.collect.ImmutableMap;
class ClassStaticizerGraphLens extends NestedGraphLens {
ClassStaticizerGraphLens(
AppView<?> appView,
- BiMap<DexField, DexField> fieldMapping,
- BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping) {
+ BidirectionalOneToOneMap<DexField, DexField> fieldMapping,
+ BidirectionalOneToOneMap<DexMethod, DexMethod> methodMapping) {
super(
ImmutableMap.of(),
- methodMapping,
+ methodMapping.getForwardMap(),
fieldMapping,
- fieldMapping.inverse(),
methodMapping.getInverseOneToOneMap(),
appView.graphLens(),
appView.dexItemFactory());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index 2313a5a..9d84c1f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -47,10 +47,9 @@
import com.android.tools.r8.utils.TraversalContinuation;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder;
+import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
@@ -738,9 +737,10 @@
}
private ProgramMethodSet staticizeMethodSymbols() {
- BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping =
+ MutableBidirectionalOneToOneMap<DexMethod, DexMethod> methodMapping =
new BidirectionalOneToOneHashMap<>();
- BiMap<DexField, DexField> fieldMapping = HashBiMap.create();
+ MutableBidirectionalOneToOneMap<DexField, DexField> fieldMapping =
+ new BidirectionalOneToOneHashMap<>();
ProgramMethodSet staticizedMethods = ProgramMethodSet.create();
for (CandidateInfo candidate : classStaticizer.candidates.values()) {
@@ -803,8 +803,8 @@
DexProgramClass candidateClass,
DexType hostType,
DexProgramClass hostClass,
- BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping,
- BiMap<DexField, DexField> fieldMapping) {
+ MutableBidirectionalOneToOneMap<DexMethod, DexMethod> methodMapping,
+ MutableBidirectionalOneToOneMap<DexField, DexField> fieldMapping) {
candidateToHostMapping.put(candidateClass.type, hostType);
// Process static fields.
diff --git a/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java b/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
index bd8db04..586e257 100644
--- a/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
@@ -10,23 +10,23 @@
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
+import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import java.util.Set;
final class PublicizerLens extends NestedGraphLens {
- private final AppView appView;
+ private final AppView<?> appView;
private final Set<DexMethod> publicizedMethods;
- private PublicizerLens(AppView appView, Set<DexMethod> publicizedMethods) {
+ private PublicizerLens(AppView<?> appView, Set<DexMethod> publicizedMethods) {
super(
ImmutableMap.of(),
ImmutableMap.of(),
- ImmutableMap.of(),
- null,
- BidirectionalManyToManyRepresentativeMap.empty(),
+ new BidirectionalManyToOneRepresentativeHashMap<>(),
+ new EmptyBidirectionalOneToOneMap<>(),
appView.graphLens(),
appView.dexItemFactory());
this.appView = appView;
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingResult.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingResult.java
index 09a9b27..9353111 100644
--- a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingResult.java
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingResult.java
@@ -11,7 +11,8 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap;
import java.util.Set;
import java.util.function.BiConsumer;
@@ -20,8 +21,8 @@
private final AppView<AppInfoWithLiveness> appView;
// Mapping from non-hoisted bridge methods to hoisted bridge methods.
- private final BidirectionalManyToOneMap<DexMethod, DexMethod> bridgeToHoistedBridgeMap =
- new BidirectionalManyToOneMap<>();
+ private final MutableBidirectionalManyToOneMap<DexMethod, DexMethod> bridgeToHoistedBridgeMap =
+ new BidirectionalManyToOneHashMap<>();
// Mapping from non-hoisted bridge methods to the set of contexts in which they are accessed.
private final MethodAccessInfoCollection.IdentityBuilder bridgeMethodAccessInfoCollectionBuilder =
@@ -32,7 +33,7 @@
}
public void forEachHoistedBridge(BiConsumer<ProgramMethod, BridgeInfo> consumer) {
- bridgeToHoistedBridgeMap.forEach(
+ bridgeToHoistedBridgeMap.forEachManyToOneMapping(
(bridges, hoistedBridge) -> {
DexProgramClass clazz = appView.definitionForProgramType(hoistedBridge.getHolderType());
ProgramMethod method = hoistedBridge.lookupOnProgramClass(clazz);
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java
index b871193..0fe63b8 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java
@@ -11,6 +11,8 @@
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
@@ -20,14 +22,13 @@
private RepackagingLens(
AppView<AppInfoWithLiveness> appView,
- BiMap<DexField, DexField> originalFieldSignatures,
- BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures,
+ BidirectionalOneToOneMap<DexField, DexField> newFieldSignatures,
+ BidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures,
BiMap<DexType, DexType> originalTypes) {
super(
originalTypes.inverse(),
- originalMethodSignatures.getInverseBacking(),
- originalFieldSignatures.inverse(),
- originalFieldSignatures,
+ originalMethodSignatures.getInverseOneToOneMap().getForwardMap(),
+ newFieldSignatures,
originalMethodSignatures,
appView.graphLens(),
appView.dexItemFactory());
@@ -48,12 +49,13 @@
public static class Builder {
protected final BiMap<DexType, DexType> originalTypes = HashBiMap.create();
- protected final BiMap<DexField, DexField> originalFieldSignatures = HashBiMap.create();
- protected final BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures =
+ protected final MutableBidirectionalOneToOneMap<DexField, DexField> newFieldSignatures =
+ new BidirectionalOneToOneHashMap<>();
+ protected final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures =
new BidirectionalOneToOneHashMap<>();
public void recordMove(DexField from, DexField to) {
- originalFieldSignatures.put(to, from);
+ newFieldSignatures.put(from, to);
}
public void recordMove(DexMethod from, DexMethod to) {
@@ -67,7 +69,7 @@
public RepackagingLens build(AppView<AppInfoWithLiveness> appView) {
assert !originalTypes.isEmpty();
return new RepackagingLens(
- appView, originalFieldSignatures, originalMethodSignatures, originalTypes);
+ appView, newFieldSignatures, originalMethodSignatures, originalTypes);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
index 58e85ff..33e64f0 100644
--- a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
@@ -24,10 +24,10 @@
import com.android.tools.r8.utils.SingletonEquivalence;
import com.android.tools.r8.utils.TraversalContinuation;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
import com.google.common.base.Equivalence;
import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multiset.Entry;
@@ -199,8 +199,9 @@
private final Map<MergeKey, Representative> representatives = new HashMap<>();
- private final BiMap<DexField, DexField> fieldMapping = HashBiMap.create();
- private final BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping =
+ private final MutableBidirectionalOneToOneMap<DexField, DexField> newFieldSignatures =
+ new BidirectionalOneToOneHashMap<>();
+ private final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> methodMapping =
new BidirectionalOneToOneHashMap<>();
private int numberOfMergedClasses = 0;
@@ -222,27 +223,18 @@
public NestedGraphLens run() {
appView.appInfo().classesWithDeterministicOrder().forEach(this::merge);
- if (Log.ENABLED) {
- Log.info(
- getClass(),
- "Merged %s classes with %s members.",
- numberOfMergedClasses,
- fieldMapping.size() + methodMapping.size());
- }
appView.setStaticallyMergedClasses(mergedClassesBuilder.build());
return buildGraphLens();
}
private NestedGraphLens buildGraphLens() {
- if (!fieldMapping.isEmpty() || !methodMapping.isEmpty()) {
- BiMap<DexField, DexField> originalFieldSignatures = fieldMapping.inverse();
- BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures =
+ if (!newFieldSignatures.isEmpty() || !methodMapping.isEmpty()) {
+ BidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures =
methodMapping.getInverseOneToOneMap();
return new NestedGraphLens(
ImmutableMap.of(),
- methodMapping,
- fieldMapping,
- originalFieldSignatures,
+ methodMapping.getForwardMap(),
+ newFieldSignatures,
originalMethodSignatures,
appView.graphLens(),
appView.dexItemFactory());
@@ -501,7 +493,7 @@
DexMethod originalMethod =
methodMapping.getRepresentativeKeyOrDefault(sourceMethod.method, sourceMethod.method);
- methodMapping.forcePut(originalMethod, sourceMethodAfterMove.method);
+ methodMapping.put(originalMethod, sourceMethodAfterMove.method);
existingMethods.add(equivalence.wrap(sourceMethodAfterMove.method));
}
@@ -536,8 +528,8 @@
result[index++] = sourceFieldAfterMove;
DexField originalField =
- fieldMapping.inverse().getOrDefault(sourceField.field, sourceField.field);
- fieldMapping.forcePut(originalField, sourceFieldAfterMove.field);
+ newFieldSignatures.getRepresentativeKeyOrDefault(sourceField.field, sourceField.field);
+ newFieldSignatures.put(originalField, sourceFieldAfterMove.field);
existingFields.add(equivalence.wrap(sourceFieldAfterMove.field));
}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 434b5d2..e5a4d6b 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -58,7 +58,8 @@
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.TraversalContinuation;
-import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap;
import com.google.common.base.Equivalence;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.Iterables;
@@ -206,8 +207,8 @@
private final Set<DexProgramClass> mergeCandidates = new LinkedHashSet<>();
// Map from source class to target class.
- private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses =
- new BidirectionalManyToOneMap<>();
+ private final MutableBidirectionalManyToOneMap<DexType, DexType> mergedClasses =
+ new BidirectionalManyToOneHashMap<>();
// Set of types that must not be merged into their subtype.
private final Set<DexType> pinnedTypes = Sets.newIdentityHashSet();
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
index f634f48..008380c 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
@@ -17,9 +17,10 @@
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.utils.IterableUtils;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import java.util.Collection;
@@ -69,20 +70,18 @@
private VerticalClassMergerGraphLens(
AppView<?> appView,
VerticallyMergedClasses mergedClasses,
- Map<DexField, DexField> fieldMap,
+ BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap,
Map<DexMethod, DexMethod> methodMap,
Set<DexMethod> mergedMethods,
Map<DexType, Map<DexMethod, GraphLensLookupResultProvider>>
contextualVirtualToDirectMethodMaps,
- BiMap<DexField, DexField> originalFieldSignatures,
- BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures,
+ BidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures,
Map<DexMethod, DexMethod> originalMethodSignaturesForBridges,
GraphLens previousLens) {
super(
mergedClasses.getForwardMap(),
methodMap,
fieldMap,
- originalFieldSignatures,
originalMethodSignatures,
previousLens,
appView.dexItemFactory());
@@ -166,13 +165,14 @@
private final DexItemFactory dexItemFactory;
- protected final BiMap<DexField, DexField> fieldMap = HashBiMap.create();
+ protected final MutableBidirectionalOneToOneMap<DexField, DexField> fieldMap =
+ new BidirectionalOneToOneHashMap<>();
protected final Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
private final ImmutableSet.Builder<DexMethod> mergedMethodsBuilder = ImmutableSet.builder();
private final Map<DexType, Map<DexMethod, GraphLensLookupResultProvider>>
contextualVirtualToDirectMethodMaps = new IdentityHashMap<>();
- private final BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures =
+ private final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures =
new BidirectionalOneToOneHashMap<>();
private final Map<DexMethod, DexMethod> originalMethodSignaturesForBridges =
new IdentityHashMap<>();
@@ -185,11 +185,10 @@
static Builder createBuilderForFixup(Builder builder, VerticallyMergedClasses mergedClasses) {
Builder newBuilder = new Builder(builder.dexItemFactory);
- for (Map.Entry<DexField, DexField> entry : builder.fieldMap.entrySet()) {
- newBuilder.map(
- entry.getKey(),
- builder.getFieldSignatureAfterClassMerging(entry.getValue(), mergedClasses));
- }
+ builder.fieldMap.forEach(
+ (key, value) ->
+ newBuilder.map(
+ key, builder.getFieldSignatureAfterClassMerging(value, mergedClasses)));
for (Map.Entry<DexMethod, DexMethod> entry : builder.methodMap.entrySet()) {
newBuilder.map(
entry.getKey(),
@@ -237,7 +236,6 @@
if (mergedClasses.isEmpty()) {
return null;
}
- BiMap<DexField, DexField> originalFieldSignatures = fieldMap.inverse();
// Build new graph lens.
return new VerticalClassMergerGraphLens(
appView,
@@ -246,7 +244,6 @@
methodMap,
mergedMethodsBuilder.build(),
contextualVirtualToDirectMethodMaps,
- originalFieldSignatures,
originalMethodSignatures,
originalMethodSignaturesForBridges,
appView.graphLens());
@@ -308,7 +305,7 @@
}
public boolean hasOriginalSignatureMappingFor(DexField field) {
- return fieldMap.inverse().containsKey(field);
+ return fieldMap.containsValue(field);
}
public boolean hasOriginalSignatureMappingFor(DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/utils/IterableUtils.java b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
index aee34f6..871a7a5 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -15,6 +15,10 @@
public class IterableUtils {
+ public static <T> Iterable<T> append(Iterable<T> iterable, T element) {
+ return Iterables.concat(iterable, singleton(element));
+ }
+
public static <T> List<T> ensureUnmodifiableList(Iterable<T> iterable) {
List<T> list;
if (iterable instanceof List<?>) {
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyMap.java
new file mode 100644
index 0000000..09b3c27
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyMap.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.collections;
+
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+/** Interface that accommodates many-to-many mappings. */
+public interface BidirectionalManyToManyMap<K, V> {
+
+ boolean containsKey(K key);
+
+ boolean containsValue(V value);
+
+ void forEach(BiConsumer<? super K, ? super V> consumer);
+
+ void forEachKey(Consumer<? super K> consumer);
+
+ Set<K> getKeys(V value);
+
+ Set<V> getValues(K key);
+
+ boolean isEmpty();
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyRepresentativeMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyRepresentativeMap.java
index a4878dc..773c066 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyRepresentativeMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyRepresentativeMap.java
@@ -4,91 +4,27 @@
package com.android.tools.r8.utils.collections;
-import java.util.Map;
+/**
+ * Interface that accommodates many-to-many mappings.
+ *
+ * <p>This interface additionally adds a "representative" for each one-to-many/many-to-one mapping.
+ * The representative for a given key is a value from {@link #getValues(K)}. The representative for
+ * a given value is a key from {@link #getKeys(V)}.
+ */
+public interface BidirectionalManyToManyRepresentativeMap<K, V>
+ extends BidirectionalManyToManyMap<K, V> {
-public abstract class BidirectionalManyToManyRepresentativeMap<K, V> {
+ K getRepresentativeKey(V value);
- public static <K, V> BidirectionalManyToManyRepresentativeMap<K, V> empty() {
- return new EmptyBidirectionalManyToManyRepresentativeMap<>();
- }
-
- public abstract boolean containsKey(K key);
-
- public abstract boolean containsValue(V value);
-
- public abstract Map<K, V> getForwardBacking();
-
- public abstract Map<V, K> getInverseBacking();
-
- public final Inverse getInverseManyToManyMap() {
- return new Inverse();
- }
-
- public abstract K getRepresentativeKey(V value);
-
- public final K getRepresentativeKeyOrDefault(V value, K defaultValue) {
+ default K getRepresentativeKeyOrDefault(V value, K defaultValue) {
K representativeKey = getRepresentativeKey(value);
return representativeKey != null ? representativeKey : defaultValue;
}
- public abstract V getRepresentativeValue(K key);
+ V getRepresentativeValue(K key);
- public final V getRepresentativeValueOrDefault(K key, V defaultValue) {
+ default V getRepresentativeValueOrDefault(K key, V defaultValue) {
V representativeValue = getRepresentativeValue(key);
return representativeValue != null ? representativeValue : defaultValue;
}
-
- public abstract Iterable<K> getKeys(V value);
-
- public abstract Iterable<V> getValues(K key);
-
- public abstract boolean isEmpty();
-
- public class Inverse extends BidirectionalManyToManyRepresentativeMap<V, K> {
-
- @Override
- public boolean containsKey(V key) {
- return BidirectionalManyToManyRepresentativeMap.this.containsValue(key);
- }
-
- @Override
- public boolean containsValue(K value) {
- return BidirectionalManyToManyRepresentativeMap.this.containsKey(value);
- }
-
- @Override
- public Map<V, K> getForwardBacking() {
- return BidirectionalManyToManyRepresentativeMap.this.getInverseBacking();
- }
-
- @Override
- public Map<K, V> getInverseBacking() {
- return BidirectionalManyToManyRepresentativeMap.this.getForwardBacking();
- }
-
- @Override
- public V getRepresentativeKey(K value) {
- return BidirectionalManyToManyRepresentativeMap.this.getRepresentativeValue(value);
- }
-
- @Override
- public K getRepresentativeValue(V key) {
- return BidirectionalManyToManyRepresentativeMap.this.getRepresentativeKey(key);
- }
-
- @Override
- public Iterable<V> getKeys(K value) {
- return BidirectionalManyToManyRepresentativeMap.this.getValues(value);
- }
-
- @Override
- public Iterable<K> getValues(V key) {
- return BidirectionalManyToManyRepresentativeMap.this.getKeys(key);
- }
-
- @Override
- public boolean isEmpty() {
- return BidirectionalManyToManyRepresentativeMap.this.isEmpty();
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneHashMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneHashMap.java
new file mode 100644
index 0000000..6d50046
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneHashMap.java
@@ -0,0 +1,126 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.collections;
+
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+public class BidirectionalManyToOneHashMap<K, V> implements MutableBidirectionalManyToOneMap<K, V> {
+
+ private final Map<K, V> backing;
+ private final Map<V, Set<K>> inverse;
+
+ public BidirectionalManyToOneHashMap() {
+ this(new IdentityHashMap<>(), new IdentityHashMap<>());
+ }
+
+ private BidirectionalManyToOneHashMap(Map<K, V> backing, Map<V, Set<K>> inverse) {
+ this.backing = backing;
+ this.inverse = inverse;
+ }
+
+ @Override
+ public boolean containsKey(K key) {
+ return backing.containsKey(key);
+ }
+
+ @Override
+ public boolean containsValue(V value) {
+ return inverse.containsKey(value);
+ }
+
+ @Override
+ public void forEach(BiConsumer<? super K, ? super V> consumer) {
+ backing.forEach(consumer);
+ }
+
+ @Override
+ public void forEachKey(Consumer<? super K> consumer) {
+ backing.keySet().forEach(consumer);
+ }
+
+ @Override
+ public void forEachManyToOneMapping(BiConsumer<? super Set<K>, V> consumer) {
+ inverse.forEach((value, keys) -> consumer.accept(keys, value));
+ }
+
+ @Override
+ public V get(Object key) {
+ return backing.get(key);
+ }
+
+ @Override
+ public V getOrDefault(Object key, V value) {
+ return backing.getOrDefault(key, value);
+ }
+
+ @Override
+ public Map<K, V> getForwardMap() {
+ return backing;
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return backing.keySet();
+ }
+
+ @Override
+ public Set<K> getKeys(V value) {
+ return inverse.getOrDefault(value, Collections.emptySet());
+ }
+
+ @Override
+ public Set<V> getValues(K key) {
+ V value = get(key);
+ return value != null ? Collections.singleton(value) : Collections.emptySet();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return backing.isEmpty();
+ }
+
+ @Override
+ public V remove(K key) {
+ V value = backing.remove(key);
+ if (value != null) {
+ Set<K> keys = inverse.get(value);
+ keys.remove(key);
+ if (keys.isEmpty()) {
+ inverse.remove(value);
+ }
+ }
+ return value;
+ }
+
+ @Override
+ public Set<K> removeValue(V value) {
+ Set<K> keys = inverse.remove(value);
+ if (keys == null) {
+ return Collections.emptySet();
+ }
+ for (K key : keys) {
+ V removedValue = backing.remove(key);
+ assert removedValue == value;
+ }
+ return keys;
+ }
+
+ @Override
+ public void put(K key, V value) {
+ remove(key);
+ backing.put(key, value);
+ inverse.computeIfAbsent(value, ignore -> new LinkedHashSet<>()).add(key);
+ }
+
+ public Set<V> values() {
+ return inverse.keySet();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
index 361b761..3ec2c15 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
@@ -4,109 +4,25 @@
package com.android.tools.r8.utils.collections;
-import java.util.Collections;
-import java.util.IdentityHashMap;
-import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
-public class BidirectionalManyToOneMap<K, V> {
+/**
+ * Interface that accommodates many-to-one mappings.
+ *
+ * <p>This interface inherits from {@link BidirectionalManyToManyMap} to allow implementing
+ * many-to-many mappings using many-to-one mappings.
+ */
+public interface BidirectionalManyToOneMap<K, V> extends BidirectionalManyToManyMap<K, V> {
- private final Map<K, V> backing;
- private final Map<V, Set<K>> inverse;
+ void forEachManyToOneMapping(BiConsumer<? super Set<K>, V> consumer);
- public BidirectionalManyToOneMap() {
- this(new IdentityHashMap<>(), new IdentityHashMap<>());
- }
+ V get(Object key);
- private BidirectionalManyToOneMap(Map<K, V> backing, Map<V, Set<K>> inverse) {
- this.backing = backing;
- this.inverse = inverse;
- }
+ V getOrDefault(Object key, V defaultValue);
- public static <K, V> BidirectionalManyToOneMap<K, V> empty() {
- return new BidirectionalManyToOneMap<>(Collections.emptyMap(), Collections.emptyMap());
- }
+ Map<K, V> getForwardMap();
- public boolean containsKey(K key) {
- return backing.containsKey(key);
- }
-
- public boolean containsValue(V value) {
- return inverse.containsKey(value);
- }
-
- public void forEach(BiConsumer<Set<K>, V> consumer) {
- inverse.forEach((value, keys) -> consumer.accept(keys, value));
- }
-
- public V get(K key) {
- return backing.get(key);
- }
-
- public V getOrDefault(K key, V value) {
- return backing.getOrDefault(key, value);
- }
-
- public Map<K, V> getForwardMap() {
- return backing;
- }
-
- public Set<K> keySet() {
- return backing.keySet();
- }
-
- public boolean hasKey(K key) {
- return backing.containsKey(key);
- }
-
- public boolean hasValue(V value) {
- return inverse.containsKey(value);
- }
-
- public Set<K> getKeys(V value) {
- return inverse.getOrDefault(value, Collections.emptySet());
- }
-
- public Set<K> getKeysOrNull(V value) {
- return inverse.get(value);
- }
-
- public boolean isEmpty() {
- return backing.isEmpty();
- }
-
- public void remove(K key) {
- V value = backing.remove(key);
- if (value != null) {
- Set<K> keys = inverse.get(value);
- keys.remove(key);
- if (keys.isEmpty()) {
- inverse.remove(value);
- }
- }
- }
-
- public Set<K> removeValue(V value) {
- Set<K> keys = inverse.remove(value);
- if (keys == null) {
- return Collections.emptySet();
- }
- for (K key : keys) {
- V removedValue = backing.remove(key);
- assert removedValue == value;
- }
- return keys;
- }
-
- public void put(K key, V value) {
- remove(key);
- backing.put(key, value);
- inverse.computeIfAbsent(value, ignore -> new LinkedHashSet<>()).add(key);
- }
-
- public Set<V> values() {
- return inverse.keySet();
- }
+ Set<K> keySet();
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneRepresentativeHashMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneRepresentativeHashMap.java
new file mode 100644
index 0000000..8126051
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneRepresentativeHashMap.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.collections;
+
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class BidirectionalManyToOneRepresentativeHashMap<K, V>
+ extends BidirectionalManyToOneHashMap<K, V>
+ implements MutableBidirectionalManyToOneRepresentativeMap<K, V> {
+
+ private final Map<V, K> representatives = new IdentityHashMap<>();
+
+ @Override
+ public K removeRepresentativeFor(V value) {
+ return representatives.remove(value);
+ }
+
+ @Override
+ public void setRepresentative(V value, K representative) {
+ representatives.put(value, representative);
+ }
+
+ @Override
+ public K getRepresentativeKey(V value) {
+ Set<K> keys = getKeys(value);
+ if (!keys.isEmpty()) {
+ return keys.size() == 1 ? keys.iterator().next() : representatives.get(value);
+ }
+ return null;
+ }
+
+ @Override
+ public V getRepresentativeValue(K key) {
+ return get(key);
+ }
+
+ @Override
+ public Set<V> getValues(K key) {
+ if (containsKey(key)) {
+ return Collections.singleton(get(key));
+ }
+ return Collections.emptySet();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneRepresentativeMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneRepresentativeMap.java
new file mode 100644
index 0000000..3e7bf7b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneRepresentativeMap.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.collections;
+
+/**
+ * Interface that accommodates many-to-one mappings.
+ *
+ * <p>This interface implicitly adds a "representative" for each many-to-one mapping by inheriting
+ * from {@link BidirectionalManyToManyRepresentativeMap}.
+ */
+public interface BidirectionalManyToOneRepresentativeMap<K, V>
+ extends BidirectionalManyToOneMap<K, V>, BidirectionalManyToManyRepresentativeMap<K, V> {}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java
index d618b52..90217df 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java
@@ -4,15 +4,17 @@
package com.android.tools.r8.utils.collections;
-import com.android.tools.r8.utils.IterableUtils;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import java.util.Collection;
+import java.util.Collections;
import java.util.Map;
import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
public class BidirectionalOneToOneHashMap<K, V>
- extends BidirectionalManyToManyRepresentativeMap<K, V> implements Map<K, V> {
+ implements MutableBidirectionalOneToOneMap<K, V>, Map<K, V> {
private final BiMap<K, V> backing;
@@ -44,8 +46,19 @@
return backing.entrySet();
}
- public V forcePut(K key, V value) {
- return backing.forcePut(key, value);
+ @Override
+ public void forEach(BiConsumer<? super K, ? super V> consumer) {
+ backing.forEach(consumer);
+ }
+
+ @Override
+ public void forEachKey(Consumer<? super K> consumer) {
+ backing.keySet().forEach(consumer);
+ }
+
+ @Override
+ public void forEachManyToOneMapping(BiConsumer<? super Set<K>, V> consumer) {
+ backing.forEach((key, value) -> consumer.accept(Collections.singleton(key), value));
}
@Override
@@ -54,15 +67,17 @@
}
@Override
- public BiMap<K, V> getForwardBacking() {
+ public V getOrDefault(Object key, V defaultValue) {
+ V value = get(key);
+ return value != null ? value : defaultValue;
+ }
+
+ @Override
+ public BiMap<K, V> getForwardMap() {
return backing;
}
@Override
- public BiMap<V, K> getInverseBacking() {
- return backing.inverse();
- }
-
public BidirectionalOneToOneHashMap<V, K> getInverseOneToOneMap() {
return new BidirectionalOneToOneHashMap<>(backing.inverse());
}
@@ -78,19 +93,19 @@
}
@Override
- public Iterable<K> getKeys(V value) {
+ public Set<K> getKeys(V value) {
if (containsValue(value)) {
- return IterableUtils.singleton(getRepresentativeKey(value));
+ return Collections.singleton(getRepresentativeKey(value));
}
- return IterableUtils.empty();
+ return Collections.emptySet();
}
@Override
- public Iterable<V> getValues(K key) {
+ public Set<V> getValues(K key) {
if (containsKey(key)) {
- return IterableUtils.singleton(getRepresentativeValue(key));
+ return Collections.singleton(getRepresentativeValue(key));
}
- return IterableUtils.empty();
+ return Collections.emptySet();
}
@Override
@@ -105,11 +120,12 @@
@Override
public V put(K key, V value) {
- return backing.put(key, value);
+ return backing.forcePut(key, value);
}
- public void putAll(BidirectionalOneToOneHashMap<K, V> map) {
- putAll(map.backing);
+ @Override
+ public void putAll(BidirectionalManyToManyMap<K, V> map) {
+ map.forEach(this::put);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneMap.java
new file mode 100644
index 0000000..e5085c2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneMap.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.collections;
+
+import com.google.common.collect.BiMap;
+
+/**
+ * Interface that accommodates one-to-one mappings.
+ *
+ * <p>This interface inherits from {@link BidirectionalManyToManyRepresentativeMap} to allow
+ * implementing many-to-many mappings using one-to-one mappings.
+ */
+public interface BidirectionalOneToOneMap<K, V>
+ extends BidirectionalManyToOneRepresentativeMap<K, V> {
+
+ @Override
+ BiMap<K, V> getForwardMap();
+
+ BidirectionalOneToOneMap<V, K> getInverseOneToOneMap();
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalManyToManyRepresentativeMap.java b/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalManyToManyRepresentativeMap.java
deleted file mode 100644
index dbcb753..0000000
--- a/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalManyToManyRepresentativeMap.java
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.utils.collections;
-
-import com.android.tools.r8.utils.IterableUtils;
-import java.util.Collections;
-import java.util.Map;
-
-public class EmptyBidirectionalManyToManyRepresentativeMap<K, V>
- extends BidirectionalManyToManyRepresentativeMap<K, V> {
-
- @Override
- public boolean containsKey(K key) {
- return false;
- }
-
- @Override
- public boolean containsValue(V value) {
- return false;
- }
-
- @Override
- public Map<K, V> getForwardBacking() {
- return Collections.emptyMap();
- }
-
- @Override
- public Map<V, K> getInverseBacking() {
- return Collections.emptyMap();
- }
-
- @Override
- public K getRepresentativeKey(V value) {
- return null;
- }
-
- @Override
- public V getRepresentativeValue(K key) {
- return null;
- }
-
- @Override
- public Iterable<K> getKeys(V value) {
- return IterableUtils.empty();
- }
-
- @Override
- public Iterable<V> getValues(K key) {
- return IterableUtils.empty();
- }
-
- @Override
- public boolean isEmpty() {
- return true;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalOneToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalOneToOneMap.java
new file mode 100644
index 0000000..69a28e6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalOneToOneMap.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.collections;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import java.util.Collections;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+public class EmptyBidirectionalOneToOneMap<K, V>
+ implements BidirectionalOneToOneMap<K, V>,
+ BidirectionalManyToOneRepresentativeMap<K, V>,
+ BidirectionalManyToManyRepresentativeMap<K, V> {
+
+ @Override
+ public boolean containsKey(K key) {
+ return false;
+ }
+
+ @Override
+ public boolean containsValue(V value) {
+ return false;
+ }
+
+ @Override
+ public void forEach(BiConsumer<? super K, ? super V> consumer) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void forEachKey(Consumer<? super K> consumer) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void forEachManyToOneMapping(BiConsumer<? super Set<K>, V> consumer) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public V get(Object key) {
+ return null;
+ }
+
+ @Override
+ public V getOrDefault(Object key, V defaultValue) {
+ return defaultValue;
+ }
+
+ @Override
+ public BiMap<K, V> getForwardMap() {
+ return HashBiMap.create();
+ }
+
+ @Override
+ public K getRepresentativeKey(V value) {
+ return null;
+ }
+
+ @Override
+ public V getRepresentativeValue(K key) {
+ return null;
+ }
+
+ @Override
+ public Set<K> getKeys(V value) {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Set<V> getValues(K key) {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return true;
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public BidirectionalOneToOneMap<V, K> getInverseOneToOneMap() {
+ return new EmptyBidirectionalOneToOneMap<>();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneMap.java
new file mode 100644
index 0000000..3e1a198
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneMap.java
@@ -0,0 +1,22 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.collections;
+
+import java.util.Set;
+
+/**
+ * Interface that accommodates many-to-one mappings.
+ *
+ * <p>This interface inherits from {@link BidirectionalManyToManyMap} to allow implementing
+ * many-to-many mappings using many-to-one mappings.
+ */
+public interface MutableBidirectionalManyToOneMap<K, V> extends BidirectionalManyToOneMap<K, V> {
+
+ void put(K key, V value);
+
+ V remove(K key);
+
+ Set<K> removeValue(V value);
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneRepresentativeMap.java b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneRepresentativeMap.java
new file mode 100644
index 0000000..defacae
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneRepresentativeMap.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.collections;
+
+/**
+ * Interface that accommodates many-to-one mappings.
+ *
+ * <p>This interface implicitly adds a "representative" for each many-to-one mapping by inheriting
+ * from {@link BidirectionalManyToManyRepresentativeMap}.
+ */
+public interface MutableBidirectionalManyToOneRepresentativeMap<K, V>
+ extends MutableBidirectionalManyToOneMap<K, V>, BidirectionalManyToOneRepresentativeMap<K, V> {
+
+ K removeRepresentativeFor(V value);
+
+ void setRepresentative(V value, K representative);
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalOneToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalOneToOneMap.java
new file mode 100644
index 0000000..08ceee8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalOneToOneMap.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils.collections;
+
+/**
+ * Interface that accommodates one-to-one mappings.
+ *
+ * <p>This interface inherits from {@link BidirectionalManyToManyRepresentativeMap} to allow
+ * implementing many-to-many mappings using one-to-one mappings.
+ */
+public interface MutableBidirectionalOneToOneMap<K, V> extends BidirectionalOneToOneMap<K, V> {
+
+ V put(K key, V value);
+
+ void putAll(BidirectionalManyToManyMap<K, V> map);
+}