Replace DexDefinition in RootSet with DexReference.
This CL replaces definition uses in the root set with references. Also,
revisited root set uses and adjusted membership checks, reference
rewriting, and assertions accordingly.
This CL also introduces lense-based root set rewriting, along with many
utils. For now, only map applier needs to rewrite root set directly.
For desugared interface methods, verification step filters out them,
but ideally, it is better to correct them while desugaring.
Bug: 121295633, 112847660, 120263141, 121240523
Change-Id: I11cdd6c19ba6fab48bf4ef5d81b4c64618467978
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 14dd1ae..aafe941 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexDefinition;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.shaking.DiscardedChecker;
import com.android.tools.r8.shaking.Enqueuer;
@@ -80,9 +80,9 @@
}
// Print -whyareyoukeeping results if any.
if (whyAreYouKeepingConsumer != null) {
- for (DexDefinition definition : mainDexRootSet.reasonAsked) {
+ for (DexReference reference : mainDexRootSet.reasonAsked) {
whyAreYouKeepingConsumer.printWhyAreYouKeeping(
- enqueuer.getGraphNode(definition), System.out);
+ enqueuer.getGraphNode(reference), System.out);
}
}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 75b0c81..186caee 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -17,8 +17,8 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexDefinition;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.conversion.IRConverter;
@@ -403,8 +403,10 @@
SeedMapper.seedMapperFromFile(
options.getProguardConfiguration().getApplyMappingFile());
timing.begin("apply-mapping");
- appView.setGraphLense(
- new ProguardMapApplier(appView.withLiveness(), seedMapper).run(timing));
+ GraphLense applyMappingLense =
+ new ProguardMapApplier(appView.withLiveness(), seedMapper).run(timing);
+ rootSet = rootSet.rewrittenWithLense(applyMappingLense);
+ appView.setGraphLense(applyMappingLense);
application = application.asDirect().rewrittenWithLense(appView.graphLense());
appView.setAppInfo(
appView
@@ -490,6 +492,7 @@
// At this point all code has been mapped according to the graph lens. We cannot remove the
// graph lens entirely, though, since it is needed for mapping all field and method signatures
// back to the original program.
+ GraphLense finalLense = appView.graphLense();
timing.begin("AppliedGraphLens construction");
appView.setGraphLense(new AppliedGraphLens(appView, application.classes()));
timing.end();
@@ -536,9 +539,9 @@
.run();
}
if (whyAreYouKeepingConsumer != null) {
- for (DexDefinition dexDefinition : mainDexRootSet.reasonAsked) {
+ for (DexReference reference : mainDexRootSet.reasonAsked) {
whyAreYouKeepingConsumer.printWhyAreYouKeeping(
- enqueuer.getGraphNode(dexDefinition), System.out);
+ enqueuer.getGraphNode(reference), System.out);
}
}
}
@@ -578,9 +581,9 @@
// Print reasons on the application after pruning, so that we reflect the actual result.
if (whyAreYouKeepingConsumer != null) {
- for (DexDefinition dexDefinition : rootSet.reasonAsked) {
+ for (DexReference reference : rootSet.reasonAsked) {
whyAreYouKeepingConsumer.printWhyAreYouKeeping(
- enqueuer.getGraphNode(dexDefinition), System.out);
+ enqueuer.getGraphNode(reference), System.out);
}
}
// Remove annotations that refer to types that no longer exist.
@@ -641,7 +644,8 @@
// Validity checks.
assert application.classes().stream().allMatch(DexClass::isValid);
- assert rootSet.verifyKeptItemsAreKept(application, appView.appInfo(), options);
+ // Use the final lense while checking the validity of the final app against the root set.
+ assert rootSet.verifyKeptItemsAreKept(application, appView.appInfo(), finalLense, options);
assert appView
.graphLense()
.verifyMappingToOriginalProgram(
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index c9edb88..028f5a2 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -66,6 +66,17 @@
return app.classesWithDeterministicOrder();
}
+ public DexDefinition definitionFor(DexReference reference) {
+ if (reference.isDexType()) {
+ return definitionFor(reference.asDexType());
+ }
+ if (reference.isDexMethod()) {
+ return definitionFor(reference.asDexMethod());
+ }
+ assert reference.isDexField();
+ return definitionFor(reference.asDexField());
+ }
+
public DexClass definitionFor(DexType type) {
return app.definitionFor(type);
}
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 11d9730..bac74f6 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -10,6 +10,8 @@
import com.android.tools.r8.utils.IteratorUtils;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Sets;
@@ -25,6 +27,9 @@
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.function.Function;
/**
* A GraphLense implements a virtual view on top of the graph, used to delay global rewrites until
@@ -494,6 +499,23 @@
return true;
}
+ public ImmutableList<DexReference> rewriteReferencesConservatively(List<DexReference> original) {
+ ImmutableList.Builder<DexReference> builder = ImmutableList.builder();
+ for (DexReference item : original) {
+ if (item.isDexMethod()) {
+ DexMethod method = item.asDexMethod();
+ if (isContextFreeForMethod(method)) {
+ builder.add(lookupMethod(method));
+ } else {
+ builder.addAll(lookupMethodInAllContexts(method));
+ }
+ } else {
+ builder.add(lookupReference(item));
+ }
+ }
+ return builder.build();
+ }
+
public ImmutableSet<DexReference> rewriteReferencesConservatively(Set<DexReference> original) {
ImmutableSet.Builder<DexReference> builder = ImmutableSet.builder();
for (DexReference item : original) {
@@ -511,6 +533,23 @@
return builder.build();
}
+ public Set<DexReference> rewriteMutableReferencesConservatively(Set<DexReference> original) {
+ Set<DexReference> result = Sets.newIdentityHashSet();
+ for (DexReference item : original) {
+ if (item.isDexMethod()) {
+ DexMethod method = item.asDexMethod();
+ if (isContextFreeForMethod(method)) {
+ result.add(lookupMethod(method));
+ } else {
+ result.addAll(lookupMethodInAllContexts(method));
+ }
+ } else {
+ result.add(lookupReference(item));
+ }
+ }
+ return result;
+ }
+
public Object2BooleanMap<DexReference> rewriteReferencesConservatively(
Object2BooleanMap<DexReference> original) {
Object2BooleanMap<DexReference> result = new Object2BooleanArrayMap<>();
@@ -532,6 +571,22 @@
return result;
}
+ public ImmutableSet<DexType> rewriteTypesConservatively(Set<DexType> original) {
+ ImmutableSet.Builder<DexType> builder = ImmutableSet.builder();
+ for (DexType item : original) {
+ builder.add(lookupType(item));
+ }
+ return builder.build();
+ }
+
+ public Set<DexType> rewriteMutableTypesConservatively(Set<DexType> original) {
+ Set<DexType> result = Sets.newIdentityHashSet();
+ for (DexType item : original) {
+ result.add(lookupType(item));
+ }
+ return result;
+ }
+
public ImmutableSortedSet<DexMethod> rewriteMethodsWithRenamedSignature(Set<DexMethod> methods) {
ImmutableSortedSet.Builder<DexMethod> builder =
new ImmutableSortedSet.Builder<>(PresortedComparable::slowCompare);
@@ -563,6 +618,45 @@
return builder.build();
}
+ public SortedSet<DexMethod> rewriteMutableMethodsConservatively(Set<DexMethod> original) {
+ SortedSet<DexMethod> result = new TreeSet<>(PresortedComparable::slowCompare);
+ if (isContextFreeForMethods()) {
+ for (DexMethod item : original) {
+ result.add(lookupMethod(item));
+ }
+ } else {
+ for (DexMethod item : original) {
+ // Avoid using lookupMethodInAllContexts when possible.
+ if (isContextFreeForMethod(item)) {
+ result.add(lookupMethod(item));
+ } else {
+ // The lense is context sensitive, but we do not have the context here. Therefore, we
+ // conservatively look up the method in all contexts.
+ result.addAll(lookupMethodInAllContexts(item));
+ }
+ }
+ }
+ return result;
+ }
+
+ public static <T extends DexReference, S> ImmutableMap<T, S> rewriteReferenceKeys(
+ Map<T, S> original, Function<T, T> rewrite) {
+ ImmutableMap.Builder<T, S> builder = new ImmutableMap.Builder<>();
+ for (T item : original.keySet()) {
+ builder.put(rewrite.apply(item), original.get(item));
+ }
+ return builder.build();
+ }
+
+ public static <T extends DexReference, S> Map<T, S> rewriteMutableReferenceKeys(
+ Map<T, S> original, Function<T, T> rewrite) {
+ Map<T, S> result = new IdentityHashMap<>();
+ for (T item : original.keySet()) {
+ result.put(rewrite.apply(item), original.get(item));
+ }
+ return result;
+ }
+
public boolean verifyMappingToOriginalProgram(
Iterable<DexProgramClass> classes,
DexApplication originalApplication,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index fbdb9f3..0af7c9e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1905,7 +1905,7 @@
String name = null;
if (invokedMethod == appInfo.dexItemFactory.classMethods.getName) {
if (code.options.enableMinification
- && !converter.rootSet.noObfuscation.contains(holder)) {
+ && !converter.rootSet.noObfuscation.contains(holderType)) {
deferred = new DexItemBasedValueString(
holderType, new ClassNameComputationInfo(NAME));
} else {
@@ -1915,7 +1915,7 @@
// TODO(b/119426668): desugar Type#getTypeName
} else if (invokedMethod == appInfo.dexItemFactory.classMethods.getCanonicalName) {
if (code.options.enableMinification
- && !converter.rootSet.noObfuscation.contains(holder)) {
+ && !converter.rootSet.noObfuscation.contains(holderType)) {
deferred = new DexItemBasedValueString(
holderType, new ClassNameComputationInfo(CANONICAL_NAME));
} else {
@@ -1923,7 +1923,7 @@
}
} else if (invokedMethod == appInfo.dexItemFactory.classMethods.getSimpleName) {
if (code.options.enableMinification
- && !converter.rootSet.noObfuscation.contains(holder)) {
+ && !converter.rootSet.noObfuscation.contains(holderType)) {
deferred = new DexItemBasedValueString(
holderType, new ClassNameComputationInfo(SIMPLE_NAME));
} else {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index ad3fb38..3deeb63 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -6,13 +6,14 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinition;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer;
import com.android.tools.r8.graph.DexEncodedMethod.TrivialInitializer.TrivialClassInitializer;
import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -59,12 +60,16 @@
this.appInfo = appInfo;
}
- private ProguardMemberRuleLookup lookupMemberRule(DexItem item) {
- ProguardMemberRule rule = appInfo.noSideEffects.get(item);
+ private ProguardMemberRuleLookup lookupMemberRule(DexDefinition definition) {
+ if (definition == null) {
+ return null;
+ }
+ DexReference reference = definition.toReference();
+ ProguardMemberRule rule = appInfo.noSideEffects.get(reference);
if (rule != null) {
return new ProguardMemberRuleLookup(RuleType.ASSUME_NO_SIDE_EFFECTS, rule);
}
- rule = appInfo.assumedValues.get(item);
+ rule = appInfo.assumedValues.get(reference);
if (rule != null) {
return new ProguardMemberRuleLookup(RuleType.ASSUME_VALUES, rule);
}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index b02801c..e53f959 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -87,12 +87,10 @@
this.classDictionary = options.getProguardConfiguration().getClassObfuscationDictionary();
this.keepInnerClassStructure = options.getProguardConfiguration().getKeepAttributes().signature;
this.noObfuscationTypes =
- DexReference.filterDexType(
- DexDefinition.mapToReference(rootSet.noObfuscation.stream()))
+ DexReference.filterDexType(rootSet.noObfuscation.stream())
.collect(Collectors.toSet());
this.keepPackageName =
- DexReference.filterDexType(
- DexDefinition.mapToReference(rootSet.keepPackageName.stream()))
+ DexReference.filterDexType(rootSet.keepPackageName.stream())
.collect(Collectors.toSet());
// Initialize top-level naming state.
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index 3b546a8..f00787b 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -67,8 +67,8 @@
DexEncodedField encodedField,
NamingState<DexType, ?> state,
boolean isLibrary) {
- if (isLibrary || rootSet.noObfuscation.contains(encodedField)) {
- DexField field = encodedField.field;
+ DexField field = encodedField.field;
+ if (isLibrary || rootSet.noObfuscation.contains(field)) {
state.reserveName(field.name, field.type);
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index df11ba9..5f990ac 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -479,11 +479,12 @@
return state;
}
- private void reserveNamesForMethod(DexEncodedMethod method,
- boolean keepAll, NamingState<DexProto, ?> state) {
+ private void reserveNamesForMethod(
+ DexEncodedMethod encodedMethod, boolean keepAll, NamingState<DexProto, ?> state) {
+ DexMethod method = encodedMethod.method;
if (keepAll || rootSet.noObfuscation.contains(method)) {
- state.reserveName(method.method.name, method.method.proto);
- globalState.reserveName(method.method.name, method.method.proto);
+ state.reserveName(method.name, method.proto);
+ globalState.reserveName(method.name, method.proto);
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java b/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
index b28a1a7..07a62a8 100644
--- a/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
+++ b/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinition;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.InternalOptions;
@@ -19,7 +20,7 @@
public class DiscardedChecker {
- private final Set<DexDefinition> checkDiscarded;
+ private final Set<DexReference> checkDiscarded;
private final List<DexProgramClass> classes;
private boolean fail = false;
private final InternalOptions options;
@@ -55,7 +56,7 @@
}
private void checkItem(DexDefinition item) {
- if (checkDiscarded.contains(item)) {
+ if (checkDiscarded.contains(item.toReference())) {
options.reporter.info(
new StringDiagnostic("Item " + item.toSourceString() + " was not discarded."));
fail = true;
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 0761a71..13dd3ea 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.graph.GraphLense.rewriteReferenceKeys;
import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentifier;
import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
import static com.android.tools.r8.shaking.AnnotationRemover.shouldKeepAnnotation;
@@ -63,7 +64,6 @@
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
@@ -297,22 +297,21 @@
this.options = options;
}
- private void enqueueRootItems(Map<DexDefinition, Set<ProguardKeepRule>> items) {
+ private void enqueueRootItems(Map<DexReference, Set<ProguardKeepRule>> items) {
items.entrySet().forEach(this::enqueueRootItem);
}
- private void enqueueRootItem(Entry<DexDefinition, Set<ProguardKeepRule>> root) {
- enqueueRootItem(root.getKey(), root.getValue());
- }
-
- private void enqueueRootItem(DexDefinition item, ProguardKeepRule rule) {
- enqueueRootItem(item, KeepReason.dueToKeepRule(rule));
+ private void enqueueRootItem(Entry<DexReference, Set<ProguardKeepRule>> root) {
+ DexDefinition item = appInfo.definitionFor(root.getKey());
+ if (item != null) {
+ enqueueRootItem(item, root.getValue());
+ }
}
private void enqueueRootItem(DexDefinition item, Set<ProguardKeepRule> rules) {
assert !rules.isEmpty();
if (keptGraphConsumer != null) {
- GraphNode node = getGraphNode(item);
+ GraphNode node = getGraphNode(item.toReference());
for (ProguardKeepRule rule : rules) {
registerEdge(node, KeepReason.dueToKeepRule(rule));
}
@@ -322,7 +321,7 @@
private void enqueueRootItem(DexDefinition item, KeepReason reason) {
if (keptGraphConsumer != null) {
- registerEdge(getGraphNode(item), reason);
+ registerEdge(getGraphNode(item.toReference()), reason);
}
internalEnqueueRootItem(item, reason);
}
@@ -368,16 +367,17 @@
}
private void enqueueHolderIfDependentNonStaticMember(
- DexClass holder, Map<DexDefinition, Set<ProguardKeepRule>> dependentItems) {
+ DexClass holder, Map<DexReference, Set<ProguardKeepRule>> dependentItems) {
// Check if any dependent members are not static, and in that case enqueue the class as well.
// Having a dependent rule like -keepclassmembers with non static items indicates that class
// instances will be present even if tracing do not find any instantiation. See b/115867670.
- for (Entry<DexDefinition, Set<ProguardKeepRule>> entry : dependentItems.entrySet()) {
- DexDefinition dependentItem = entry.getKey();
- if (dependentItem.isDexClass()) {
+ for (Entry<DexReference, Set<ProguardKeepRule>> entry : dependentItems.entrySet()) {
+ DexReference dependentItem = entry.getKey();
+ if (dependentItem.isDexType()) {
continue;
}
- if (!dependentItem.isStaticMember()) {
+ DexDefinition dependentDefinition = appInfo.definitionFor(dependentItem);
+ if (!dependentDefinition.isStaticMember()) {
enqueueRootItem(holder, entry.getValue());
// Enough to enqueue the known holder once.
break;
@@ -810,7 +810,7 @@
annotations.forEach(this::handleAnnotationOfLiveType);
}
- Map<DexDefinition, Set<ProguardKeepRule>> dependentItems = rootSet.getDependentItems(holder);
+ Map<DexReference, Set<ProguardKeepRule>> dependentItems = rootSet.getDependentItems(holder);
enqueueHolderIfDependentNonStaticMember(holder, dependentItems);
// Add all dependent members to the workqueue.
enqueueRootItems(dependentItems);
@@ -1325,7 +1325,7 @@
// TODO(sgjesse): Does this have to be enqueued as a root item? Right now it is done as the
// marking of not renaming is in the root set.
enqueueRootItem(valuesMethod, reason);
- rootSet.noObfuscation.add(valuesMethod);
+ rootSet.noObfuscation.add(valuesMethod.toReference());
}
}
@@ -1465,17 +1465,14 @@
targetedMethods.getItems(),
executorService);
ConsequentRootSet consequentRootSet = ifRuleEvaluator.run(liveTypes);
+ rootSet.addConsequentRootSet(consequentRootSet);
enqueueRootItems(consequentRootSet.noShrinking);
- rootSet.neverInline.addAll(consequentRootSet.neverInline);
- rootSet.neverClassInline.addAll(consequentRootSet.neverClassInline);
- rootSet.noOptimization.addAll(consequentRootSet.noOptimization);
- rootSet.noObfuscation.addAll(consequentRootSet.noObfuscation);
- rootSet.addDependentItems(consequentRootSet.dependentNoShrinking);
// Check if any newly dependent members are not static, and in that case find the holder
// and enqueue it as well. This is -if version of workaround for b/115867670.
consequentRootSet.dependentNoShrinking.forEach((precondition, dependentItems) -> {
- if (precondition.isDexClass()) {
- enqueueHolderIfDependentNonStaticMember(precondition.asDexClass(), dependentItems);
+ if (precondition.isDexType()) {
+ DexClass preconditionHolder = appInfo.definitionFor(precondition.asDexType());
+ enqueueHolderIfDependentNonStaticMember(preconditionHolder, dependentItems);
}
// Add all dependent members to the workqueue.
enqueueRootItems(dependentItems);
@@ -1948,11 +1945,11 @@
/**
* All items with assumenosideeffects rule.
*/
- public final Map<DexDefinition, ProguardMemberRule> noSideEffects;
+ public final Map<DexReference, ProguardMemberRule> noSideEffects;
/**
* All items with assumevalues rule.
*/
- public final Map<DexDefinition, ProguardMemberRule> assumedValues;
+ public final Map<DexReference, ProguardMemberRule> assumedValues;
/**
* All methods that should be inlined if possible due to a configuration directive.
*/
@@ -1965,9 +1962,13 @@
* All methods that *must* never be inlined due to a configuration directive (testing only).
*/
public final Set<DexMethod> neverInline;
- /** All methods that may not have any parameters with a constant value removed. */
+ /**
+ * All methods that may not have any parameters with a constant value removed.
+ */
public final Set<DexMethod> keepConstantArguments;
- /** All methods that may not have any unused arguments removed. */
+ /**
+ * All methods that may not have any unused arguments removed.
+ */
public final Set<DexMethod> keepUnusedArguments;
/**
* All types that *must* never be inlined due to a configuration directive (testing only).
@@ -2143,16 +2144,14 @@
this.callSites = previous.callSites;
this.brokenSuperInvokes = lense.rewriteMethodsConservatively(previous.brokenSuperInvokes);
this.prunedTypes = rewriteItems(previous.prunedTypes, lense::lookupType);
- assert lense.assertDefinitionsNotModified(previous.noSideEffects.keySet());
- this.noSideEffects = previous.noSideEffects;
- assert lense.assertDefinitionsNotModified(previous.assumedValues.keySet());
- this.assumedValues = previous.assumedValues;
+ this.noSideEffects = rewriteReferenceKeys(previous.noSideEffects, lense::lookupReference);
+ this.assumedValues = rewriteReferenceKeys(previous.assumedValues, lense::lookupReference);
assert lense.assertDefinitionsNotModified(
previous.alwaysInline.stream()
.map(this::definitionFor)
.filter(Objects::nonNull)
.collect(Collectors.toList()));
- this.alwaysInline = previous.alwaysInline;
+ this.alwaysInline = lense.rewriteMethodsWithRenamedSignature(previous.alwaysInline);
this.forceInline = lense.rewriteMethodsWithRenamedSignature(previous.forceInline);
this.neverInline = lense.rewriteMethodsWithRenamedSignature(previous.neverInline);
this.keepConstantArguments =
@@ -2165,7 +2164,7 @@
.filter(Objects::nonNull)
.collect(Collectors.toList()));
this.neverClassInline = rewriteItems(previous.neverClassInline, lense::lookupType);
- this.neverMerge = previous.neverMerge;
+ this.neverMerge = rewriteItems(previous.neverMerge, lense::lookupType);
this.identifierNameStrings =
lense.rewriteReferencesConservatively(previous.identifierNameStrings);
// Switchmap classes should never be affected by renaming.
@@ -2174,8 +2173,8 @@
.map(this::definitionFor)
.filter(Objects::nonNull)
.collect(Collectors.toList()));
- this.switchMaps = previous.switchMaps;
- this.ordinalsMaps = rewriteKeys(previous.ordinalsMaps, lense::lookupType);
+ this.switchMaps = rewriteReferenceKeys(previous.switchMaps, lense::lookupField);
+ this.ordinalsMaps = rewriteReferenceKeys(previous.ordinalsMaps, lense::lookupType);
// Sanity check sets after rewriting.
assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
assert Sets.intersection(instanceFieldWrites.keySet(), staticFieldWrites.keySet()).isEmpty();
@@ -2318,14 +2317,6 @@
return builder.build();
}
- private static <T extends PresortedComparable<T>, S> ImmutableMap<T, S> rewriteKeys(
- Map<T, S> original, Function<T, T> rewrite) {
- ImmutableMap.Builder<T, S> builder = new ImmutableMap.Builder<>();
- for (T item : original.keySet()) {
- builder.put(rewrite.apply(item), original.get(item));
- }
- return builder.build();
- }
private static <T extends PresortedComparable<T>, S>
Map<T, Set<S>> rewriteKeysWhileMergingValues(
@@ -2820,15 +2811,15 @@
return reason.getSourceNode(this);
}
- public GraphNode getGraphNode(DexDefinition item) {
- if (item instanceof DexClass) {
- return getClassGraphNode(((DexClass) item).type);
+ public GraphNode getGraphNode(DexReference reference) {
+ if (reference.isDexType()) {
+ return getClassGraphNode(reference.asDexType());
}
- if (item instanceof DexEncodedMethod) {
- return getMethodGraphNode(((DexEncodedMethod) item).method);
+ if (reference.isDexMethod()) {
+ return getMethodGraphNode(reference.asDexMethod());
}
- if (item instanceof DexEncodedField) {
- return getFieldGraphNode(((DexEncodedField) item).field);
+ if (reference.isDexField()) {
+ return getFieldGraphNode(reference.asDexField());
}
throw new Unreachable();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 203cf82..43c39a6 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.graph.GraphLense.rewriteMutableReferenceKeys;
+import static com.android.tools.r8.graph.GraphLense.rewriteReferenceKeys;
+
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
@@ -22,6 +25,7 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
@@ -53,6 +57,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
+import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -61,14 +66,14 @@
private final AppView<? extends AppInfo> appView;
private final DirectMappedDexApplication application;
private final Iterable<? extends ProguardConfigurationRule> rules;
- private final Map<DexDefinition, Set<ProguardKeepRule>> noShrinking = new IdentityHashMap<>();
- private final Set<DexDefinition> noOptimization = Sets.newIdentityHashSet();
- private final Set<DexDefinition> noObfuscation = Sets.newIdentityHashSet();
- private final LinkedHashMap<DexDefinition, DexDefinition> reasonAsked = new LinkedHashMap<>();
- private final Set<DexDefinition> keepPackageName = Sets.newIdentityHashSet();
+ private final Map<DexReference, Set<ProguardKeepRule>> noShrinking = new IdentityHashMap<>();
+ private final Set<DexReference> noOptimization = Sets.newIdentityHashSet();
+ private final Set<DexReference> noObfuscation = Sets.newIdentityHashSet();
+ private final LinkedHashMap<DexReference, DexReference> reasonAsked = new LinkedHashMap<>();
+ private final Set<DexReference> keepPackageName = Sets.newIdentityHashSet();
private final Set<ProguardConfigurationRule> rulesThatUseExtendsOrImplementsWrong =
Sets.newIdentityHashSet();
- private final Set<DexDefinition> checkDiscarded = Sets.newIdentityHashSet();
+ private final Set<DexReference> checkDiscarded = Sets.newIdentityHashSet();
private final Set<DexMethod> alwaysInline = Sets.newIdentityHashSet();
private final Set<DexMethod> forceInline = Sets.newIdentityHashSet();
private final Set<DexMethod> neverInline = Sets.newIdentityHashSet();
@@ -76,10 +81,10 @@
private final Set<DexMethod> keepUnusedArguments = Sets.newIdentityHashSet();
private final Set<DexType> neverClassInline = Sets.newIdentityHashSet();
private final Set<DexType> neverMerge = Sets.newIdentityHashSet();
- private final Map<DexDefinition, Map<DexDefinition, Set<ProguardKeepRule>>> dependentNoShrinking =
+ private final Map<DexReference, Map<DexReference, Set<ProguardKeepRule>>> dependentNoShrinking =
new IdentityHashMap<>();
- private final Map<DexDefinition, ProguardMemberRule> noSideEffects = new IdentityHashMap<>();
- private final Map<DexDefinition, ProguardMemberRule> assumedValues = new IdentityHashMap<>();
+ private final Map<DexReference, ProguardMemberRule> noSideEffects = new IdentityHashMap<>();
+ private final Map<DexReference, ProguardMemberRule> assumedValues = new IdentityHashMap<>();
private final Set<DexReference> identifierNameStrings = Sets.newIdentityHashSet();
private final InternalOptions options;
@@ -863,11 +868,11 @@
}
// Keep the type if the item is also kept.
dependentNoShrinking
- .computeIfAbsent(item, x -> new IdentityHashMap<>())
- .computeIfAbsent(definition, k -> new HashSet<>())
+ .computeIfAbsent(item.toReference(), x -> new IdentityHashMap<>())
+ .computeIfAbsent(type, k -> new HashSet<>())
.add(context);
// Unconditionally add to no-obfuscation, as that is only checked for surviving items.
- noObfuscation.add(definition);
+ noObfuscation.add(type);
}
private void includeDescriptorClasses(DexDefinition item, ProguardKeepRule context) {
@@ -903,32 +908,32 @@
if (!modifiers.allowsShrinking) {
if (precondition != null) {
dependentNoShrinking
- .computeIfAbsent(precondition, x -> new IdentityHashMap<>())
- .computeIfAbsent(item, i -> new HashSet<>())
+ .computeIfAbsent(precondition.toReference(), x -> new IdentityHashMap<>())
+ .computeIfAbsent(item.toReference(), i -> new HashSet<>())
.add(keepRule);
} else {
- noShrinking.computeIfAbsent(item, i -> new HashSet<>()).add(keepRule);
+ noShrinking.computeIfAbsent(item.toReference(), i -> new HashSet<>()).add(keepRule);
}
}
if (!modifiers.allowsOptimization) {
- noOptimization.add(item);
+ noOptimization.add(item.toReference());
}
if (!modifiers.allowsObfuscation) {
- noObfuscation.add(item);
+ noObfuscation.add(item.toReference());
}
if (modifiers.includeDescriptorClasses) {
includeDescriptorClasses(item, keepRule);
}
} else if (context instanceof ProguardAssumeNoSideEffectRule) {
- noSideEffects.put(item, rule);
+ noSideEffects.put(item.toReference(), rule);
} else if (context instanceof ProguardWhyAreYouKeepingRule) {
- reasonAsked.computeIfAbsent(item, i -> i);
+ reasonAsked.computeIfAbsent(item.toReference(), i -> i);
} else if (context instanceof ProguardKeepPackageNamesRule) {
- keepPackageName.add(item);
+ keepPackageName.add(item.toReference());
} else if (context instanceof ProguardAssumeValuesRule) {
- assumedValues.put(item, rule);
+ assumedValues.put(item.toReference(), rule);
} else if (context instanceof ProguardCheckDiscardRule) {
- checkDiscarded.add(item);
+ checkDiscarded.add(item.toReference());
} else if (context instanceof InlineRule) {
if (item.isDexEncodedMethod()) {
switch (((InlineRule) context).getType()) {
@@ -984,12 +989,12 @@
public static class RootSet {
- public final Map<DexDefinition, Set<ProguardKeepRule>> noShrinking;
- public final Set<DexDefinition> noOptimization;
- public final Set<DexDefinition> noObfuscation;
- public final ImmutableList<DexDefinition> reasonAsked;
- public final Set<DexDefinition> keepPackageName;
- public final Set<DexDefinition> checkDiscarded;
+ public final Map<DexReference, Set<ProguardKeepRule>> noShrinking;
+ public final Set<DexReference> noOptimization;
+ public final Set<DexReference> noObfuscation;
+ public final ImmutableList<DexReference> reasonAsked;
+ public final Set<DexReference> keepPackageName;
+ public final Set<DexReference> checkDiscarded;
public final Set<DexMethod> alwaysInline;
public final Set<DexMethod> forceInline;
public final Set<DexMethod> neverInline;
@@ -997,20 +1002,20 @@
public final Set<DexMethod> keepUnusedArguments;
public final Set<DexType> neverClassInline;
public final Set<DexType> neverMerge;
- public final Map<DexDefinition, ProguardMemberRule> noSideEffects;
- public final Map<DexDefinition, ProguardMemberRule> assumedValues;
- private final Map<DexDefinition, Map<DexDefinition, Set<ProguardKeepRule>>>
+ public final Map<DexReference, ProguardMemberRule> noSideEffects;
+ public final Map<DexReference, ProguardMemberRule> assumedValues;
+ private final Map<DexReference, Map<DexReference, Set<ProguardKeepRule>>>
dependentNoShrinking;
public final Set<DexReference> identifierNameStrings;
public final Set<ProguardIfRule> ifRules;
private RootSet(
- Map<DexDefinition, Set<ProguardKeepRule>> noShrinking,
- Set<DexDefinition> noOptimization,
- Set<DexDefinition> noObfuscation,
- ImmutableList<DexDefinition> reasonAsked,
- Set<DexDefinition> keepPackageName,
- Set<DexDefinition> checkDiscarded,
+ Map<DexReference, Set<ProguardKeepRule>> noShrinking,
+ Set<DexReference> noOptimization,
+ Set<DexReference> noObfuscation,
+ ImmutableList<DexReference> reasonAsked,
+ Set<DexReference> keepPackageName,
+ Set<DexReference> checkDiscarded,
Set<DexMethod> alwaysInline,
Set<DexMethod> forceInline,
Set<DexMethod> neverInline,
@@ -1018,12 +1023,12 @@
Set<DexMethod> keepUnusedArguments,
Set<DexType> neverClassInline,
Set<DexType> neverMerge,
- Map<DexDefinition, ProguardMemberRule> noSideEffects,
- Map<DexDefinition, ProguardMemberRule> assumedValues,
- Map<DexDefinition, Map<DexDefinition, Set<ProguardKeepRule>>> dependentNoShrinking,
+ Map<DexReference, ProguardMemberRule> noSideEffects,
+ Map<DexReference, ProguardMemberRule> assumedValues,
+ Map<DexReference, Map<DexReference, Set<ProguardKeepRule>>> dependentNoShrinking,
Set<DexReference> identifierNameStrings,
Set<ProguardIfRule> ifRules) {
- this.noShrinking = Collections.unmodifiableMap(noShrinking);
+ this.noShrinking = noShrinking;
this.noOptimization = noOptimization;
this.noObfuscation = noObfuscation;
this.reasonAsked = reasonAsked;
@@ -1043,33 +1048,81 @@
this.ifRules = Collections.unmodifiableSet(ifRules);
}
+ private RootSet(RootSet previous, GraphLense lense) {
+ this.noShrinking = rewriteMutableReferenceKeys(previous.noShrinking, lense::lookupReference);
+ this.noOptimization = lense.rewriteMutableReferencesConservatively(previous.noOptimization);
+ this.noObfuscation = lense.rewriteMutableReferencesConservatively(previous.noObfuscation);
+ this.reasonAsked = lense.rewriteReferencesConservatively(previous.reasonAsked);
+ this.keepPackageName = lense.rewriteReferencesConservatively(previous.keepPackageName);
+ this.checkDiscarded = lense.rewriteReferencesConservatively(previous.checkDiscarded);
+ this.alwaysInline = lense.rewriteMethodsConservatively(previous.alwaysInline);
+ this.forceInline = lense.rewriteMethodsConservatively(previous.forceInline);
+ this.neverInline = lense.rewriteMutableMethodsConservatively(previous.neverInline);
+ this.keepConstantArguments =
+ lense.rewriteMutableMethodsConservatively(previous.keepConstantArguments);
+ this.keepUnusedArguments =
+ lense.rewriteMutableMethodsConservatively(previous.keepUnusedArguments);
+ this.neverClassInline = lense.rewriteMutableTypesConservatively(previous.neverClassInline);
+ this.neverMerge = lense.rewriteTypesConservatively(previous.neverMerge);
+ this.noSideEffects = rewriteReferenceKeys(previous.noSideEffects, lense::lookupReference);
+ this.assumedValues = rewriteReferenceKeys(previous.assumedValues, lense::lookupReference);
+ this.dependentNoShrinking =
+ rewriteDependentReferenceKeys(previous.dependentNoShrinking, lense::lookupReference);
+ this.identifierNameStrings =
+ lense.rewriteReferencesConservatively(previous.identifierNameStrings);
+ this.ifRules = Collections.unmodifiableSet(previous.ifRules);
+ }
+
+ public RootSet rewrittenWithLense(GraphLense lense) {
+ return new RootSet(this, lense);
+ }
+
+ private static <T extends DexReference, S> Map<T, Map<T, S>> rewriteDependentReferenceKeys(
+ Map<T, Map<T, S>> original, Function<T, T> rewrite) {
+ Map<T, Map<T, S>> result = new IdentityHashMap<>();
+ for (T item : original.keySet()) {
+ result.put(rewrite.apply(item), rewriteReferenceKeys(original.get(item), rewrite));
+ }
+ return result;
+ }
+
+ void addConsequentRootSet(ConsequentRootSet consequentRootSet) {
+ neverInline.addAll(consequentRootSet.neverInline);
+ neverClassInline.addAll(consequentRootSet.neverClassInline);
+ noOptimization.addAll(consequentRootSet.noOptimization);
+ noObfuscation.addAll(consequentRootSet.noObfuscation);
+ addDependentItems(consequentRootSet.dependentNoShrinking);
+ }
+
// Add dependent items that depend on -if rules.
- void addDependentItems(
- Map<DexDefinition, Map<DexDefinition, Set<ProguardKeepRule>>> dependentItems) {
+ private void addDependentItems(
+ Map<DexReference, Map<DexReference, Set<ProguardKeepRule>>> dependentItems) {
dependentItems.forEach(
- (def, dependence) ->
+ (reference, dependence) ->
dependentNoShrinking
- .computeIfAbsent(def, x -> new IdentityHashMap<>())
+ .computeIfAbsent(reference, x -> new IdentityHashMap<>())
.putAll(dependence));
}
- Map<DexDefinition, Set<ProguardKeepRule>> getDependentItems(DexDefinition item) {
- return Collections
- .unmodifiableMap(dependentNoShrinking.getOrDefault(item, Collections.emptyMap()));
+ Map<DexReference, Set<ProguardKeepRule>> getDependentItems(DexDefinition item) {
+ return Collections.unmodifiableMap(
+ dependentNoShrinking.getOrDefault(item.toReference(), Collections.emptyMap()));
}
public boolean verifyKeptFieldsAreAccessedAndLive(AppInfoWithLiveness appInfo) {
- for (DexDefinition definition : noShrinking.keySet()) {
- if (definition.isDexEncodedField()) {
- DexEncodedField field = definition.asDexEncodedField();
- if (field.isStatic() || isKeptDirectlyOrIndirectly(field.field.clazz, appInfo)) {
+ for (DexReference reference : noShrinking.keySet()) {
+ if (reference.isDexField()) {
+ DexField field = reference.asDexField();
+ DexEncodedField encodedField = appInfo.definitionFor(field);
+ if (encodedField != null
+ && (encodedField.isStatic() || isKeptDirectlyOrIndirectly(field.clazz, appInfo))) {
// TODO(b/121354886): Enable asserts for reads and writes.
- /*assert appInfo.fieldsRead.contains(field.field)
- : "Expected kept field `" + field.field.toSourceString() + "` to be read";
- assert appInfo.fieldsWritten.contains(field.field)
- : "Expected kept field `" + field.field.toSourceString() + "` to be written";*/
- assert appInfo.liveFields.contains(field.field)
- : "Expected kept field `" + field.field.toSourceString() + "` to be live";
+ /*assert appInfo.fieldsRead.contains(field)
+ : "Expected kept field `" + field.toSourceString() + "` to be read";
+ assert appInfo.fieldsWritten.contains(field)
+ : "Expected kept field `" + field.toSourceString() + "` to be written";*/
+ assert appInfo.liveFields.contains(field)
+ : "Expected kept field `" + field.toSourceString() + "` to be live";
}
}
}
@@ -1077,17 +1130,17 @@
}
public boolean verifyKeptMethodsAreTargetedAndLive(AppInfoWithLiveness appInfo) {
- for (DexDefinition definition : noShrinking.keySet()) {
- if (definition.isDexEncodedMethod()) {
- DexEncodedMethod method = definition.asDexEncodedMethod();
- assert appInfo.targetedMethods.contains(method.method)
- : "Expected kept method `" + method.method.toSourceString() + "` to be targeted";
-
- if (!method.accessFlags.isAbstract()
- && isKeptDirectlyOrIndirectly(method.method.holder, appInfo)) {
- assert appInfo.liveMethods.contains(method.method)
+ for (DexReference reference : noShrinking.keySet()) {
+ if (reference.isDexMethod()) {
+ DexMethod method = reference.asDexMethod();
+ assert appInfo.targetedMethods.contains(method)
+ : "Expected kept method `" + method.toSourceString() + "` to be targeted";
+ DexEncodedMethod encodedMethod = appInfo.definitionFor(method);
+ if (!encodedMethod.accessFlags.isAbstract()
+ && isKeptDirectlyOrIndirectly(method.holder, appInfo)) {
+ assert appInfo.liveMethods.contains(method)
: "Expected non-abstract kept method `"
- + method.method.toSourceString()
+ + method.toSourceString()
+ "` to be live";
}
}
@@ -1096,37 +1149,59 @@
}
public boolean verifyKeptTypesAreLive(AppInfoWithLiveness appInfo) {
- for (DexDefinition definition : noShrinking.keySet()) {
- if (definition.isDexClass()) {
- DexClass clazz = definition.asDexClass();
- assert appInfo.liveTypes.contains(clazz.type)
- : "Expected kept type `" + clazz.type.toSourceString() + "` to be live";
+ for (DexReference reference : noShrinking.keySet()) {
+ if (reference.isDexType()) {
+ DexType type = reference.asDexType();
+ assert appInfo.liveTypes.contains(type)
+ : "Expected kept type `" + type.toSourceString() + "` to be live";
}
}
return true;
}
private boolean isKeptDirectlyOrIndirectly(DexType type, AppInfoWithLiveness appInfo) {
+ if (noShrinking.containsKey(type)) {
+ return true;
+ }
DexClass clazz = appInfo.definitionFor(type);
if (clazz == null) {
return false;
}
- if (noShrinking.containsKey(clazz)) {
- return true;
- }
if (clazz.superType != null) {
return isKeptDirectlyOrIndirectly(clazz.superType, appInfo);
}
return false;
}
- public boolean verifyKeptItemsAreKept(
- DexApplication application, AppInfo appInfo, InternalOptions options) {
- if (options.getProguardConfiguration().hasApplyMappingFile()) {
- // TODO(b/121295633): Root set is obsolete after mapping has been applied.
- return true;
+ // TODO(b/121240523): Ideally, these default interface methods should be delegated.
+ private boolean isDesugaredMethod(
+ AppInfo appInfo, GraphLense lense, DexClass clazz, DexMethod method) {
+ if (clazz == null) {
+ clazz = appInfo.definitionFor(method.getHolder());
}
+ Set<DexMethod> lookupMethods = lense.lookupMethodInAllContexts(method);
+ if (lookupMethods.size() == 1) {
+ for (DexMethod lookupMethod : lookupMethods) {
+ if (lookupMethod == method) {
+ continue;
+ }
+ DexEncodedMethod encodedMethod = appInfo.definitionFor(lookupMethod);
+ if (clazz != null
+ && clazz.isInterface()
+ && encodedMethod != null
+ && encodedMethod.hasCode()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ public boolean verifyKeptItemsAreKept(
+ DexApplication application,
+ AppInfo appInfo,
+ GraphLense lense,
+ InternalOptions options) {
boolean isInterfaceMethodDesugaringEnabled =
options.enableDesugaring
&& options.interfaceMethodDesugaring == OffOrAuto.Auto
@@ -1136,33 +1211,36 @@
appInfo.hasLiveness() ? appInfo.withLiveness().pinnedItems : null;
// Create a mapping from each required type to the set of required members on that type.
- Map<DexType, Set<DexDefinition>> requiredDefinitionsPerType = new IdentityHashMap<>();
- for (DexDefinition definition : noShrinking.keySet()) {
+ Map<DexType, Set<DexReference>> requiredReferencesPerType = new IdentityHashMap<>();
+ for (DexReference reference : noShrinking.keySet()) {
// Check that `pinnedItems` is a super set of the root set.
- assert pinnedItems == null || pinnedItems.contains(definition.toReference());
- if (definition.isDexClass()) {
- DexType type = definition.toReference().asDexType();
- requiredDefinitionsPerType.putIfAbsent(type, Sets.newIdentityHashSet());
+ assert pinnedItems == null || pinnedItems.contains(reference)
+ || (reference.isDexMethod()
+ && isInterfaceMethodDesugaringEnabled
+ && isDesugaredMethod(appInfo, lense, null, reference.asDexMethod()));
+ if (reference.isDexType()) {
+ DexType type = reference.asDexType();
+ requiredReferencesPerType.putIfAbsent(type, Sets.newIdentityHashSet());
} else {
- assert definition.isDexEncodedField() || definition.isDexEncodedMethod();
- Descriptor<?, ?> descriptor = definition.toReference().asDescriptor();
- requiredDefinitionsPerType
+ assert reference.isDexField() || reference.isDexMethod();
+ Descriptor<?, ?> descriptor = reference.asDescriptor();
+ requiredReferencesPerType
.computeIfAbsent(descriptor.getHolder(), key -> Sets.newIdentityHashSet())
- .add(definition);
+ .add(reference);
}
}
// Run through each class in the program and check that it has members it must have.
for (DexProgramClass clazz : application.classes()) {
- Set<DexDefinition> requiredDefinitions =
- requiredDefinitionsPerType.getOrDefault(clazz.type, ImmutableSet.of());
+ Set<DexReference> requiredReferences =
+ requiredReferencesPerType.getOrDefault(clazz.type, ImmutableSet.of());
Set<DexField> fields = null;
Set<DexMethod> methods = null;
- for (DexDefinition requiredDefinition : requiredDefinitions) {
- if (requiredDefinition.isDexEncodedField()) {
- DexEncodedField requiredField = requiredDefinition.asDexEncodedField();
+ for (DexReference requiredReference : requiredReferences) {
+ if (requiredReference.isDexField()) {
+ DexField requiredField = requiredReference.asDexField();
if (fields == null) {
// Create a Set of the fields to avoid quadratic behavior.
fields =
@@ -1170,15 +1248,12 @@
.map(DexEncodedField::getKey)
.collect(Collectors.toSet());
}
- assert fields.contains(requiredField.field);
- } else if (requiredDefinition.isDexEncodedMethod()) {
- DexEncodedMethod requiredMethod = requiredDefinition.asDexEncodedMethod();
- if (isInterfaceMethodDesugaringEnabled) {
- if (clazz.isInterface() && requiredMethod.hasCode()) {
- // TODO(b/121240523): Ideally, these default interface methods should not be in the
- // root set.
- continue;
- }
+ assert fields.contains(requiredField);
+ } else if (requiredReference.isDexMethod()) {
+ DexMethod requiredMethod = requiredReference.asDexMethod();
+ if (isInterfaceMethodDesugaringEnabled
+ && isDesugaredMethod(appInfo, lense, clazz, requiredMethod)) {
+ continue;
}
if (methods == null) {
// Create a Set of the methods to avoid quadratic behavior.
@@ -1187,21 +1262,21 @@
.map(DexEncodedMethod::getKey)
.collect(Collectors.toSet());
}
- assert methods.contains(requiredMethod.method);
+ assert methods.contains(requiredMethod);
} else {
assert false;
}
}
- requiredDefinitionsPerType.remove(clazz.type);
+ requiredReferencesPerType.remove(clazz.type);
}
// If the map is non-empty, then a type in the root set was not in the application.
- if (!requiredDefinitionsPerType.isEmpty()) {
- DexType type = requiredDefinitionsPerType.keySet().iterator().next();
+ if (!requiredReferencesPerType.isEmpty()) {
+ DexType type = requiredReferencesPerType.keySet().iterator().next();
DexClass clazz = application.definitionFor(type);
assert clazz == null || clazz.isProgramClass()
: "Unexpected library type in root set: `" + type + "`";
- assert requiredDefinitionsPerType.isEmpty()
+ assert requiredReferencesPerType.isEmpty()
: "Expected type `" + type.toSourceString() + "` to be present";
}
@@ -1227,7 +1302,7 @@
builder.append("\n\nNo Shrinking:");
noShrinking.keySet().stream()
- .sorted(Comparator.comparing(DexDefinition::toSourceString))
+ .sorted(Comparator.comparing(DexReference::toSourceString))
.forEach(a -> builder
.append("\n").append(a.toSourceString()).append(" ").append(noShrinking.get(a)));
builder.append("\n");
@@ -1239,18 +1314,18 @@
static class ConsequentRootSet {
final Set<DexMethod> neverInline;
final Set<DexType> neverClassInline;
- final Map<DexDefinition, Set<ProguardKeepRule>> noShrinking;
- final Set<DexDefinition> noOptimization;
- final Set<DexDefinition> noObfuscation;
- final Map<DexDefinition, Map<DexDefinition, Set<ProguardKeepRule>>> dependentNoShrinking;
+ final Map<DexReference, Set<ProguardKeepRule>> noShrinking;
+ final Set<DexReference> noOptimization;
+ final Set<DexReference> noObfuscation;
+ final Map<DexReference, Map<DexReference, Set<ProguardKeepRule>>> dependentNoShrinking;
private ConsequentRootSet(
Set<DexMethod> neverInline,
Set<DexType> neverClassInline,
- Map<DexDefinition, Set<ProguardKeepRule>> noShrinking,
- Set<DexDefinition> noOptimization,
- Set<DexDefinition> noObfuscation,
- Map<DexDefinition, Map<DexDefinition, Set<ProguardKeepRule>>> dependentNoShrinking) {
+ Map<DexReference, Set<ProguardKeepRule>> noShrinking,
+ Set<DexReference> noOptimization,
+ Set<DexReference> noObfuscation,
+ Map<DexReference, Map<DexReference, Set<ProguardKeepRule>>> dependentNoShrinking) {
this.neverInline = Collections.unmodifiableSet(neverInline);
this.neverClassInline = Collections.unmodifiableSet(neverClassInline);
this.noShrinking = Collections.unmodifiableMap(noShrinking);
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 15dcafa..fd90187 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexDefinition;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -272,9 +271,7 @@
// TODO(christofferqa): Remove the invariant that the graph lense should not modify any
// methods from the sets alwaysInline and noSideEffects (see use of assertNotModified).
extractPinnedItems(appInfo.alwaysInline, AbortReason.ALWAYS_INLINE);
- extractPinnedItems(
- DexDefinition.mapToReference(appInfo.noSideEffects.keySet().stream())::iterator,
- AbortReason.NO_SIDE_EFFECTS);
+ extractPinnedItems(appInfo.noSideEffects.keySet(), AbortReason.NO_SIDE_EFFECTS);
for (DexProgramClass clazz : classes) {
for (DexEncodedMethod method : clazz.methods()) {
@@ -620,7 +617,7 @@
}
private boolean verifyGraphLense(GraphLense graphLense) {
- assert graphLense.assertDefinitionsNotModified(appInfo.noSideEffects.keySet());
+ assert graphLense.assertReferencesNotModified(appInfo.noSideEffects.keySet());
// Note that the method assertReferencesNotModified() relies on getRenamedFieldSignature() and
// getRenamedMethodSignature() instead of lookupField() and lookupMethod(). This is important