Rewrite synthetic methods to final signature in one step
Change-Id: I1220586d44b5d211c375ec723bf3a059a34bdafe
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 c15db19..8f6530e 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -1033,7 +1033,7 @@
@Override
public DexField getRenamedFieldSignature(DexField originalField) {
DexField renamedField = getPrevious().getRenamedFieldSignature(originalField);
- return fieldMap.getOrDefault(renamedField, renamedField);
+ return internalGetNextFieldSignature(renamedField);
}
@Override
@@ -1133,6 +1133,10 @@
return prototypeChanges;
}
+ protected DexField internalGetNextFieldSignature(DexField field) {
+ return fieldMap.getOrDefault(field, field);
+ }
+
@Override
protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
return originalMethodSignatures.getRepresentativeValueOrDefault(method, method);
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 0204788..d610175 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.synthesis;
+import static com.google.common.base.Predicates.alwaysTrue;
+
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -29,7 +31,8 @@
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.BidirectionalOneToManyRepresentativeHashMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalOneToManyRepresentativeMap;
import com.android.tools.r8.utils.structural.RepresentativeMap;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
@@ -43,7 +46,6 @@
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
import java.util.function.BiConsumer;
@@ -69,118 +71,60 @@
public static class SyntheticFinalizationGraphLens extends NestedGraphLens {
- private final Map<DexType, DexType> syntheticTypeMap;
- private final Map<DexMethod, DexMethod> syntheticMethodsMap;
-
private SyntheticFinalizationGraphLens(
GraphLens previous,
- Map<DexType, DexType> syntheticClassesMap,
- Map<DexMethod, DexMethod> syntheticMethodsMap,
Map<DexType, DexType> typeMap,
BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap,
Map<DexMethod, DexMethod> methodMap,
BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod> originalMethodSignatures,
DexItemFactory factory) {
super(typeMap, methodMap, fieldMap, originalMethodSignatures, previous, factory);
- this.syntheticTypeMap = syntheticClassesMap;
- this.syntheticMethodsMap = syntheticMethodsMap;
}
@Override
public boolean isSyntheticFinalizationGraphLens() {
return true;
}
-
- // The mapping is many to one, so the inverse is only defined up to equivalence groups.
- // Override the access to renamed signatures to first check for synthetic mappings before
- // using the original item mappings of the
-
- @Override
- public DexField getRenamedFieldSignature(DexField originalField) {
- if (syntheticTypeMap.containsKey(originalField.holder)) {
- DexField renamed = fieldMap.get(originalField);
- if (renamed != null) {
- return renamed;
- }
- }
- return super.getRenamedFieldSignature(originalField);
- }
-
- @Override
- public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) {
- if (syntheticTypeMap.containsKey(originalMethod.holder)) {
- DexMethod renamed = methodMap.get(originalMethod);
- if (renamed != null) {
- return renamed;
- }
- }
- DexMethod renamed = syntheticMethodsMap.get(originalMethod);
- return renamed != null ? renamed : super.getRenamedMethodSignature(originalMethod, applied);
- }
}
private static class Builder {
- // Forward mapping of internal to external synthetics.
- Map<DexType, DexType> syntheticClassesMap = new IdentityHashMap<>();
- Map<DexMethod, DexMethod> syntheticMethodsMap = new IdentityHashMap<>();
-
Map<DexType, DexType> typeMap = new IdentityHashMap<>();
BidirectionalManyToOneRepresentativeHashMap<DexField, DexField> fieldMap =
new BidirectionalManyToOneRepresentativeHashMap<>();
Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
- protected final BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures =
- new BidirectionalOneToOneHashMap<>();
-
- void moveSyntheticClass(DexType from, DexType to) {
- assert !syntheticClassesMap.containsKey(from);
- syntheticClassesMap.put(from, to);
- typeMap.put(from, to);
- }
-
- void moveSyntheticMethod(DexMethod from, DexMethod to) {
- assert !syntheticMethodsMap.containsKey(from);
- syntheticMethodsMap.put(from, to);
- methodMap.put(from, to);
- typeMap.put(from.getHolderType(), to.getHolderType());
- }
+ protected final MutableBidirectionalOneToManyRepresentativeMap<DexMethod, DexMethod>
+ originalMethodSignatures = new BidirectionalOneToManyRepresentativeHashMap<>();
void move(DexType from, DexType to) {
- typeMap.put(from, to);
+ DexType old = typeMap.put(from, to);
+ assert old == null || old == to;
}
void move(DexField from, DexField to) {
- fieldMap.put(from, to);
+ DexField old = fieldMap.put(from, to);
+ assert old == null || old == to;
}
void move(DexMethod from, DexMethod to) {
- methodMap.put(from, to);
+ DexMethod old = methodMap.put(from, to);
+ assert old == null || old == to;
originalMethodSignatures.put(to, from);
}
SyntheticFinalizationGraphLens build(GraphLens previous, DexItemFactory factory) {
- assert verifySubMap(syntheticClassesMap, typeMap);
if (typeMap.isEmpty() && fieldMap.isEmpty() && methodMap.isEmpty()) {
return null;
}
return new SyntheticFinalizationGraphLens(
previous,
- syntheticClassesMap,
- syntheticMethodsMap,
typeMap,
fieldMap,
methodMap,
originalMethodSignatures,
factory);
}
-
- private static <K, V> boolean verifySubMap(Map<K, V> sub, Map<K, V> sup) {
- for (Entry<K, V> entry : sub.entrySet()) {
- assert sup.get(entry.getKey()) == entry.getValue();
- }
- return true;
- }
}
public static class EquivalenceGroup<T extends SyntheticDefinition<?, T, ?>> {
@@ -279,24 +223,19 @@
ImmutableMap.builder();
ImmutableMap.Builder<DexType, SyntheticProgramClassReference> finalClassesBuilder =
ImmutableMap.builder();
- List<DexProgramClass> finalSyntheticProgramDefinitions = new ArrayList<>();
Set<DexType> derivedMainDexTypes = Sets.newIdentityHashSet();
{
Map<String, NumberGenerator> generators = new HashMap<>();
application =
buildLensAndProgram(
appView,
- computeEquivalences(appView, committed.getNonLegacyMethods().values(), generators),
- computeEquivalences(appView, committed.getNonLegacyClasses().values(), generators),
+ computeEquivalences(
+ appView, committed.getNonLegacyMethods().values(), generators, lensBuilder),
+ computeEquivalences(
+ appView, committed.getNonLegacyClasses().values(), generators, lensBuilder),
lensBuilder,
- (clazz, reference) -> {
- finalSyntheticProgramDefinitions.add(clazz);
- finalClassesBuilder.put(clazz.getType(), reference);
- },
- (clazz, reference) -> {
- finalSyntheticProgramDefinitions.add(clazz);
- finalMethodsBuilder.put(clazz.getType(), reference);
- },
+ (clazz, reference) -> finalClassesBuilder.put(clazz.getType(), reference),
+ (clazz, reference) -> finalMethodsBuilder.put(clazz.getType(), reference),
derivedMainDexTypes);
}
ImmutableMap<DexType, SyntheticMethodReference> finalMethods = finalMethodsBuilder.build();
@@ -332,7 +271,8 @@
Map<DexType, EquivalenceGroup<D>> computeEquivalences(
AppView<?> appView,
ImmutableCollection<R> references,
- Map<String, NumberGenerator> generators) {
+ Map<String, NumberGenerator> generators,
+ Builder lensBuilder) {
boolean intermediate = appView.options().intermediate;
Map<DexType, D> definitions = lookupDefinitions(appView, references);
ClassToFeatureSplitMap classToFeatureSplitMap =
@@ -348,7 +288,12 @@
classToFeatureSplitMap,
synthetics);
return computeActualEquivalences(
- potentialEquivalences, generators, appView, intermediate, classToFeatureSplitMap);
+ potentialEquivalences,
+ generators,
+ appView,
+ intermediate,
+ classToFeatureSplitMap,
+ lensBuilder);
}
private boolean isNotSyntheticType(DexType type) {
@@ -374,80 +319,15 @@
BiConsumer<DexProgramClass, SyntheticMethodReference> addFinalSyntheticMethod,
Set<DexType> derivedMainDexSynthetics) {
DexApplication application = appView.appInfo().app();
- DexItemFactory factory = appView.dexItemFactory();
MainDexInfo mainDexInfo = appView.appInfo().getMainDexInfo();
List<DexProgramClass> newProgramClasses = new ArrayList<>();
Set<DexType> pruned = Sets.newIdentityHashSet();
- syntheticMethodGroups.forEach(
- (syntheticType, syntheticGroup) -> {
- SyntheticMethodDefinition representative = syntheticGroup.getRepresentative();
- SynthesizingContext context = representative.getContext();
- context.registerPrefixRewriting(syntheticType, appView);
- DexProgramClass externalSyntheticClass =
- createExternalMethodClass(syntheticType, representative, factory);
- newProgramClasses.add(externalSyntheticClass);
- addSyntheticMarker(representative.getKind(), externalSyntheticClass, context, appView);
- assert externalSyntheticClass.getMethodCollection().size() == 1;
- DexEncodedMethod externalSyntheticMethod =
- externalSyntheticClass.methods().iterator().next();
- for (SyntheticMethodDefinition member : syntheticGroup.getMembers()) {
- DexMethod memberReference = member.getMethod().getReference();
- pruned.add(member.getHolder().getType());
- if (memberReference != externalSyntheticMethod.method) {
- lensBuilder.moveSyntheticMethod(memberReference, externalSyntheticMethod.method);
- }
- if (member.getContext().isDerivedFromMainDexList(mainDexInfo)) {
- derivedMainDexSynthetics.add(syntheticType);
- }
- }
- });
-
- List<DexProgramClass> deduplicatedClasses = new ArrayList<>();
- syntheticClassGroups.forEach(
- (syntheticType, syntheticGroup) -> {
- SyntheticProgramClassDefinition representative = syntheticGroup.getRepresentative();
- SynthesizingContext context = representative.getContext();
- context.registerPrefixRewriting(syntheticType, appView);
- DexProgramClass externalSyntheticClass = representative.getHolder();
- newProgramClasses.add(externalSyntheticClass);
- addSyntheticMarker(representative.getKind(), externalSyntheticClass, context, appView);
- for (SyntheticProgramClassDefinition member : syntheticGroup.getMembers()) {
- DexProgramClass memberClass = member.getHolder();
- DexType memberType = memberClass.getType();
- pruned.add(memberType);
- if (memberType != syntheticType) {
- lensBuilder.moveSyntheticClass(memberType, syntheticType);
- }
- // The aliasing of the non-representative members needs to be recorded manually.
- if (member != representative) {
- deduplicatedClasses.add(memberClass);
- }
- if (member.getContext().isDerivedFromMainDexList(mainDexInfo)) {
- derivedMainDexSynthetics.add(syntheticType);
- }
- }
- });
-
- for (DexProgramClass clazz : application.classes()) {
- if (!pruned.contains(clazz.type)) {
- newProgramClasses.add(clazz);
- }
- }
- application = application.builder().replaceProgramClasses(newProgramClasses).build();
-
- // We can only assert that the method container classes are in here as the classes need
- // to be rewritten by the tree-fixer.
- for (DexType key : syntheticMethodGroups.keySet()) {
- assert application.definitionFor(key) != null;
- }
-
- DexApplication.Builder<?> builder = application.builder();
TreeFixerBase treeFixer =
new TreeFixerBase(appView) {
@Override
public DexType mapClassType(DexType type) {
- return lensBuilder.syntheticClassesMap.getOrDefault(type, type);
+ return lensBuilder.typeMap.getOrDefault(type, type);
}
@Override
@@ -465,6 +345,59 @@
lensBuilder.move(from, to);
}
};
+
+ List<DexProgramClass> deduplicatedClasses = new ArrayList<>();
+ syntheticMethodGroups.forEach(
+ (syntheticType, syntheticGroup) -> {
+ SyntheticMethodDefinition representative = syntheticGroup.getRepresentative();
+ SynthesizingContext context = representative.getContext();
+ context.registerPrefixRewriting(syntheticType, appView);
+ DexProgramClass representativeClass = representative.getHolder();
+ addSyntheticMarker(representative.getKind(), representativeClass, context, appView);
+ assert representativeClass.getMethodCollection().size() == 1;
+ for (SyntheticMethodDefinition member : syntheticGroup.getMembers()) {
+ if (member != representative) {
+ pruned.add(member.getHolder().getType());
+ deduplicatedClasses.add(member.getHolder());
+ }
+ if (member.getContext().isDerivedFromMainDexList(mainDexInfo)) {
+ derivedMainDexSynthetics.add(syntheticType);
+ }
+ }
+ });
+
+ syntheticClassGroups.forEach(
+ (syntheticType, syntheticGroup) -> {
+ SyntheticProgramClassDefinition representative = syntheticGroup.getRepresentative();
+ SynthesizingContext context = representative.getContext();
+ context.registerPrefixRewriting(syntheticType, appView);
+ addSyntheticMarker(
+ representative.getKind(), representative.getHolder(), context, appView);
+ for (SyntheticProgramClassDefinition member : syntheticGroup.getMembers()) {
+ DexProgramClass memberClass = member.getHolder();
+ DexType memberType = memberClass.getType();
+ if (member != representative) {
+ pruned.add(memberType);
+ deduplicatedClasses.add(memberClass);
+ }
+ if (member.getContext().isDerivedFromMainDexList(mainDexInfo)) {
+ derivedMainDexSynthetics.add(syntheticType);
+ }
+ }
+ });
+
+ for (DexProgramClass clazz : application.classes()) {
+ if (!pruned.contains(clazz.type)) {
+ newProgramClasses.add(clazz);
+ }
+ }
+ application = application.builder().replaceProgramClasses(newProgramClasses).build();
+
+ // Assert that the non-representatives have been removed from the app.
+ assert verifyNonRepresentativesRemovedFromApplication(application, syntheticClassGroups);
+ assert verifyNonRepresentativesRemovedFromApplication(application, syntheticMethodGroups);
+
+ DexApplication.Builder<?> builder = application.builder();
treeFixer.fixupClasses(deduplicatedClasses);
builder.replaceProgramClasses(treeFixer.fixupClasses(application.classes()));
application = builder.build();
@@ -486,15 +419,16 @@
(syntheticType, syntheticGroup) -> {
DexProgramClass externalSyntheticClass = appForLookup.programDefinitionFor(syntheticType);
SyntheticMethodDefinition representative = syntheticGroup.getRepresentative();
+ assert externalSyntheticClass.getMethodCollection().size() == 1;
+ assert externalSyntheticClass.getMethodCollection().hasDirectMethods();
+ DexEncodedMethod syntheticMethodDefinition =
+ externalSyntheticClass.getMethodCollection().getDirectMethod(alwaysTrue());
addFinalSyntheticMethod.accept(
externalSyntheticClass,
new SyntheticMethodReference(
representative.getKind(),
representative.getContext(),
- representative
- .getMethod()
- .getReference()
- .withHolder(externalSyntheticClass.type, factory)));
+ syntheticMethodDefinition.getReference()));
});
for (DexType key : syntheticMethodGroups.keySet()) {
@@ -508,6 +442,18 @@
return application;
}
+ private static <T extends SyntheticDefinition<?, T, ?>>
+ boolean verifyNonRepresentativesRemovedFromApplication(
+ DexApplication application, Map<DexType, EquivalenceGroup<T>> syntheticGroups) {
+ for (EquivalenceGroup<?> syntheticGroup : syntheticGroups.values()) {
+ for (SyntheticDefinition<?, ?, ?> member : syntheticGroup.getMembers()) {
+ assert member == syntheticGroup.getRepresentative()
+ || application.definitionFor(member.getHolder().getType()) == null;
+ }
+ }
+ return true;
+ }
+
private static void addSyntheticMarker(
SyntheticKind kind,
DexProgramClass externalSyntheticClass,
@@ -519,25 +465,6 @@
}
}
- private static DexProgramClass createExternalMethodClass(
- DexType syntheticType, SyntheticMethodDefinition representative, DexItemFactory factory) {
- SyntheticProgramClassBuilder builder =
- new SyntheticProgramClassBuilder(syntheticType, representative.getContext(), factory);
- // TODO(b/158159959): Support grouping multiple methods per synthetic class.
- builder.addMethod(
- methodBuilder -> {
- DexEncodedMethod definition = representative.getMethod().getDefinition();
- methodBuilder
- .setName(SyntheticNaming.INTERNAL_SYNTHETIC_METHOD_PREFIX)
- .setAccessFlags(definition.accessFlags)
- .setProto(definition.getProto())
- .setClassFileVersion(
- definition.hasClassFileVersion() ? definition.getClassFileVersion() : null)
- .setCode(m -> definition.getCode());
- });
- return builder.build();
- }
-
private static boolean shouldAnnotateSynthetics(InternalOptions options) {
// Only intermediate builds have annotated synthetics to allow later sharing.
// This is currently also disabled on CF to CF desugaring to avoid missing class references to
@@ -552,7 +479,8 @@
Map<String, NumberGenerator> generators,
AppView<?> appView,
boolean intermediate,
- ClassToFeatureSplitMap classToFeatureSplitMap) {
+ ClassToFeatureSplitMap classToFeatureSplitMap,
+ Builder lensBuilder) {
Map<String, List<EquivalenceGroup<T>>> groupsPerPrefix = new HashMap<>();
potentialEquivalences.forEach(
members -> {
@@ -601,6 +529,9 @@
DexType representativeType =
createExternalType(kind, externalSyntheticTypePrefix, generators, appView);
equivalences.put(representativeType, group);
+ for (T member : group.getMembers()) {
+ lensBuilder.move(member.getHolder().getType(), representativeType);
+ }
}
});
return equivalences;
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
index 8f29df9..b75d224 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneHashMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneHashMap.java
@@ -130,10 +130,11 @@
}
@Override
- public void put(K key, V value) {
- remove(key);
+ public V put(K key, V value) {
+ V old = remove(key);
backing.put(key, value);
inverse.computeIfAbsent(value, ignore -> new LinkedHashSet<>()).add(key);
+ return old;
}
@Override
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 830138a..e23a565 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
@@ -77,6 +77,11 @@
}
@Override
+ public K getKey(V value) {
+ return backing.inverse().get(value);
+ }
+
+ @Override
public BiMap<K, V> getForwardMap() {
return backing;
}
@@ -88,12 +93,12 @@
@Override
public K getRepresentativeKey(V value) {
- return backing.inverse().get(value);
+ return getKey(value);
}
@Override
public V getRepresentativeValue(K key) {
- return backing.get(key);
+ return get(key);
}
@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
index e5085c2..569c52e 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneMap.java
@@ -15,6 +15,8 @@
public interface BidirectionalOneToOneMap<K, V>
extends BidirectionalManyToOneRepresentativeMap<K, V> {
+ K getKey(V value);
+
@Override
BiMap<K, V> getForwardMap();
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
index f91f7df..62fba9a 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalOneToOneMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalOneToOneMap.java
@@ -57,6 +57,11 @@
}
@Override
+ public K getKey(V value) {
+ return null;
+ }
+
+ @Override
public BiMap<K, V> getForwardMap() {
return HashBiMap.create();
}
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
index 7953e91..05bd5ea 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneMap.java
@@ -11,7 +11,7 @@
void clear();
- void put(K key, V value);
+ V put(K key, V value);
void put(Iterable<K> key, V value);
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
index 5e5f469..87a29fa 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalOneToOneMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalOneToOneMap.java
@@ -10,4 +10,6 @@
V put(K key, V value);
void putAll(BidirectionalManyToManyMap<K, V> map);
+
+ V remove(Object key);
}