Merge "Reland "Load all library and program classes when running R8""
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 3cf4664..228c0e5 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -445,9 +445,6 @@
appViewWithLiveness.setAppInfo(new SwitchMapCollector(appViewWithLiveness, options).run());
appViewWithLiveness.setAppInfo(
new EnumOrdinalMapCollector(appViewWithLiveness, options).run());
-
- // TODO(b/79143143): re-enable once fixed.
- // graphLense = new BridgeMethodAnalysis(graphLense, appInfo.withLiveness()).run();
}
timing.begin("Create IR");
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java
index e7282e1..0db0ca4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ReferenceTypeLatticeElement.java
@@ -43,13 +43,13 @@
}
@Override
- public boolean isNull() {
+ public boolean isNullType() {
return type == DexItemFactory.nullValueType;
}
@Override
public TypeLatticeElement asNullable() {
- assert isNull();
+ assert isNullType();
return this;
}
@@ -88,7 +88,7 @@
@Override
public int hashCode() {
- assert isNull();
+ assert isNullType();
return System.identityHashCode(this);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
index b29cd31..ad8edd3 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeLatticeElement.java
@@ -29,7 +29,6 @@
public static final ReferenceTypeLatticeElement NULL =
ReferenceTypeLatticeElement.getNullTypeLatticeElement();
-
// TODO(b/72693244): Switch to NullLatticeElement.
private final boolean isNullable;
@@ -42,7 +41,7 @@
}
public NullLatticeElement nullElement() {
- if (isNull()) {
+ if (isNullType()) {
return NullLatticeElement.definitelyNull();
}
if (!isNullable()) {
@@ -91,10 +90,10 @@
if (isTop() || other.isTop()) {
return TOP;
}
- if (isNull()) {
+ if (isNullType()) {
return other.asNullable();
}
- if (other.isNull()) {
+ if (other.isNullType()) {
return asNullable();
}
if (isPrimitive()) {
@@ -270,7 +269,7 @@
public boolean isPreciseType() {
return isArrayType()
|| isClassType()
- || isNull()
+ || isNullType()
|| isInt()
|| isFloat()
|| isLong()
@@ -286,21 +285,13 @@
}
/**
- * Should use {@link #isConstantNull()} or {@link #isDefinitelyNull()} instead.
- */
- @Deprecated
- public boolean isNull() {
- return false;
- }
-
- /**
* Determines if this type only includes null values that are defined by a const-number
* instruction in the same enclosing method.
*
* These null values can be assigned to any type.
*/
- public boolean isConstantNull() {
- return isNull();
+ public boolean isNullType() {
+ return false;
}
/**
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index c68baf9..596fdb8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -148,7 +148,7 @@
assert inType.nullElement().lessThanOrEqual(outType.nullElement());
// Since we cannot remove the cast the in-value must be different from null.
- assert !inType.isNull();
+ assert !inType.isNullType();
// TODO(b/72693244): Consider checking equivalence. This requires that the types are always
// as precise as possible, though, meaning that almost all changes to the IR must be followed
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index 7a6a9a4..3976889 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -302,7 +302,7 @@
assert super.verifyTypes(appInfo, graphLense);
assert !isZero()
|| outValue().getTypeLattice().isPrimitive()
- || outValue().getTypeLattice().isConstantNull();
+ || outValue().getTypeLattice().isNullType();
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Throw.java b/src/main/java/com/android/tools/r8/ir/code/Throw.java
index c9b12f2..23bb76c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Throw.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Throw.java
@@ -82,7 +82,7 @@
return true;
}
TypeLatticeElement exceptionType = exception().getTypeLattice();
- if (exceptionType.isConstantNull()) {
+ if (exceptionType.isNullType()) {
// throw null
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
index 2094584..b417c48 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
@@ -343,7 +343,7 @@
// redundant
!knownToBeNonNullValue.isNeverNull()
// v <- non-null NULL ?!
- && !typeLattice.isConstantNull()
+ && !typeLattice.isNullType()
// v <- non-null known-to-be-non-null // redundant
&& typeLattice.isNullable()
// e.g., v <- non-null INT ?!
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
index 47b9289..9d2bb32 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
@@ -36,6 +36,7 @@
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
public class UnusedArgumentsCollector {
@@ -89,7 +90,9 @@
Streams.stream(appView.appInfo().classes())
.map(this::runnableForClass)
.map(executorService::submit)
- .iterator());
+ // Materialize list such that all runnables are submitted to the executor service
+ // before calling awaitFutures().
+ .collect(Collectors.toList()));
if (!methodMapping.isEmpty()) {
return new UnusedArgumentsGraphLense(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
index bd4f2b1..cdd85e4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
@@ -305,7 +305,7 @@
continue;
}
TypeLatticeElement inType = in.getTypeLattice();
- if (inType.isConstantNull()) {
+ if (inType.isNullType()) {
Value nullStringValue =
code.createValue(TypeLatticeElement.stringClassType(appInfo), invoke.getLocalInfo());
ConstString nullString = new ConstString(
diff --git a/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
deleted file mode 100644
index 86065d9..0000000
--- a/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.optimize;
-
-import com.android.tools.r8.errors.Unimplemented;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLense;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.logging.Log;
-import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
-import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
-import java.util.IdentityHashMap;
-import java.util.Map;
-
-public class BridgeMethodAnalysis {
-
- private final GraphLense lense;
- private final AppInfoWithLiveness appInfo;
- private final Map<DexMethod, DexMethod> bridgeTargetToBridgeMap = new IdentityHashMap<>();
-
- public BridgeMethodAnalysis(GraphLense lense, AppInfoWithLiveness appInfo) {
- this.lense = lense;
- this.appInfo = appInfo;
- }
-
- public GraphLense run() {
- for (DexClass clazz : appInfo.classes()) {
- clazz.forEachMethod(this::identifyBridgeMethod);
- }
- return new BridgeLense(lense, bridgeTargetToBridgeMap);
- }
-
- private void identifyBridgeMethod(DexEncodedMethod method) {
- // The tree pruner can mark bridge methods abstract if they are not reachable but cannot
- // be removed.
- if (method.accessFlags.isBridge() && !method.accessFlags.isAbstract()) {
- InvokeSingleTargetExtractor targetExtractor =
- new InvokeSingleTargetExtractor(appInfo.dexItemFactory);
- method.getCode().registerCodeReferences(targetExtractor);
- DexMethod target = targetExtractor.getTarget();
- InvokeKind kind = targetExtractor.getKind();
- if (target != null && target.getArity() == method.method.getArity()) {
- assert !method.accessFlags.isPrivate() && !method.accessFlags.isConstructor();
- if (kind == InvokeKind.STATIC) {
- assert method.accessFlags.isStatic();
- DexMethod actualTarget = lense.lookupMethod(target, method, Type.STATIC).getMethod();
- DexEncodedMethod targetMethod = appInfo.lookupStaticTarget(actualTarget);
- if (targetMethod != null) {
- addForwarding(method, targetMethod);
- }
- } else if (kind == InvokeKind.VIRTUAL) {
- // TODO(herhut): Add support for bridges with multiple targets.
- DexMethod actualTarget = lense.lookupMethod(target, method, Type.VIRTUAL).getMethod();
- DexEncodedMethod targetMethod = appInfo.lookupSingleVirtualTarget(actualTarget);
- if (targetMethod != null) {
- addForwarding(method, targetMethod);
- }
- }
- }
- }
- }
-
- private void addForwarding(DexEncodedMethod method, DexEncodedMethod target) {
- // This is a single target bridge we can inline.
- if (Log.ENABLED) {
- Log.info(getClass(), "Adding bridge forwarding %s -> %s.", method.method,
- target.method);
- }
- // If we manage to rewrite all invocations, the bridge will be the only invocation of the target
- // of the bridge and the target will get inlined. This should happen in most cases. For the few
- // other cases, we might have inserted some extra checkcast instructions for the return type.
- bridgeTargetToBridgeMap.put(target.method, method.method);
- }
-
-
- private static class BridgeLense extends GraphLense {
-
- private final GraphLense previousLense;
- private final Map<DexMethod, DexMethod> bridgeTargetToBridgeMap;
-
- private BridgeLense(GraphLense previousLense,
- Map<DexMethod, DexMethod> bridgeTargetToBridgeMap) {
- this.previousLense = previousLense;
- this.bridgeTargetToBridgeMap = bridgeTargetToBridgeMap;
- }
-
- @Override
- public DexField getOriginalFieldSignature(DexField field) {
- return previousLense.getOriginalFieldSignature(field);
- }
-
- @Override
- public DexMethod getOriginalMethodSignature(DexMethod method) {
- // TODO(b/79143143): implement this when re-enable bridge analysis.
- throw new Unimplemented("BridgeLense.getOriginalMethodSignature() not implemented");
- }
-
- @Override
- public DexField getRenamedFieldSignature(DexField originalField) {
- return previousLense.getRenamedFieldSignature(originalField);
- }
-
- @Override
- public DexMethod getRenamedMethodSignature(DexMethod originalMethod) {
- // TODO(b/79143143): implement this when re-enabling bridge analysis.
- throw new Unimplemented("BridgeLense.getRenamedMethodSignature() not implemented");
- }
-
- @Override
- public DexType lookupType(DexType type) {
- return previousLense.lookupType(type);
- }
-
- @Override
- public GraphLenseLookupResult lookupMethod(
- DexMethod method, DexEncodedMethod context, Type type) {
- GraphLenseLookupResult previous = previousLense.lookupMethod(method, context, type);
- DexMethod bridge = bridgeTargetToBridgeMap.get(previous.getMethod());
- // Do not forward calls from a bridge method to itself while the bridge method is still
- // a bridge.
- if (bridge == null || (context.accessFlags.isBridge() && bridge == context.method)) {
- return previous;
- }
- return new GraphLenseLookupResult(bridge, type);
- }
-
- @Override
- public RewrittenPrototypeDescription lookupPrototypeChanges(DexMethod method) {
- return previousLense.lookupPrototypeChanges(method);
- }
-
- @Override
- public DexField lookupField(DexField field) {
- return previousLense.lookupField(field);
- }
-
- @Override
- public boolean isContextFreeForMethods() {
- return false;
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("------ BridgeMap ------").append(System.lineSeparator());
- for (Map.Entry<DexMethod, DexMethod> entry : bridgeTargetToBridgeMap.entrySet()) {
- builder.append(entry.getKey().toSourceString()).append(" -> ");
- builder.append(entry.getValue().toSourceString()).append(System.lineSeparator());
- }
- builder.append("-----------------------").append(System.lineSeparator());
- builder.append(previousLense.toString());
- return builder.toString();
- }
- }
-}
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 279d381..f10d000 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -297,11 +297,11 @@
this.options = options;
}
- private void enqueueRootItems(Map<DexDefinition, ProguardKeepRule> items) {
+ private void enqueueRootItems(Map<DexDefinition, Set<ProguardKeepRule>> items) {
items.entrySet().forEach(this::enqueueRootItem);
}
- private void enqueueRootItem(Entry<DexDefinition, ProguardKeepRule> root) {
+ private void enqueueRootItem(Entry<DexDefinition, Set<ProguardKeepRule>> root) {
enqueueRootItem(root.getKey(), root.getValue());
}
@@ -309,7 +309,26 @@
enqueueRootItem(item, KeepReason.dueToKeepRule(rule));
}
+ private void enqueueRootItem(DexDefinition item, Set<ProguardKeepRule> rules) {
+ assert !rules.isEmpty();
+ if (keptGraphConsumer != null) {
+ GraphNode node = getGraphNode(item);
+ for (ProguardKeepRule rule : rules) {
+ registerEdge(node, KeepReason.dueToKeepRule(rule));
+ }
+ }
+ internalEnqueueRootItem(item, KeepReason.dueToKeepRule(rules.iterator().next()));
+ }
+
private void enqueueRootItem(DexDefinition item, KeepReason reason) {
+ if (keptGraphConsumer != null) {
+ registerEdge(getGraphNode(item), reason);
+ }
+ internalEnqueueRootItem(item, reason);
+ }
+
+ private void internalEnqueueRootItem(DexDefinition item, KeepReason reason) {
+ // TODO(b/120959039): do we need to propagate the reason to the action now?
if (item.isDexClass()) {
DexClass clazz = item.asDexClass();
workList.add(Action.markInstantiated(clazz, reason));
@@ -349,11 +368,11 @@
}
private void enqueueHolderIfDependentNonStaticMember(
- DexClass holder, Map<DexDefinition, ProguardKeepRule> dependentItems) {
+ DexClass holder, Map<DexDefinition, 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, ProguardKeepRule> entry : dependentItems.entrySet()) {
+ for (Entry<DexDefinition, Set<ProguardKeepRule>> entry : dependentItems.entrySet()) {
DexDefinition dependentItem = entry.getKey();
if (dependentItem.isDexClass()) {
continue;
@@ -791,7 +810,7 @@
annotations.forEach(this::handleAnnotationOfLiveType);
}
- Map<DexDefinition, ProguardKeepRule> dependentItems = rootSet.getDependentItems(holder);
+ Map<DexDefinition, Set<ProguardKeepRule>> dependentItems = rootSet.getDependentItems(holder);
enqueueHolderIfDependentNonStaticMember(holder, dependentItems);
// Add all dependent members to the workqueue.
enqueueRootItems(dependentItems);
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 2f7f54a..e86966f 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -61,7 +61,7 @@
private final AppView<? extends AppInfo> appView;
private final DirectMappedDexApplication application;
private final Collection<ProguardConfigurationRule> rules;
- private final Map<DexDefinition, ProguardKeepRule> noShrinking = new IdentityHashMap<>();
+ 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<>();
@@ -76,7 +76,7 @@
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, ProguardKeepRule>> dependentNoShrinking =
+ private final Map<DexDefinition, Map<DexDefinition, Set<ProguardKeepRule>>> dependentNoShrinking =
new IdentityHashMap<>();
private final Map<DexDefinition, ProguardMemberRule> noSideEffects = new IdentityHashMap<>();
private final Map<DexDefinition, ProguardMemberRule> assumedValues = new IdentityHashMap<>();
@@ -854,8 +854,10 @@
return;
}
// Keep the type if the item is also kept.
- dependentNoShrinking.computeIfAbsent(item, x -> new IdentityHashMap<>())
- .put(definition, context);
+ dependentNoShrinking
+ .computeIfAbsent(item, x -> new IdentityHashMap<>())
+ .computeIfAbsent(definition, k -> new HashSet<>())
+ .add(context);
// Unconditionally add to no-obfuscation, as that is only checked for surviving items.
noObfuscation.add(definition);
}
@@ -890,10 +892,12 @@
ProguardKeepRuleModifiers modifiers = keepRule.getModifiers();
if (!modifiers.allowsShrinking) {
if (precondition != null) {
- dependentNoShrinking.computeIfAbsent(precondition, x -> new IdentityHashMap<>())
- .put(item, keepRule);
+ dependentNoShrinking
+ .computeIfAbsent(precondition, x -> new IdentityHashMap<>())
+ .computeIfAbsent(item, i -> new HashSet<>())
+ .add(keepRule);
} else {
- noShrinking.put(item, keepRule);
+ noShrinking.computeIfAbsent(item, i -> new HashSet<>()).add(keepRule);
}
}
if (!modifiers.allowsOptimization) {
@@ -970,7 +974,7 @@
public static class RootSet {
- public final Map<DexDefinition, ProguardKeepRule> noShrinking;
+ public final Map<DexDefinition, Set<ProguardKeepRule>> noShrinking;
public final Set<DexDefinition> noOptimization;
public final Set<DexDefinition> noObfuscation;
public final ImmutableList<DexDefinition> reasonAsked;
@@ -985,12 +989,13 @@
public final Set<DexType> neverMerge;
public final Map<DexDefinition, ProguardMemberRule> noSideEffects;
public final Map<DexDefinition, ProguardMemberRule> assumedValues;
- private final Map<DexDefinition, Map<DexDefinition, ProguardKeepRule>> dependentNoShrinking;
+ private final Map<DexDefinition, Map<DexDefinition, Set<ProguardKeepRule>>>
+ dependentNoShrinking;
public final Set<DexReference> identifierNameStrings;
public final Set<ProguardIfRule> ifRules;
private RootSet(
- Map<DexDefinition, ProguardKeepRule> noShrinking,
+ Map<DexDefinition, Set<ProguardKeepRule>> noShrinking,
Set<DexDefinition> noOptimization,
Set<DexDefinition> noObfuscation,
ImmutableList<DexDefinition> reasonAsked,
@@ -1005,7 +1010,7 @@
Set<DexType> neverMerge,
Map<DexDefinition, ProguardMemberRule> noSideEffects,
Map<DexDefinition, ProguardMemberRule> assumedValues,
- Map<DexDefinition, Map<DexDefinition, ProguardKeepRule>> dependentNoShrinking,
+ Map<DexDefinition, Map<DexDefinition, Set<ProguardKeepRule>>> dependentNoShrinking,
Set<DexReference> identifierNameStrings,
Set<ProguardIfRule> ifRules) {
this.noShrinking = Collections.unmodifiableMap(noShrinking);
@@ -1030,7 +1035,7 @@
// Add dependent items that depend on -if rules.
void addDependentItems(
- Map<DexDefinition, Map<DexDefinition, ProguardKeepRule>> dependentItems) {
+ Map<DexDefinition, Map<DexDefinition, Set<ProguardKeepRule>>> dependentItems) {
dependentItems.forEach(
(def, dependence) ->
dependentNoShrinking
@@ -1038,7 +1043,7 @@
.putAll(dependence));
}
- Map<DexDefinition, ProguardKeepRule> getDependentItems(DexDefinition item) {
+ Map<DexDefinition, Set<ProguardKeepRule>> getDependentItems(DexDefinition item) {
return Collections
.unmodifiableMap(dependentNoShrinking.getOrDefault(item, Collections.emptyMap()));
}
@@ -1224,18 +1229,18 @@
static class ConsequentRootSet {
final Set<DexMethod> neverInline;
final Set<DexType> neverClassInline;
- final Map<DexDefinition, ProguardKeepRule> noShrinking;
+ final Map<DexDefinition, Set<ProguardKeepRule>> noShrinking;
final Set<DexDefinition> noOptimization;
final Set<DexDefinition> noObfuscation;
- final Map<DexDefinition, Map<DexDefinition, ProguardKeepRule>> dependentNoShrinking;
+ final Map<DexDefinition, Map<DexDefinition, Set<ProguardKeepRule>>> dependentNoShrinking;
private ConsequentRootSet(
Set<DexMethod> neverInline,
Set<DexType> neverClassInline,
- Map<DexDefinition, ProguardKeepRule> noShrinking,
+ Map<DexDefinition, Set<ProguardKeepRule>> noShrinking,
Set<DexDefinition> noOptimization,
Set<DexDefinition> noObfuscation,
- Map<DexDefinition, Map<DexDefinition, ProguardKeepRule>> dependentNoShrinking) {
+ Map<DexDefinition, Map<DexDefinition, 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/utils/ThreadUtils.java b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
index bde7016..fb4f409 100644
--- a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -15,11 +15,7 @@
public static void awaitFutures(Iterable<? extends Future<?>> futures)
throws ExecutionException {
- awaitFutures(futures.iterator());
- }
-
- public static void awaitFutures(Iterator<? extends Future<?>> futureIterator)
- throws ExecutionException {
+ Iterator<? extends Future<?>> futureIterator = futures.iterator();
try {
while (futureIterator.hasNext()) {
futureIterator.next().get();
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByAnnotatedMethodTestRunner.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByAnnotatedMethodTestRunner.java
index 19cdf04..a7e3e46 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByAnnotatedMethodTestRunner.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByAnnotatedMethodTestRunner.java
@@ -86,7 +86,7 @@
.method(fooMethod)
.assertNotRenamed()
.assertNotInvokedFrom(mainMethod)
- // TODO(b/122297131): keepAnnotatedMethods should also be keeping foo alive!
+ .assertKeptBy(keepAnnotatedMethods)
.assertKeptBy(keepClassesOfAnnotatedMethods);
// Check baz is removed.
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoRulesTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoRulesTest.java
new file mode 100644
index 0000000..7cb464d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoRulesTest.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.keptgraph;
+
+import com.android.tools.r8.Keep;
+
+@Keep
+public class KeptByTwoRulesTest {
+
+ public static void foo() {
+ System.out.println("called foo");
+ }
+
+ public static void main(String[] args) {
+ foo();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoRulesTestRunner.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoRulesTestRunner.java
new file mode 100644
index 0000000..38c3f00
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoRulesTestRunner.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.keptgraph;
+
+import static com.android.tools.r8.references.Reference.classFromClass;
+import static com.android.tools.r8.references.Reference.methodFromMethod;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.graphinspector.GraphInspector;
+import com.android.tools.r8.utils.graphinspector.GraphInspector.QueryNode;
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class KeptByTwoRulesTestRunner extends TestBase {
+
+ private static final Class<?> CLASS = KeptByTwoRulesTest.class;
+ private static final Collection<Class<?>> CLASSES = Arrays.asList(CLASS);
+
+ private final String EXPECTED = StringUtils.lines("called foo");
+
+ private final Backend backend;
+
+ @Parameters(name = "{0}")
+ public static Backend[] data() {
+ return Backend.values();
+ }
+
+ public KeptByTwoRulesTestRunner(Backend backend) {
+ this.backend = backend;
+ }
+
+ @Test
+ public void test() throws Exception {
+ MethodReference mainMethod = methodFromMethod(CLASS.getDeclaredMethod("main", String[].class));
+ MethodReference fooMethod = methodFromMethod(CLASS.getDeclaredMethod("foo"));
+
+ if (backend == Backend.CF) {
+ testForJvm().addProgramClasses(CLASSES).run(CLASS).assertSuccessWithOutput(EXPECTED);
+ }
+
+ String keepPublicRule = "-keep @com.android.tools.r8.Keep class * { public *; }";
+ String keepFooRule = "-keep class " + CLASS.getTypeName() + " { public void foo(); }";
+ GraphInspector inspector =
+ testForR8(backend)
+ .enableGraphInspector()
+ .enableInliningAnnotations()
+ .addProgramClasses(CLASSES)
+ .addKeepRules(keepPublicRule, keepFooRule)
+ .run(CLASS)
+ .assertSuccessWithOutput(EXPECTED)
+ .graphInspector();
+
+ assertEquals(2, inspector.getRoots().size());
+ QueryNode keepPublic = inspector.rule(keepPublicRule).assertRoot();
+ QueryNode keepFoo = inspector.rule(keepFooRule).assertRoot();
+
+ inspector
+ .method(mainMethod)
+ .assertNotRenamed()
+ .assertKeptBy(keepPublic)
+ .assertNotKeptBy(keepFoo);
+
+ // Check foo is called from main and kept by two rules.
+ inspector
+ .method(fooMethod)
+ .assertNotRenamed()
+ .assertInvokedFrom(mainMethod)
+ .assertKeptBy(keepPublic)
+ .assertKeptBy(keepFoo);
+
+ // Check the class is also kept by both rules.
+ inspector
+ .clazz(classFromClass(CLASS))
+ .assertNotRenamed()
+ .assertKeptBy(keepPublic)
+ .assertKeptBy(keepFoo);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/graphinspector/GraphInspector.java b/src/test/java/com/android/tools/r8/utils/graphinspector/GraphInspector.java
index 107978b..940482b 100644
--- a/src/test/java/com/android/tools/r8/utils/graphinspector/GraphInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/graphinspector/GraphInspector.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils.graphinspector;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -113,16 +114,26 @@
}
public QueryNode assertNotInvokedFrom(MethodReference method) {
- assertTrue(
- errorMessage("no invocation from " + method.toString(), "invoke"),
- !isInvokedFrom(method));
+ assertFalse(
+ errorMessage("no invocation from " + method.toString(), "invoke"), isInvokedFrom(method));
return this;
}
public QueryNode assertKeptBy(QueryNode node) {
- assertTrue("Invalid call to assertKeptBy with: " + node.getNodeDescription(),
- node.isPresent());
- assertTrue(errorMessage("kept by " + node.getNodeDescription(), "none"), isKeptBy(node));
+ assertTrue(
+ "Invalid call to assertKeptBy with: " + node.getNodeDescription(), node.isPresent());
+ assertTrue(
+ errorMessage("kept by " + node.getNodeDescription(), "was not kept by it"),
+ isKeptBy(node));
+ return this;
+ }
+
+ public QueryNode assertNotKeptBy(QueryNode node) {
+ assertTrue(
+ "Invalid call to assertNotKeptBy with: " + node.getNodeDescription(), node.isPresent());
+ assertFalse(
+ errorMessage("not kept by " + node.getNodeDescription(), "was kept by it"),
+ isKeptBy(node));
return this;
}
}