Compute feature placement of synthetics based on their context.
Fixes: 180086194
Change-Id: I7c689b37131bd43b72635f0a39b427f2432c1b58
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 8f56827..614ac4d 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -467,7 +467,7 @@
// Pull out the classes that should go into feature splits.
Map<FeatureSplit, Set<DexProgramClass>> featureSplitClasses =
- classToFeatureSplitMap.getFeatureSplitClasses(classes);
+ classToFeatureSplitMap.getFeatureSplitClasses(classes, appView.getSyntheticItems());
if (featureSplitClasses.size() > 0) {
for (Set<DexProgramClass> featureClasses : featureSplitClasses.values()) {
classes.removeAll(featureClasses);
@@ -475,14 +475,17 @@
}
List<DexProgramClass> toRemove = new ArrayList<>();
for (DexProgramClass dexProgramClass : classes) {
- Collection<DexProgramClass> synthesizedFrom = dexProgramClass.getSynthesizedFrom();
- if (!synthesizedFrom.isEmpty()) {
- DexProgramClass from = Iterables.getFirst(synthesizedFrom, null);
- FeatureSplit featureSplit = classToFeatureSplitMap.getFeatureSplit(from);
- if (!featureSplit.isBase()) {
- Set<DexProgramClass> dexProgramClasses = featureSplitClasses.get(featureSplit);
- dexProgramClasses.add(dexProgramClass);
- toRemove.add(dexProgramClass);
+ if (appView.getSyntheticItems().isLegacySyntheticClass(dexProgramClass)) {
+ Collection<DexProgramClass> synthesizedFrom = dexProgramClass.getSynthesizedFrom();
+ if (!synthesizedFrom.isEmpty()) {
+ DexProgramClass from = Iterables.getFirst(synthesizedFrom, null);
+ FeatureSplit featureSplit =
+ classToFeatureSplitMap.getFeatureSplit(from, appView.getSyntheticItems());
+ if (!featureSplit.isBase()) {
+ Set<DexProgramClass> dexProgramClasses = featureSplitClasses.get(featureSplit);
+ dexProgramClasses.add(dexProgramClass);
+ toRemove.add(dexProgramClass);
+ }
}
}
}
diff --git a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
index 1ea1595..d96643d 100644
--- a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
+++ b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
@@ -17,11 +17,15 @@
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.Sets;
+import java.util.Collection;
import java.util.HashMap;
import java.util.IdentityHashMap;
+import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@@ -88,9 +92,9 @@
return result;
}
- public int compareFeatureSplitsForDexTypes(DexType a, DexType b) {
- FeatureSplit featureSplitA = getFeatureSplit(a);
- FeatureSplit featureSplitB = getFeatureSplit(b);
+ public int compareFeatureSplitsForDexTypes(DexType a, DexType b, SyntheticItems syntheticItems) {
+ FeatureSplit featureSplitA = getFeatureSplit(a, syntheticItems);
+ FeatureSplit featureSplitB = getFeatureSplit(b, syntheticItems);
assert featureSplitA != null;
assert featureSplitB != null;
if (featureSplitA == featureSplitB) {
@@ -109,10 +113,10 @@
}
public Map<FeatureSplit, Set<DexProgramClass>> getFeatureSplitClasses(
- Set<DexProgramClass> classes) {
+ Set<DexProgramClass> classes, SyntheticItems syntheticItems) {
Map<FeatureSplit, Set<DexProgramClass>> result = new IdentityHashMap<>();
for (DexProgramClass clazz : classes) {
- FeatureSplit featureSplit = getFeatureSplit(clazz);
+ FeatureSplit featureSplit = getFeatureSplit(clazz, syntheticItems);
if (featureSplit != null && !featureSplit.isBase()) {
result.computeIfAbsent(featureSplit, ignore -> Sets.newIdentityHashSet()).add(clazz);
}
@@ -120,41 +124,61 @@
return result;
}
- public FeatureSplit getFeatureSplit(ProgramDefinition clazz) {
- return getFeatureSplit(clazz.getContextType());
+ public FeatureSplit getFeatureSplit(ProgramDefinition clazz, SyntheticItems syntheticItems) {
+ return getFeatureSplit(clazz.getContextType(), syntheticItems);
}
- public FeatureSplit getFeatureSplit(DexType type) {
+ public FeatureSplit getFeatureSplit(DexType type, SyntheticItems syntheticItems) {
+ Collection<DexType> contexts = syntheticItems.getSynthesizingContexts(type);
+ if (!contexts.isEmpty()) {
+ Iterator<DexType> iterator = contexts.iterator();
+ DexType context = iterator.next();
+ FeatureSplit feature = classToFeatureSplitMap.getOrDefault(context, FeatureSplit.BASE);
+ assert verifyConsistentFeatureContexts(iterator, feature);
+ return feature;
+ }
return classToFeatureSplitMap.getOrDefault(type, FeatureSplit.BASE);
}
+ private boolean verifyConsistentFeatureContexts(
+ Iterator<DexType> contextIterator, FeatureSplit feature) {
+ while (contextIterator.hasNext()) {
+ assert feature
+ == classToFeatureSplitMap.getOrDefault(contextIterator.next(), FeatureSplit.BASE);
+ }
+ return true;
+ }
+
public boolean isEmpty() {
return classToFeatureSplitMap.isEmpty();
}
- public boolean isInBase(DexProgramClass clazz) {
- return getFeatureSplit(clazz).isBase();
+ public boolean isInBase(DexProgramClass clazz, SyntheticItems syntheticItems) {
+ return getFeatureSplit(clazz, syntheticItems).isBase();
}
- public boolean isInBaseOrSameFeatureAs(DexProgramClass clazz, ProgramDefinition context) {
- FeatureSplit split = getFeatureSplit(clazz);
- return split.isBase() || split == getFeatureSplit(context);
+ public boolean isInBaseOrSameFeatureAs(
+ DexProgramClass clazz, ProgramDefinition context, SyntheticItems syntheticItems) {
+ FeatureSplit split = getFeatureSplit(clazz, syntheticItems);
+ return split.isBase() || split == getFeatureSplit(context, syntheticItems);
}
- public boolean isInFeature(DexProgramClass clazz) {
- return !isInBase(clazz);
+ public boolean isInFeature(DexProgramClass clazz, SyntheticItems syntheticItems) {
+ return !isInBase(clazz, syntheticItems);
}
- public boolean isInSameFeatureOrBothInBase(ProgramMethod a, ProgramMethod b) {
- return isInSameFeatureOrBothInBase(a.getHolder(), b.getHolder());
+ public boolean isInSameFeatureOrBothInBase(
+ ProgramMethod a, ProgramMethod b, SyntheticItems syntheticItems) {
+ return isInSameFeatureOrBothInBase(a.getHolder(), b.getHolder(), syntheticItems);
}
- public boolean isInSameFeatureOrBothInBase(DexProgramClass a, DexProgramClass b) {
- return getFeatureSplit(a) == getFeatureSplit(b);
+ public boolean isInSameFeatureOrBothInBase(
+ DexProgramClass a, DexProgramClass b, SyntheticItems syntheticItems) {
+ return getFeatureSplit(a, syntheticItems) == getFeatureSplit(b, syntheticItems);
}
- public boolean isInSameFeatureOrBothInBase(DexType a, DexType b) {
- return getFeatureSplit(a) == getFeatureSplit(b);
+ public boolean isInSameFeatureOrBothInBase(DexType a, DexType b, SyntheticItems syntheticItems) {
+ return getFeatureSplit(a, syntheticItems) == getFeatureSplit(b, syntheticItems);
}
public ClassToFeatureSplitMap rewrittenWithLens(GraphLens lens) {
@@ -187,4 +211,14 @@
});
return classToFeatureSplitMapAfterPruning;
}
+
+ // Static helpers to avoid verbose predicates.
+
+ private static ClassToFeatureSplitMap getMap(AppView<AppInfoWithLiveness> appView) {
+ return appView.appInfo().getClassToFeatureSplitMap();
+ }
+
+ public static boolean isInFeature(DexProgramClass clazz, AppView<AppInfoWithLiveness> appView) {
+ return getMap(appView).isInFeature(clazz, appView.getSyntheticItems());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/AccessControl.java b/src/main/java/com/android/tools/r8/graph/AccessControl.java
index f872a56..51ff1e2 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessControl.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessControl.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
+import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.OptionalBool;
/**
@@ -18,16 +19,21 @@
DexClass clazz,
ProgramDefinition context,
AppView<? extends AppInfoWithClassHierarchy> appView) {
- return isClassAccessible(clazz, context, appView.appInfo().getClassToFeatureSplitMap());
+ return isClassAccessible(
+ clazz, context, appView.appInfo().getClassToFeatureSplitMap(), appView.getSyntheticItems());
}
public static OptionalBool isClassAccessible(
- DexClass clazz, ProgramDefinition context, ClassToFeatureSplitMap classToFeatureSplitMap) {
+ DexClass clazz,
+ ProgramDefinition context,
+ ClassToFeatureSplitMap classToFeatureSplitMap,
+ SyntheticItems syntheticItems) {
if (!clazz.isPublic() && !clazz.getType().isSamePackage(context.getContextType())) {
return OptionalBool.FALSE;
}
if (clazz.isProgramClass()
- && !classToFeatureSplitMap.isInBaseOrSameFeatureAs(clazz.asProgramClass(), context)) {
+ && !classToFeatureSplitMap.isInBaseOrSameFeatureAs(
+ clazz.asProgramClass(), context, syntheticItems)) {
return OptionalBool.UNKNOWN;
}
return OptionalBool.TRUE;
@@ -60,7 +66,11 @@
AppInfoWithClassHierarchy appInfo) {
AccessFlags<?> memberFlags = member.getDefinition().getAccessFlags();
OptionalBool classAccessibility =
- isClassAccessible(initialResolutionHolder, context, appInfo.getClassToFeatureSplitMap());
+ isClassAccessible(
+ initialResolutionHolder,
+ context,
+ appInfo.getClassToFeatureSplitMap(),
+ appInfo.getSyntheticItems());
if (classAccessibility.isFalse()) {
return OptionalBool.FALSE;
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppServices.java b/src/main/java/com/android/tools/r8/graph/AppServices.java
index bb61928..2cfb4d9 100644
--- a/src/main/java/com/android/tools/r8/graph/AppServices.java
+++ b/src/main/java/com/android/tools/r8/graph/AppServices.java
@@ -105,12 +105,14 @@
}
// Check if service is defined feature
DexProgramClass serviceClass = appView.definitionForProgramType(serviceType);
- if (serviceClass != null && classToFeatureSplitMap.isInFeature(serviceClass)) {
+ if (serviceClass != null
+ && classToFeatureSplitMap.isInFeature(serviceClass, appView.getSyntheticItems())) {
return true;
}
for (DexType implementationType : featureImplementations.get(FeatureSplit.BASE)) {
DexProgramClass implementationClass = appView.definitionForProgramType(implementationType);
- if (implementationClass != null && classToFeatureSplitMap.isInFeature(implementationClass)) {
+ if (implementationClass != null
+ && classToFeatureSplitMap.isInFeature(implementationClass, appView.getSyntheticItems())) {
return true;
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java
index 9e4dd2b..20caa50 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java
@@ -19,6 +19,9 @@
@Override
public FeatureSplit getMergeKey(DexProgramClass clazz) {
- return appView.appInfo().getClassToFeatureSplitMap().getFeatureSplit(clazz);
+ return appView
+ .appInfo()
+ .getClassToFeatureSplitMap()
+ .getFeatureSplit(clazz, appView.getSyntheticItems());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 969b5d5..33e414f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -38,6 +38,7 @@
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
@@ -152,10 +153,11 @@
return false;
}
+ SyntheticItems syntheticItems = appView.getSyntheticItems();
ClassToFeatureSplitMap classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap();
- if (!classToFeatureSplitMap.isInSameFeatureOrBothInBase(singleTarget, method)) {
+ if (!classToFeatureSplitMap.isInSameFeatureOrBothInBase(singleTarget, method, syntheticItems)) {
// Still allow inlining if we inline from the base into a feature.
- if (!classToFeatureSplitMap.isInBase(singleTarget.getHolder())) {
+ if (!classToFeatureSplitMap.isInBase(singleTarget.getHolder(), syntheticItems)) {
whyAreYouNotInliningReporter.reportInliningAcrossFeatureSplit();
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 8dc0dd3..0f12d78 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClasspathMethod;
import com.android.tools.r8.graph.Code;
@@ -1287,7 +1288,7 @@
code -> {
ProgramMethod context = code.context();
assert !context.getDefinition().getCode().isOutlineCode();
- if (appView.appInfo().getClassToFeatureSplitMap().isInFeature(context.getHolder())) {
+ if (ClassToFeatureSplitMap.isInFeature(context.getHolder(), appView)) {
return;
}
for (BasicBlock block : code.blocks) {
@@ -1306,7 +1307,7 @@
public void identifyOutlineSites(IRCode code) {
ProgramMethod context = code.context();
assert !context.getDefinition().getCode().isOutlineCode();
- assert !appView.appInfo().getClassToFeatureSplitMap().isInFeature(context.getHolder());
+ assert !ClassToFeatureSplitMap.isInFeature(context.getHolder(), appView);
for (BasicBlock block : code.blocks) {
new OutlineSiteIdentifier(context, block).process();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index afdf56a..289b2ba 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -246,7 +246,8 @@
// Make sure the (base) type is visible.
ClassToFeatureSplitMap classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap();
- if (AccessControl.isClassAccessible(baseClass, context, classToFeatureSplitMap)
+ if (AccessControl.isClassAccessible(
+ baseClass, context, classToFeatureSplitMap, appView.getSyntheticItems())
.isPossiblyFalse()) {
return;
}
@@ -254,7 +255,8 @@
// If the type is guaranteed to be visible, it must be in the same feature as the current method
// or in the base.
assert !baseClass.isProgramClass()
- || classToFeatureSplitMap.isInBaseOrSameFeatureAs(baseClass.asProgramClass(), context);
+ || classToFeatureSplitMap.isInBaseOrSameFeatureAs(
+ baseClass.asProgramClass(), context, appView.getSyntheticItems());
consumer.accept(type, baseClass);
}
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 4963f21..afc9e50 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -357,7 +357,7 @@
if (!appInfo
.getClassToFeatureSplitMap()
- .isInSameFeatureOrBothInBase(sourceClass, targetClass)) {
+ .isInSameFeatureOrBothInBase(sourceClass, targetClass, appView.getSyntheticItems())) {
return false;
}
if (appView.appServices().allServiceTypes().contains(sourceClass.type)
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
index 9520d57..f6577d3 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
@@ -66,7 +66,10 @@
public abstract C getHolder();
final HashCode computeHash(
- RepresentativeMap map, boolean intermediate, ClassToFeatureSplitMap classToFeatureSplitMap) {
+ RepresentativeMap map,
+ boolean intermediate,
+ ClassToFeatureSplitMap classToFeatureSplitMap,
+ SyntheticItems syntheticItems) {
Hasher hasher = Hashing.murmur3_128().newHasher();
if (getKind().isFixedSuffixSynthetic) {
// Fixed synthetics are non-shareable. Its unique type is used as the hash key.
@@ -81,7 +84,7 @@
if (!classToFeatureSplitMap.isEmpty()) {
hasher.putInt(
classToFeatureSplitMap
- .getFeatureSplit(getContext().getSynthesizingContextType())
+ .getFeatureSplit(getContext().getSynthesizingContextType(), syntheticItems)
.hashCode());
}
@@ -95,15 +98,17 @@
D other,
boolean includeContext,
GraphLens graphLens,
- ClassToFeatureSplitMap classToFeatureSplitMap) {
- return compareTo(other, includeContext, graphLens, classToFeatureSplitMap) == 0;
+ ClassToFeatureSplitMap classToFeatureSplitMap,
+ SyntheticItems syntheticItems) {
+ return compareTo(other, includeContext, graphLens, classToFeatureSplitMap, syntheticItems) == 0;
}
int compareTo(
D other,
boolean includeContext,
GraphLens graphLens,
- ClassToFeatureSplitMap classToFeatureSplitMap) {
+ ClassToFeatureSplitMap classToFeatureSplitMap,
+ SyntheticItems syntheticItems) {
DexType thisType = getHolder().getType();
DexType otherType = other.getHolder().getType();
if (getKind().isFixedSuffixSynthetic) {
@@ -120,10 +125,10 @@
DexType synthesizingContextType = this.getContext().getSynthesizingContextType();
DexType otherSynthesizingContextType = other.getContext().getSynthesizingContextType();
if (!classToFeatureSplitMap.isInSameFeatureOrBothInBase(
- synthesizingContextType, otherSynthesizingContextType)) {
+ synthesizingContextType, otherSynthesizingContextType, syntheticItems)) {
int order =
classToFeatureSplitMap.compareFeatureSplitsForDexTypes(
- synthesizingContextType, otherSynthesizingContextType);
+ synthesizingContextType, otherSynthesizingContextType, syntheticItems);
assert order != 0;
return order;
}
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 60dca8f..65de13f 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -203,9 +203,11 @@
public int compareToIncludingContext(
EquivalenceGroup<T> other,
GraphLens graphLens,
- ClassToFeatureSplitMap classToFeatureSplitMap) {
+ ClassToFeatureSplitMap classToFeatureSplitMap,
+ SyntheticItems syntheticItems) {
return getRepresentative()
- .compareTo(other.getRepresentative(), true, graphLens, classToFeatureSplitMap);
+ .compareTo(
+ other.getRepresentative(), true, graphLens, classToFeatureSplitMap, syntheticItems);
}
@Override
@@ -219,11 +221,14 @@
}
private final InternalOptions options;
- private final CommittedSyntheticsCollection synthetics;
+ private final SyntheticItems synthetics;
+ private final CommittedSyntheticsCollection committed;
- SyntheticFinalization(InternalOptions options, CommittedSyntheticsCollection synthetics) {
+ SyntheticFinalization(
+ InternalOptions options, SyntheticItems synthetics, CommittedSyntheticsCollection committed) {
this.options = options;
this.synthetics = synthetics;
+ this.committed = committed;
}
public static void finalize(AppView<AppInfo> appView) {
@@ -269,8 +274,8 @@
application =
buildLensAndProgram(
appView,
- computeEquivalences(appView, synthetics.getNonLegacyMethods().values(), generators),
- computeEquivalences(appView, synthetics.getNonLegacyClasses().values(), generators),
+ computeEquivalences(appView, committed.getNonLegacyMethods().values(), generators),
+ computeEquivalences(appView, committed.getNonLegacyClasses().values(), generators),
lensBuilder,
(clazz, reference) -> {
finalSyntheticProgramDefinitions.add(clazz);
@@ -291,7 +296,7 @@
assert appView.appInfo().getMainDexInfo() == mainDexInfo;
Set<DexType> prunedSynthetics = Sets.newIdentityHashSet();
- synthetics.forEachNonLegacyItem(
+ committed.forEachNonLegacyItem(
reference -> {
DexType type = reference.getHolder();
if (!finalMethods.containsKey(type) && !finalClasses.containsKey(type)) {
@@ -304,7 +309,7 @@
SyntheticItems.INVALID_ID_AFTER_SYNTHETIC_FINALIZATION,
application,
new CommittedSyntheticsCollection(
- synthetics.getLegacyTypes(), finalMethods, finalClasses),
+ committed.getLegacyTypes(), finalMethods, finalClasses),
ImmutableList.of()),
lensBuilder.build(appView.graphLens(), appView.dexItemFactory()),
PrunedItems.builder()
@@ -330,18 +335,19 @@
intermediate,
appView.dexItemFactory(),
appView.graphLens(),
- classToFeatureSplitMap);
+ classToFeatureSplitMap,
+ synthetics);
return computeActualEquivalences(
potentialEquivalences, generators, appView, intermediate, classToFeatureSplitMap);
}
private boolean isNotSyntheticType(DexType type) {
- return !synthetics.containsNonLegacyType(type);
+ return !committed.containsNonLegacyType(type);
}
private boolean verifyNoNestedSynthetics() {
// Check that a context is never itself synthetic class.
- synthetics.forEachNonLegacyItem(
+ committed.forEachNonLegacyItem(
item -> {
assert isNotSyntheticType(item.getContext().getSynthesizingContextType());
});
@@ -368,7 +374,7 @@
DexApplication application, List<DexProgramClass> finalSyntheticClasses) {
ListMultimap<DexProgramClass, DexProgramClass> originalToSynthesized =
ArrayListMultimap.create();
- for (DexType type : synthetics.getLegacyTypes()) {
+ for (DexType type : committed.getLegacyTypes()) {
DexProgramClass clazz = DexProgramClass.asProgramClassOrNull(application.definitionFor(type));
if (clazz != null) {
for (DexProgramClass origin : clazz.getSynthesizedFrom()) {
@@ -670,10 +676,12 @@
potentialEquivalences.forEach(
members -> {
List<List<T>> groups =
- groupEquivalent(members, intermediate, appView.graphLens(), classToFeatureSplitMap);
+ groupEquivalent(
+ members, intermediate, appView.graphLens(), classToFeatureSplitMap, synthetics);
for (List<T> group : groups) {
T representative =
- findDeterministicRepresentative(group, appView.graphLens(), classToFeatureSplitMap);
+ findDeterministicRepresentative(
+ group, appView.graphLens(), classToFeatureSplitMap, synthetics);
// The representative is required to be the first element of the group.
group.remove(representative);
group.add(0, representative);
@@ -691,7 +699,8 @@
// representative which is equal to 'context' here (see assert below).
groups.sort(
(a, b) ->
- a.compareToIncludingContext(b, appView.graphLens(), classToFeatureSplitMap));
+ a.compareToIncludingContext(
+ b, appView.graphLens(), classToFeatureSplitMap, synthetics));
for (int i = 0; i < groups.size(); i++) {
EquivalenceGroup<T> group = groups.get(i);
assert group
@@ -702,7 +711,11 @@
// of the synthetic name will be non-deterministic between the two.
assert i == 0
|| checkGroupsAreDistinct(
- groups.get(i - 1), group, appView.graphLens(), classToFeatureSplitMap);
+ groups.get(i - 1),
+ group,
+ appView.graphLens(),
+ classToFeatureSplitMap,
+ synthetics);
SyntheticKind kind = group.members.get(0).getKind();
DexType representativeType =
createExternalType(kind, externalSyntheticTypePrefix, generators, appView);
@@ -716,14 +729,15 @@
List<T> potentialEquivalence,
boolean intermediate,
GraphLens graphLens,
- ClassToFeatureSplitMap classToFeatureSplitMap) {
+ ClassToFeatureSplitMap classToFeatureSplitMap,
+ SyntheticItems syntheticItems) {
List<List<T>> groups = new ArrayList<>();
// Each other member is in a shared group if it is actually equivalent to the first member.
for (T synthetic : potentialEquivalence) {
boolean requireNewGroup = true;
for (List<T> group : groups) {
if (synthetic.isEquivalentTo(
- group.get(0), intermediate, graphLens, classToFeatureSplitMap)) {
+ group.get(0), intermediate, graphLens, classToFeatureSplitMap, syntheticItems)) {
requireNewGroup = false;
group.add(synthetic);
break;
@@ -742,15 +756,20 @@
EquivalenceGroup<T> g1,
EquivalenceGroup<T> g2,
GraphLens graphLens,
- ClassToFeatureSplitMap classToFeatureSplitMap) {
- int order = g1.compareToIncludingContext(g2, graphLens, classToFeatureSplitMap);
+ ClassToFeatureSplitMap classToFeatureSplitMap,
+ SyntheticItems syntheticItems) {
+ int order = g1.compareToIncludingContext(g2, graphLens, classToFeatureSplitMap, syntheticItems);
assert order != 0;
- assert order != g2.compareToIncludingContext(g1, graphLens, classToFeatureSplitMap);
+ assert order
+ != g2.compareToIncludingContext(g1, graphLens, classToFeatureSplitMap, syntheticItems);
return true;
}
private static <T extends SyntheticDefinition<?, T, ?>> T findDeterministicRepresentative(
- List<T> members, GraphLens graphLens, ClassToFeatureSplitMap classToFeatureSplitMap) {
+ List<T> members,
+ GraphLens graphLens,
+ ClassToFeatureSplitMap classToFeatureSplitMap,
+ SyntheticItems syntheticItems) {
// Pick a deterministic member as representative.
T smallest = members.get(0);
for (int i = 1; i < members.size(); i++) {
@@ -794,7 +813,8 @@
boolean intermediate,
DexItemFactory factory,
GraphLens graphLens,
- ClassToFeatureSplitMap classToFeatureSplitMap) {
+ ClassToFeatureSplitMap classToFeatureSplitMap,
+ SyntheticItems syntheticItems) {
if (definitions.isEmpty()) {
return Collections.emptyList();
}
@@ -817,7 +837,8 @@
RepresentativeMap map = t -> syntheticTypes.contains(t) ? factory.voidType : t;
Map<HashCode, List<T>> equivalences = new HashMap<>(definitions.size());
for (T definition : definitions.values()) {
- HashCode hash = definition.computeHash(map, intermediate, classToFeatureSplitMap);
+ HashCode hash =
+ definition.computeHash(map, intermediate, classToFeatureSplitMap, syntheticItems);
equivalences.computeIfAbsent(hash, k -> new ArrayList<>()).add(definition);
}
return equivalences.values();
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 8263f40..9922011 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -84,6 +84,11 @@
private final PendingSynthetics pending = new PendingSynthetics();
+ // Empty collection for use only in tests and utilities.
+ public static SyntheticItems empty() {
+ return new SyntheticItems(-1, CommittedSyntheticsCollection.empty());
+ }
+
// Only for use from initial AppInfo/AppInfoWithClassHierarchy create functions. */
public static CommittedItems createInitialSyntheticItems(DexApplication application) {
return new CommittedItems(
@@ -215,6 +220,18 @@
return isSyntheticClass(clazz.type);
}
+ public Collection<DexType> getSynthesizingContexts(DexType type) {
+ SyntheticReference<?, ?, ?> reference = committed.getNonLegacyItem(type);
+ if (reference != null) {
+ return Collections.singletonList(reference.getContext().getSynthesizingContextType());
+ }
+ SyntheticDefinition<?, ?, ?> definition = pending.nonLegacyDefinitions.get(type);
+ if (definition != null) {
+ return Collections.singletonList(definition.getContext().getSynthesizingContextType());
+ }
+ return Collections.emptyList();
+ }
+
// TODO(b/180091213): Implement this and remove client provided the oracle.
public Set<DexReference> getSynthesizingContexts(
DexProgramClass clazz, SynthesizingContextOracle oracle) {
@@ -445,6 +462,7 @@
Result computeFinalSynthetics(AppView<?> appView) {
assert !hasPendingSyntheticClasses();
- return new SyntheticFinalization(appView.options(), committed).computeFinalSynthetics(appView);
+ return new SyntheticFinalization(appView.options(), this, committed)
+ .computeFinalSynthetics(appView);
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 21c3447..1534b4a 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -42,6 +42,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.shaking.FilteredClassPath;
+import com.android.tools.r8.synthesis.SyntheticItems;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteStreams;
@@ -611,7 +612,8 @@
classDescriptor -> {
if (featureSplitConfiguration != null) {
DexType type = dexItemFactory.createType(classDescriptor);
- FeatureSplit featureSplit = classToFeatureSplitMap.getFeatureSplit(type);
+ FeatureSplit featureSplit =
+ classToFeatureSplitMap.getFeatureSplit(type, SyntheticItems.empty());
if (featureSplit != null && !featureSplit.isBase()) {
return featureSplitArchiveOutputStreams.get(featureSplit);
}
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java b/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
index af73519..20c2b51 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThrowingConsumer;
@@ -55,7 +56,6 @@
r8FullTestBuilder
.noMinification()
.enableInliningAnnotations()
- .addInliningAnnotations()
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.O));
ThrowingConsumer<R8TestCompileResult, Exception> ensureLambdaNotInBase =
r8TestCompileResult -> {
@@ -87,6 +87,10 @@
.addFeatureSplit(FeatureClass.class)
.addFeatureSplit(Feature2Class.class)
.addKeepFeatureMainRules(BaseSuperClass.class, FeatureClass.class, Feature2Class.class)
+ .addKeepMethodRules(
+ Reference.methodFromMethod(
+ BaseSuperClass.class.getDeclaredMethod(
+ "keptApplyLambda", MyFunction.class, String.class)))
.noMinification()
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
@@ -108,6 +112,10 @@
}
public abstract String getFromFeature();
+
+ public String keptApplyLambda(MyFunction fn, String arg) {
+ return fn.apply(arg);
+ }
}
public interface MyFunction {
@@ -133,7 +141,7 @@
@NeverInline
private String useTheLambda(MyFunction f) {
- return f.apply("42");
+ return keptApplyLambda(f, "42");
}
}
@@ -159,7 +167,7 @@
@NeverInline
private String useTheLambda(MyFunction f) {
- return f.apply("43");
+ return keptApplyLambda(f, "43");
}
}
}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageFeatureWithSyntheticsTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageFeatureWithSyntheticsTest.java
index a203729..4eb6d15 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageFeatureWithSyntheticsTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageFeatureWithSyntheticsTest.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.junit.Test;
@@ -37,12 +36,16 @@
private static final List<Class<?>> FIRST_CLASSES =
ImmutableList.of(FIRST_FOO, FIRST_PKG_PRIVATE);
+ private static final Class<?> FIRST_FIRST_FOO =
+ com.android.tools.r8.repackage.testclasses.repackagefeaturewithsynthetics.first.first.Foo
+ .class;
+
+ private static final Class<?> FIRST_FIRST_PKG_PRIVATE =
+ com.android.tools.r8.repackage.testclasses.repackagefeaturewithsynthetics.first.first
+ .PkgProtectedMethod.class;
+
private static final List<Class<?>> FIRST_FIRST_CLASSES =
- ImmutableList.of(
- com.android.tools.r8.repackage.testclasses.repackagefeaturewithsynthetics.first.first.Foo
- .class,
- com.android.tools.r8.repackage.testclasses.repackagefeaturewithsynthetics.first.first
- .PkgProtectedMethod.class);
+ ImmutableList.of(FIRST_FIRST_FOO, FIRST_FIRST_PKG_PRIVATE);
private static List<Class<?>> getTestClasses() {
return ImmutableList.<Class<?>>builder()
@@ -84,11 +87,7 @@
.addProgramClasses(getTestClasses())
.addFeatureSplit(getFeatureClasses().toArray(new Class<?>[0]))
.addKeepMainRule(TestClass.class)
- .addKeepClassAndMembersRules(
- com.android.tools.r8.repackage.testclasses.repackagefeaturewithsynthetics.first
- .PkgProtectedMethod.class,
- com.android.tools.r8.repackage.testclasses.repackagefeaturewithsynthetics.first
- .first.PkgProtectedMethod.class)
+ .addKeepClassAndMembersRules(FIRST_PKG_PRIVATE, FIRST_FIRST_PKG_PRIVATE)
.addKeepClassAndMembersRules(I.class)
.addKeepMethodRules(
Reference.methodFromMethod(TestClass.class.getDeclaredMethod("run", I.class)))
@@ -106,20 +105,21 @@
// Check that the first.Foo is repackaged but that the pkg private access class is not.
// The implies that the lambda which is doing a package private access cannot be repackaged.
// If it is, the access will fail resulting in a runtime exception.
- CodeInspector baseInspector = compileResult.inspector();
- assertThat(FIRST_FOO, isRepackagedAsExpected(baseInspector, "first"));
- assertThat(FIRST_PKG_PRIVATE, isNotRepackaged(baseInspector));
- assertEquals(
- getTestClasses().size() + expectedSyntheticsInBase, baseInspector.allClasses().size());
-
- // The feature first.first.Foo is not repackaged and neither is the lambda.
- // TODO(b/180086194): first.first.Foo should have been repackaged, but is currently identified
- // as being in 'base' when inlining takes place.
- CodeInspector featureInspector = new CodeInspector(compileResult.getFeature(0));
- getFeatureClasses().forEach(c -> assertThat(c, isNotRepackaged(featureInspector)));
- assertEquals(
- getFeatureClasses().size() + expectedSyntheticsInFeature,
- featureInspector.allClasses().size());
+ compileResult.inspect(
+ baseInspector -> {
+ assertThat(FIRST_FOO, isRepackagedAsExpected(baseInspector, "first"));
+ assertThat(FIRST_PKG_PRIVATE, isNotRepackaged(baseInspector));
+ assertEquals(
+ getTestClasses().size() + expectedSyntheticsInBase,
+ baseInspector.allClasses().size());
+ },
+ featureInspector -> {
+ assertThat(FIRST_FIRST_FOO, isRepackagedAsExpected(featureInspector, "first$1"));
+ assertThat(FIRST_FIRST_PKG_PRIVATE, isNotRepackaged(featureInspector));
+ assertEquals(
+ getFeatureClasses().size() + expectedSyntheticsInFeature,
+ featureInspector.allClasses().size());
+ });
compileResult
.addFeatureSplitsToRunClasspathFiles()