Cleanup invoke collections in Enqueuer and AppInfoWithLiveness
Change-Id: Ie51df5abe9f5a83324b01c75b3a7b3a4a47fda19
diff --git a/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java b/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
new file mode 100644
index 0000000..8cbbefe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
@@ -0,0 +1,128 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.graph;
+
+import com.android.tools.r8.utils.MapUtils;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.function.BiConsumer;
+
+public class MethodAccessInfoCollection {
+
+ private final Map<DexMethod, ProgramMethodSet> directInvokes;
+ private final Map<DexMethod, ProgramMethodSet> interfaceInvokes;
+ private final Map<DexMethod, ProgramMethodSet> staticInvokes;
+ private final Map<DexMethod, ProgramMethodSet> superInvokes;
+ private final Map<DexMethod, ProgramMethodSet> virtualInvokes;
+
+ private MethodAccessInfoCollection(
+ Map<DexMethod, ProgramMethodSet> directInvokes,
+ Map<DexMethod, ProgramMethodSet> interfaceInvokes,
+ Map<DexMethod, ProgramMethodSet> staticInvokes,
+ Map<DexMethod, ProgramMethodSet> superInvokes,
+ Map<DexMethod, ProgramMethodSet> virtualInvokes) {
+ this.directInvokes = directInvokes;
+ this.interfaceInvokes = interfaceInvokes;
+ this.staticInvokes = staticInvokes;
+ this.superInvokes = superInvokes;
+ this.virtualInvokes = virtualInvokes;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public void forEachDirectInvoke(BiConsumer<DexMethod, ProgramMethodSet> consumer) {
+ directInvokes.forEach(consumer);
+ }
+
+ public void forEachInterfaceInvoke(BiConsumer<DexMethod, ProgramMethodSet> consumer) {
+ interfaceInvokes.forEach(consumer);
+ }
+
+ public void forEachStaticInvoke(BiConsumer<DexMethod, ProgramMethodSet> consumer) {
+ staticInvokes.forEach(consumer);
+ }
+
+ public void forEachSuperInvoke(BiConsumer<DexMethod, ProgramMethodSet> consumer) {
+ superInvokes.forEach(consumer);
+ }
+
+ public void forEachVirtualInvoke(BiConsumer<DexMethod, ProgramMethodSet> consumer) {
+ virtualInvokes.forEach(consumer);
+ }
+
+ public MethodAccessInfoCollection rewrittenWithLens(
+ DexDefinitionSupplier definitions, GraphLens lens) {
+ return new MethodAccessInfoCollection(
+ rewriteInvokesWithLens(directInvokes, definitions, lens),
+ rewriteInvokesWithLens(interfaceInvokes, definitions, lens),
+ rewriteInvokesWithLens(staticInvokes, definitions, lens),
+ rewriteInvokesWithLens(superInvokes, definitions, lens),
+ rewriteInvokesWithLens(virtualInvokes, definitions, lens));
+ }
+
+ private static Map<DexMethod, ProgramMethodSet> rewriteInvokesWithLens(
+ Map<DexMethod, ProgramMethodSet> invokes, DexDefinitionSupplier definitions, GraphLens lens) {
+ return MapUtils.map(
+ invokes,
+ capacity -> new TreeMap<>(DexMethod::slowCompareTo),
+ lens::getRenamedMethodSignature,
+ methods -> methods.rewrittenWithLens(definitions, lens),
+ (methods, other) -> {
+ methods.addAll(other);
+ return methods;
+ });
+ }
+
+ public static class Builder {
+
+ // TODO(b/132593519): We should not need sorted maps with the new member rebinding analysis.
+ private final Map<DexMethod, ProgramMethodSet> directInvokes =
+ new TreeMap<>(DexMethod::slowCompareTo);
+ private final Map<DexMethod, ProgramMethodSet> interfaceInvokes =
+ new TreeMap<>(DexMethod::slowCompareTo);
+ private final Map<DexMethod, ProgramMethodSet> staticInvokes =
+ new TreeMap<>(DexMethod::slowCompareTo);
+ private final Map<DexMethod, ProgramMethodSet> superInvokes =
+ new TreeMap<>(DexMethod::slowCompareTo);
+ private final Map<DexMethod, ProgramMethodSet> virtualInvokes =
+ new TreeMap<>(DexMethod::slowCompareTo);
+
+ public boolean registerInvokeDirectInContext(DexMethod invokedMethod, ProgramMethod context) {
+ return registerInvokeMethodInContext(invokedMethod, context, directInvokes);
+ }
+
+ public boolean registerInvokeInterfaceInContext(
+ DexMethod invokedMethod, ProgramMethod context) {
+ return registerInvokeMethodInContext(invokedMethod, context, interfaceInvokes);
+ }
+
+ public boolean registerInvokeStaticInContext(DexMethod invokedMethod, ProgramMethod context) {
+ return registerInvokeMethodInContext(invokedMethod, context, staticInvokes);
+ }
+
+ public boolean registerInvokeSuperInContext(DexMethod invokedMethod, ProgramMethod context) {
+ return registerInvokeMethodInContext(invokedMethod, context, superInvokes);
+ }
+
+ public boolean registerInvokeVirtualInContext(DexMethod invokedMethod, ProgramMethod context) {
+ return registerInvokeMethodInContext(invokedMethod, context, virtualInvokes);
+ }
+
+ private static boolean registerInvokeMethodInContext(
+ DexMethod invokedMethod, ProgramMethod context, Map<DexMethod, ProgramMethodSet> invokes) {
+ return invokes
+ .computeIfAbsent(invokedMethod, ignore -> ProgramMethodSet.create())
+ .add(context);
+ }
+
+ public MethodAccessInfoCollection build() {
+ return new MethodAccessInfoCollection(
+ directInvokes, interfaceInvokes, staticInvokes, superInvokes, virtualInvokes);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
index a8f3018..09790f1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
/**
* An optimization that merges a method override (B.m()) into the method it overrides (A.m()).
@@ -41,8 +42,8 @@
private final AppView<AppInfoWithLiveness> appView;
private final IRConverter converter;
- private final ProgramMethodSet candidatesForInstanceOfOptimization =
- ProgramMethodSet.createSorted();
+ private final SortedProgramMethodSet candidatesForInstanceOfOptimization =
+ SortedProgramMethodSet.create();
public CheckCastAndInstanceOfMethodSpecialization(
AppView<AppInfoWithLiveness> appView, IRConverter converter) {
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index dcd003c..b8022b9 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -16,13 +16,14 @@
import com.android.tools.r8.graph.FieldAccessInfoCollection;
import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.BiForEachable;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -127,50 +128,71 @@
return appView.appInfo().unsafeResolveMethodDueToDexFormat(method).getSingleTarget();
}
+ private void computeMethodRebinding(MethodAccessInfoCollection methodAccessInfoCollection) {
+ // Virtual invokes are on classes, so use class resolution.
+ computeMethodRebinding(
+ methodAccessInfoCollection::forEachVirtualInvoke, this::classLookup, Type.VIRTUAL);
+ // Interface invokes are always on interfaces, so use interface resolution.
+ computeMethodRebinding(
+ methodAccessInfoCollection::forEachInterfaceInvoke, this::interfaceLookup, Type.INTERFACE);
+ // Super invokes can be on both kinds, decide using the holder class.
+ computeMethodRebinding(
+ methodAccessInfoCollection::forEachSuperInvoke, this::anyLookup, Type.SUPER);
+ // Direct invokes (private/constructor) can also be on both kinds.
+ computeMethodRebinding(
+ methodAccessInfoCollection::forEachDirectInvoke, this::anyLookup, Type.DIRECT);
+ // Likewise static invokes.
+ computeMethodRebinding(
+ methodAccessInfoCollection::forEachStaticInvoke, this::anyLookup, Type.STATIC);
+ }
+
private void computeMethodRebinding(
- Map<DexMethod, ProgramMethodSet> methodsWithContexts,
+ BiForEachable<DexMethod, ProgramMethodSet> methodsWithContexts,
Function<DexMethod, DexEncodedMethod> lookupTarget,
Type invokeType) {
- for (DexMethod method : methodsWithContexts.keySet()) {
- // We can safely ignore array types, as the corresponding methods are defined in a library.
- if (!method.holder.isClassType()) {
- continue;
- }
- DexClass originalClass = appView.definitionFor(method.holder);
- if (originalClass == null || originalClass.isNotProgramClass()) {
- continue;
- }
- DexEncodedMethod target = lookupTarget.apply(method);
- // TODO(b/128404854) Rebind to the lowest library class or program class. For now we allow
- // searching in library for methods, but this should be done on classpath instead.
- if (target != null && target.method != method) {
- DexClass targetClass = appView.definitionFor(target.holder());
- if (originalClass.isProgramClass()) {
- // In Java bytecode, it is only possible to target interface methods that are in one of
- // the immediate super-interfaces via a super-invocation (see IndirectSuperInterfaceTest).
- // To avoid introducing an IncompatibleClassChangeError at runtime we therefore insert a
- // bridge method when we are about to rebind to an interface method that is not the
- // original target.
- if (needsBridgeForInterfaceMethod(originalClass, targetClass, invokeType)) {
- target =
- insertBridgeForInterfaceMethod(
- method, target, originalClass.asProgramClass(), targetClass, lookupTarget);
+ methodsWithContexts.forEach(
+ (method, contexts) -> {
+ // We can safely ignore array types, as the corresponding methods are defined in a
+ // library.
+ if (!method.holder.isClassType()) {
+ return;
}
+ DexClass originalClass = appView.definitionFor(method.holder);
+ if (originalClass == null || originalClass.isNotProgramClass()) {
+ return;
+ }
+ DexEncodedMethod target = lookupTarget.apply(method);
+ // TODO(b/128404854) Rebind to the lowest library class or program class. For now we allow
+ // searching in library for methods, but this should be done on classpath instead.
+ if (target == null || target.method == method) {
+ return;
+ }
+ DexClass targetClass = appView.definitionFor(target.holder());
+ if (originalClass.isProgramClass()) {
+ // In Java bytecode, it is only possible to target interface methods that are in one of
+ // the immediate super-interfaces via a super-invocation (see
+ // IndirectSuperInterfaceTest).
+ // To avoid introducing an IncompatibleClassChangeError at runtime we therefore insert a
+ // bridge method when we are about to rebind to an interface method that is not the
+ // original target.
+ if (needsBridgeForInterfaceMethod(originalClass, targetClass, invokeType)) {
+ target =
+ insertBridgeForInterfaceMethod(
+ method, target, originalClass.asProgramClass(), targetClass, lookupTarget);
+ }
- // If the target class is not public but the targeted method is, we might run into
- // visibility problems when rebinding.
- final DexEncodedMethod finalTarget = target;
- ProgramMethodSet contexts = methodsWithContexts.get(method);
- if (contexts.stream()
- .anyMatch(context -> mayNeedBridgeForVisibility(context, finalTarget))) {
- target =
- insertBridgeForVisibilityIfNeeded(
- method, target, originalClass, targetClass, lookupTarget);
+ // If the target class is not public but the targeted method is, we might run into
+ // visibility problems when rebinding.
+ final DexEncodedMethod finalTarget = target;
+ if (contexts.stream()
+ .anyMatch(context -> mayNeedBridgeForVisibility(context, finalTarget))) {
+ target =
+ insertBridgeForVisibilityIfNeeded(
+ method, target, originalClass, targetClass, lookupTarget);
+ }
}
- }
- builder.map(method, lens.lookupMethod(validTargetFor(target.method, method)), invokeType);
- }
- }
+ builder.map(method, lens.lookupMethod(validTargetFor(target.method, method)), invokeType);
+ });
}
private boolean needsBridgeForInterfaceMethod(
@@ -337,16 +359,7 @@
public GraphLens run() {
AppInfoWithLiveness appInfo = appView.appInfo();
- // Virtual invokes are on classes, so use class resolution.
- computeMethodRebinding(appInfo.virtualInvokes, this::classLookup, Type.VIRTUAL);
- // Interface invokes are always on interfaces, so use interface resolution.
- computeMethodRebinding(appInfo.interfaceInvokes, this::interfaceLookup, Type.INTERFACE);
- // Super invokes can be on both kinds, decide using the holder class.
- computeMethodRebinding(appInfo.superInvokes, this::anyLookup, Type.SUPER);
- // Direct invokes (private/constructor) can also be on both kinds.
- computeMethodRebinding(appInfo.directInvokes, this::anyLookup, Type.DIRECT);
- // Likewise static invokes.
- computeMethodRebinding(appInfo.staticInvokes, this::anyLookup, Type.STATIC);
+ computeMethodRebinding(appInfo.getMethodAccessInfoCollection());
computeFieldRebinding();
GraphLens lens = builder.build(this.lens);
appInfo.getFieldAccessInfoCollection().flattenAccessContexts();
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 1b1d37c..92722cd 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -35,6 +35,7 @@
import com.android.tools.r8.graph.InstantiatedSubTypeInfo;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
import com.android.tools.r8.graph.LookupTarget;
+import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
import com.android.tools.r8.graph.PresortedComparable;
@@ -118,18 +119,10 @@
* each field. The latter is used, for example, during member rebinding.
*/
private FieldAccessInfoCollectionImpl fieldAccessInfoCollection;
+ /** Set of all methods referenced in invokes along with their calling contexts. */
+ private final MethodAccessInfoCollection methodAccessInfoCollection;
/** Information about instantiated classes and their allocation sites. */
private final ObjectAllocationInfoCollectionImpl objectAllocationInfoCollection;
- /** Set of all methods referenced in virtual invokes, along with calling context. */
- public final SortedMap<DexMethod, ProgramMethodSet> virtualInvokes;
- /** Set of all methods referenced in interface invokes, along with calling context. */
- public final SortedMap<DexMethod, ProgramMethodSet> interfaceInvokes;
- /** Set of all methods referenced in super invokes, along with calling context. */
- public final SortedMap<DexMethod, ProgramMethodSet> superInvokes;
- /** Set of all methods referenced in direct invokes, along with calling context. */
- public final SortedMap<DexMethod, ProgramMethodSet> directInvokes;
- /** Set of all methods referenced in static invokes, along with calling context. */
- public final SortedMap<DexMethod, ProgramMethodSet> staticInvokes;
/**
* Set of live call sites in the code. Note that if desugaring has taken place call site objects
* will have been removed from the code.
@@ -212,12 +205,8 @@
SortedSet<DexMethod> virtualMethodsTargetedByInvokeDirect,
SortedSet<DexMethod> liveMethods,
FieldAccessInfoCollectionImpl fieldAccessInfoCollection,
+ MethodAccessInfoCollection methodAccessInfoCollection,
ObjectAllocationInfoCollectionImpl objectAllocationInfoCollection,
- SortedMap<DexMethod, ProgramMethodSet> virtualInvokes,
- SortedMap<DexMethod, ProgramMethodSet> interfaceInvokes,
- SortedMap<DexMethod, ProgramMethodSet> superInvokes,
- SortedMap<DexMethod, ProgramMethodSet> directInvokes,
- SortedMap<DexMethod, ProgramMethodSet> staticInvokes,
Set<DexCallSite> callSites,
KeepInfoCollection keepInfo,
Map<DexReference, ProguardMemberRule> mayHaveSideEffects,
@@ -255,16 +244,12 @@
this.virtualMethodsTargetedByInvokeDirect = virtualMethodsTargetedByInvokeDirect;
this.liveMethods = liveMethods;
this.fieldAccessInfoCollection = fieldAccessInfoCollection;
+ this.methodAccessInfoCollection = methodAccessInfoCollection;
this.objectAllocationInfoCollection = objectAllocationInfoCollection;
this.keepInfo = keepInfo;
this.mayHaveSideEffects = mayHaveSideEffects;
this.noSideEffects = noSideEffects;
this.assumedValues = assumedValues;
- this.virtualInvokes = virtualInvokes;
- this.interfaceInvokes = interfaceInvokes;
- this.superInvokes = superInvokes;
- this.directInvokes = directInvokes;
- this.staticInvokes = staticInvokes;
this.callSites = callSites;
this.alwaysInline = alwaysInline;
this.forceInline = forceInline;
@@ -301,6 +286,7 @@
SortedSet<DexMethod> virtualMethodsTargetedByInvokeDirect,
SortedSet<DexMethod> liveMethods,
FieldAccessInfoCollectionImpl fieldAccessInfoCollection,
+ MethodAccessInfoCollection methodAccessInfoCollection,
ObjectAllocationInfoCollectionImpl objectAllocationInfoCollection,
SortedMap<DexMethod, ProgramMethodSet> virtualInvokes,
SortedMap<DexMethod, ProgramMethodSet> interfaceInvokes,
@@ -347,16 +333,12 @@
this.virtualMethodsTargetedByInvokeDirect = virtualMethodsTargetedByInvokeDirect;
this.liveMethods = liveMethods;
this.fieldAccessInfoCollection = fieldAccessInfoCollection;
+ this.methodAccessInfoCollection = methodAccessInfoCollection;
this.objectAllocationInfoCollection = objectAllocationInfoCollection;
this.keepInfo = keepInfo;
this.mayHaveSideEffects = mayHaveSideEffects;
this.noSideEffects = noSideEffects;
this.assumedValues = assumedValues;
- this.virtualInvokes = virtualInvokes;
- this.interfaceInvokes = interfaceInvokes;
- this.superInvokes = superInvokes;
- this.directInvokes = directInvokes;
- this.staticInvokes = staticInvokes;
this.callSites = callSites;
this.alwaysInline = alwaysInline;
this.forceInline = forceInline;
@@ -398,12 +380,8 @@
previous.virtualMethodsTargetedByInvokeDirect,
previous.liveMethods,
previous.fieldAccessInfoCollection,
+ previous.methodAccessInfoCollection,
previous.objectAllocationInfoCollection,
- previous.virtualInvokes,
- previous.interfaceInvokes,
- previous.superInvokes,
- previous.directInvokes,
- previous.staticInvokes,
previous.callSites,
previous.keepInfo,
previous.mayHaveSideEffects,
@@ -453,12 +431,8 @@
previous.virtualMethodsTargetedByInvokeDirect,
previous.liveMethods,
previous.fieldAccessInfoCollection,
+ previous.methodAccessInfoCollection,
previous.objectAllocationInfoCollection,
- previous.virtualInvokes,
- previous.interfaceInvokes,
- previous.superInvokes,
- previous.directInvokes,
- previous.staticInvokes,
previous.callSites,
extendPinnedItems(previous, additionalPinnedItems),
previous.mayHaveSideEffects,
@@ -545,16 +519,12 @@
this.virtualMethodsTargetedByInvokeDirect = previous.virtualMethodsTargetedByInvokeDirect;
this.liveMethods = previous.liveMethods;
this.fieldAccessInfoCollection = previous.fieldAccessInfoCollection;
+ this.methodAccessInfoCollection = previous.methodAccessInfoCollection;
this.objectAllocationInfoCollection = previous.objectAllocationInfoCollection;
this.keepInfo = previous.keepInfo;
this.mayHaveSideEffects = previous.mayHaveSideEffects;
this.noSideEffects = previous.noSideEffects;
this.assumedValues = previous.assumedValues;
- this.virtualInvokes = previous.virtualInvokes;
- this.interfaceInvokes = previous.interfaceInvokes;
- this.superInvokes = previous.superInvokes;
- this.directInvokes = previous.directInvokes;
- this.staticInvokes = previous.staticInvokes;
this.callSites = previous.callSites;
this.alwaysInline = previous.alwaysInline;
this.forceInline = previous.forceInline;
@@ -779,6 +749,11 @@
return fieldAccessInfoCollection;
}
+ /** This method provides immutable access to `methodAccessInfoCollection`. */
+ public MethodAccessInfoCollection getMethodAccessInfoCollection() {
+ return methodAccessInfoCollection;
+ }
+
/** This method provides immutable access to `objectAllocationInfoCollection`. */
public ObjectAllocationInfoCollection getObjectAllocationInfoCollection() {
return objectAllocationInfoCollection;
@@ -1020,15 +995,9 @@
lens.rewriteMethods(methodsTargetedByInvokeDynamic),
lens.rewriteMethods(virtualMethodsTargetedByInvokeDirect),
lens.rewriteMethods(liveMethods),
- fieldAccessInfoCollection != null
- ? fieldAccessInfoCollection.rewrittenWithLens(definitionSupplier, lens)
- : null,
+ fieldAccessInfoCollection.rewrittenWithLens(definitionSupplier, lens),
+ methodAccessInfoCollection.rewrittenWithLens(definitionSupplier, lens),
objectAllocationInfoCollection.rewrittenWithLens(definitionSupplier, lens),
- rewriteInvokesWithContexts(virtualInvokes, lens),
- rewriteInvokesWithContexts(interfaceInvokes, lens),
- rewriteInvokesWithContexts(superInvokes, lens),
- rewriteInvokesWithContexts(directInvokes, lens),
- rewriteInvokesWithContexts(staticInvokes, lens),
// TODO(sgjesse): Rewrite call sites as well? Right now they are only used by minification
// after second tree shaking.
callSites,
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 98def17..cb7847c 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -8,7 +8,6 @@
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;
-import static com.android.tools.r8.shaking.EnqueuerUtils.toImmutableSortedMap;
import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.cf.code.CfFieldInstruction;
@@ -61,6 +60,7 @@
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.LookupLambdaTarget;
import com.android.tools.r8.graph.LookupTarget;
+import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
import com.android.tools.r8.graph.PresortedComparable;
import com.android.tools.r8.graph.ProgramField;
@@ -141,6 +141,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.BiConsumer;
+import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -200,13 +201,10 @@
private AnnotationRemover.Builder annotationRemoverBuilder;
private final EnqueuerDefinitionSupplier enqueuerDefinitionSupplier;
- private final Map<DexMethod, ProgramMethodSet> virtualInvokes = new IdentityHashMap<>();
- private final Map<DexMethod, ProgramMethodSet> interfaceInvokes = new IdentityHashMap<>();
- private final Map<DexMethod, ProgramMethodSet> superInvokes = new IdentityHashMap<>();
- private final Map<DexMethod, ProgramMethodSet> directInvokes = new IdentityHashMap<>();
- private final Map<DexMethod, ProgramMethodSet> staticInvokes = new IdentityHashMap<>();
private final FieldAccessInfoCollectionImpl fieldAccessInfoCollection =
new FieldAccessInfoCollectionImpl();
+ private final MethodAccessInfoCollection.Builder methodAccessInfoCollection =
+ MethodAccessInfoCollection.builder();
private final ObjectAllocationInfoCollectionImpl.Builder objectAllocationInfoCollection;
private final Set<DexCallSite> callSites = Sets.newIdentityHashSet();
@@ -784,11 +782,11 @@
//
private boolean registerMethodWithTargetAndContext(
- Map<DexMethod, ProgramMethodSet> seen, DexMethod method, ProgramMethod context) {
+ BiPredicate<DexMethod, ProgramMethod> registration, DexMethod method, ProgramMethod context) {
DexType baseHolder = method.holder.toBaseType(appView.dexItemFactory());
if (baseHolder.isClassType()) {
markTypeAsLive(baseHolder, clazz -> graphReporter.reportClassReferencedFrom(clazz, context));
- return seen.computeIfAbsent(method, ignore -> ProgramMethodSet.create()).add(context);
+ return registration.test(method, context);
}
return false;
}
@@ -1078,7 +1076,8 @@
private void traceInvokeDirect(
DexMethod invokedMethod, ProgramMethod context, KeepReason reason) {
- if (!registerMethodWithTargetAndContext(directInvokes, invokedMethod, context)) {
+ if (!registerMethodWithTargetAndContext(
+ methodAccessInfoCollection::registerInvokeDirectInContext, invokedMethod, context)) {
return;
}
if (Log.ENABLED) {
@@ -1098,7 +1097,8 @@
private void traceInvokeInterface(
DexMethod method, ProgramMethod context, KeepReason keepReason) {
- if (!registerMethodWithTargetAndContext(interfaceInvokes, method, context)) {
+ if (!registerMethodWithTargetAndContext(
+ methodAccessInfoCollection::registerInvokeInterfaceInContext, method, context)) {
return;
}
if (Log.ENABLED) {
@@ -1137,7 +1137,8 @@
if (invokedMethod == dexItemFactory.proxyMethods.newProxyInstance) {
pendingReflectiveUses.add(context);
}
- if (!registerMethodWithTargetAndContext(staticInvokes, invokedMethod, context)) {
+ if (!registerMethodWithTargetAndContext(
+ methodAccessInfoCollection::registerInvokeStaticInContext, invokedMethod, context)) {
return;
}
if (Log.ENABLED) {
@@ -1151,7 +1152,8 @@
// We have to revisit super invokes based on the context they are found in. The same
// method descriptor will hit different targets, depending on the context it is used in.
DexMethod actualTarget = getInvokeSuperTarget(invokedMethod, context);
- if (!registerMethodWithTargetAndContext(superInvokes, invokedMethod, context)) {
+ if (!registerMethodWithTargetAndContext(
+ methodAccessInfoCollection::registerInvokeSuperInContext, invokedMethod, context)) {
return;
}
if (Log.ENABLED) {
@@ -1180,7 +1182,8 @@
// Revisit the current method to implicitly add -keep rule for items with reflective access.
pendingReflectiveUses.add(context);
}
- if (!registerMethodWithTargetAndContext(virtualInvokes, invokedMethod, context)) {
+ if (!registerMethodWithTargetAndContext(
+ methodAccessInfoCollection::registerInvokeVirtualInContext, invokedMethod, context)) {
return;
}
if (Log.ENABLED) {
@@ -3037,13 +3040,8 @@
toSortedDescriptorSet(liveMethods.getItems()),
// Filter out library fields and pinned fields, because these are read by default.
fieldAccessInfoCollection,
+ methodAccessInfoCollection.build(),
objectAllocationInfoCollection.build(appInfo),
- // TODO(b/132593519): Do we require these sets to be sorted for determinism?
- toImmutableSortedMap(virtualInvokes, PresortedComparable::slowCompare),
- toImmutableSortedMap(interfaceInvokes, PresortedComparable::slowCompare),
- toImmutableSortedMap(superInvokes, PresortedComparable::slowCompare),
- toImmutableSortedMap(directInvokes, PresortedComparable::slowCompare),
- toImmutableSortedMap(staticInvokes, PresortedComparable::slowCompare),
callSites,
keepInfo,
rootSet.mayHaveSideEffects,
diff --git a/src/main/java/com/android/tools/r8/utils/BiForEachable.java b/src/main/java/com/android/tools/r8/utils/BiForEachable.java
new file mode 100644
index 0000000..f1dd413
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/BiForEachable.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import java.util.function.BiConsumer;
+
+public interface BiForEachable<S, T> {
+
+ void forEach(BiConsumer<S, T> consumer);
+}
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index 3009e49..c899a75 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -6,9 +6,31 @@
import com.android.tools.r8.utils.StringUtils.BraceType;
import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.IntFunction;
public class MapUtils {
+ public static <K, V> Map<K, V> map(
+ Map<K, V> map,
+ IntFunction<Map<K, V>> factory,
+ Function<K, K> keyMapping,
+ Function<V, V> valueMapping,
+ BiFunction<V, V, V> valueMerger) {
+ Map<K, V> result = factory.apply(map.size());
+ map.forEach(
+ (key, value) -> {
+ K newKey = keyMapping.apply(key);
+ V newValue = valueMapping.apply(value);
+ V existingValue = result.put(newKey, newValue);
+ if (existingValue != null) {
+ result.put(newKey, valueMerger.apply(existingValue, newValue));
+ }
+ });
+ return result;
+ }
+
public static <T> void removeIdentityMappings(Map<T, T> map) {
map.entrySet().removeIf(entry -> entry.getKey() == entry.getValue());
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
index 0732bcb..2412cfd 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
@@ -4,9 +4,11 @@
package com.android.tools.r8.utils.collections;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
@@ -15,26 +17,34 @@
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
-import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
import java.util.stream.Stream;
public class ProgramMethodSet implements Iterable<ProgramMethod> {
- private static final ProgramMethodSet EMPTY = new ProgramMethodSet(ImmutableMap.of());
+ private static final ProgramMethodSet EMPTY = new ProgramMethodSet(ImmutableMap::of);
- private Map<DexMethod, ProgramMethod> backing;
+ private final Map<DexMethod, ProgramMethod> backing;
+ private final Supplier<? extends Map<DexMethod, ProgramMethod>> backingFactory;
- ProgramMethodSet(Map<DexMethod, ProgramMethod> backing) {
+ protected ProgramMethodSet(Supplier<? extends Map<DexMethod, ProgramMethod>> backingFactory) {
+ this(backingFactory, backingFactory.get());
+ }
+
+ protected ProgramMethodSet(
+ Supplier<? extends Map<DexMethod, ProgramMethod>> backingFactory,
+ Map<DexMethod, ProgramMethod> backing) {
this.backing = backing;
+ this.backingFactory = backingFactory;
}
public static ProgramMethodSet create() {
- return new ProgramMethodSet(new IdentityHashMap<>());
+ return new ProgramMethodSet(IdentityHashMap::new);
}
public static ProgramMethodSet create(int capacity) {
- return new ProgramMethodSet(new IdentityHashMap<>(capacity));
+ return new ProgramMethodSet(IdentityHashMap::new, new IdentityHashMap<>(capacity));
}
public static ProgramMethodSet create(ProgramMethod element) {
@@ -44,15 +54,11 @@
}
public static ProgramMethodSet createConcurrent() {
- return new ProgramMethodSet(new ConcurrentHashMap<>());
+ return new ProgramMethodSet(ConcurrentHashMap::new);
}
public static ProgramMethodSet createLinked() {
- return new ProgramMethodSet(new LinkedHashMap<>());
- }
-
- public static ProgramMethodSet createSorted() {
- return new ProgramMethodSet(new TreeMap<>(DexMethod::slowCompareTo));
+ return new ProgramMethodSet(LinkedHashMap::new);
}
public static ProgramMethodSet empty() {
@@ -107,6 +113,18 @@
return remove(method.getReference());
}
+ public ProgramMethodSet rewrittenWithLens(DexDefinitionSupplier definitions, GraphLens lens) {
+ ProgramMethodSet rewritten = new ProgramMethodSet(backingFactory);
+ forEach(
+ method -> {
+ ProgramMethod newMethod = lens.mapProgramMethod(method, definitions);
+ if (newMethod != null) {
+ rewritten.add(newMethod);
+ }
+ });
+ return rewritten;
+ }
+
public int size() {
return backing.size();
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/SortedProgramMethodSet.java b/src/main/java/com/android/tools/r8/utils/collections/SortedProgramMethodSet.java
index 4c6db49..6f6d1a5 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/SortedProgramMethodSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/SortedProgramMethodSet.java
@@ -4,8 +4,10 @@
package com.android.tools.r8.utils.collections;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.utils.ForEachable;
import com.android.tools.r8.utils.ForEachableUtils;
@@ -13,11 +15,12 @@
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
+import java.util.function.Supplier;
public class SortedProgramMethodSet extends ProgramMethodSet {
- private SortedProgramMethodSet(TreeMap<DexMethod, ProgramMethod> backing) {
- super(backing);
+ private SortedProgramMethodSet(Supplier<TreeMap<DexMethod, ProgramMethod>> backingFactory) {
+ super(backingFactory);
}
public static SortedProgramMethodSet create() {
@@ -32,12 +35,19 @@
public static SortedProgramMethodSet create(ForEachable<ProgramMethod> methods) {
SortedProgramMethodSet result =
- new SortedProgramMethodSet(new TreeMap<>(DexMethod::slowCompareTo));
+ new SortedProgramMethodSet(() -> new TreeMap<>(DexMethod::slowCompareTo));
methods.forEach(result::add);
return result;
}
@Override
+ public SortedProgramMethodSet rewrittenWithLens(
+ DexDefinitionSupplier definitions, GraphLens lens) {
+ return create(
+ consumer -> forEach(method -> consumer.accept(lens.mapProgramMethod(method, definitions))));
+ }
+
+ @Override
public Set<DexEncodedMethod> toDefinitionSet() {
Comparator<DexEncodedMethod> comparator =
(x, y) -> x.getReference().slowCompareTo(y.getReference());