Trace failure dependencies in markProgramMethodOverridesAsLive
Change-Id: I931ecaa708e852af8561673c1cb2fa5e500ebb24
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index dc8e0bb..12a5e8d 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -794,15 +794,23 @@
return builder.resolve(clazz);
}
- // Non-private lookup (ie, not resolution) to find interface targets.
- DexClassAndMethod lookupMaximallySpecificTarget(DexClass clazz, DexMethod method) {
- MaximallySpecificMethodsBuilder builder = new MaximallySpecificMethodsBuilder();
- resolveMethodStep3Helper(method.getProto(), method.getName(), clazz, builder);
- return builder.lookup();
+ MethodResolutionResult resolveMaximallySpecificTarget(DexClass clazz, DexMethod method) {
+ return resolveMaximallySpecificTargetHelper(clazz, method).resolve(clazz);
}
- // Non-private lookup (ie, not resolution) to find interface targets.
- DexClassAndMethod lookupMaximallySpecificTarget(LambdaDescriptor lambda, DexMethod method) {
+ private MaximallySpecificMethodsBuilder resolveMaximallySpecificTargetHelper(
+ DexClass clazz, DexMethod method) {
+ MaximallySpecificMethodsBuilder builder = new MaximallySpecificMethodsBuilder();
+ resolveMethodStep3Helper(method.getProto(), method.getName(), clazz, builder);
+ return builder;
+ }
+
+ MethodResolutionResult resolveMaximallySpecificTarget(LambdaDescriptor lambda, DexMethod method) {
+ return resolveMaximallySpecificTargetHelper(lambda, method).internalResolve(null);
+ }
+
+ private MaximallySpecificMethodsBuilder resolveMaximallySpecificTargetHelper(
+ LambdaDescriptor lambda, DexMethod method) {
MaximallySpecificMethodsBuilder builder = new MaximallySpecificMethodsBuilder();
resolveMethodStep3Helper(
method.getProto(),
@@ -810,7 +818,17 @@
dexItemFactory().objectType,
lambda.interfaces,
builder);
- return builder.lookup();
+ return builder;
+ }
+
+ // Non-private lookup (ie, not resolution) to find interface targets.
+ DexClassAndMethod lookupMaximallySpecificTarget(DexClass clazz, DexMethod method) {
+ return resolveMaximallySpecificTargetHelper(clazz, method).lookup();
+ }
+
+ // Non-private lookup (ie, not resolution) to find interface targets.
+ DexClassAndMethod lookupMaximallySpecificTarget(LambdaDescriptor lambda, DexMethod method) {
+ return resolveMaximallySpecificTargetHelper(lambda, method).lookup();
}
/** Helper method that builds the set of maximally specific methods. */
@@ -1059,10 +1077,7 @@
}
DexClassAndMethod lookup() {
- SingleResolutionResult result = internalResolve(null).asSingleResolution();
- return result != null
- ? DexClassAndMethod.create(result.getResolvedHolder(), result.getResolvedMethod())
- : null;
+ return internalResolve(null).getResolutionPair();
}
MethodResolutionResult resolve(DexClass initialResolutionHolder) {
diff --git a/src/main/java/com/android/tools/r8/graph/LookupResult.java b/src/main/java/com/android/tools/r8/graph/LookupResult.java
index 17d7f55..bc85814 100644
--- a/src/main/java/com/android/tools/r8/graph/LookupResult.java
+++ b/src/main/java/com/android/tools/r8/graph/LookupResult.java
@@ -5,9 +5,10 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess.LookupResultCollectionState;
+import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.function.Consumer;
public abstract class LookupResult {
@@ -28,18 +29,23 @@
return null;
}
- public final void forEach(Consumer<LookupTarget> onTarget) {
- forEach(onTarget::accept, onTarget::accept);
+ public final void forEach(Consumer<? super LookupTarget> onTarget) {
+ forEach(onTarget, onTarget);
}
public abstract void forEach(
- Consumer<DexClassAndMethod> onMethodTarget, Consumer<LookupLambdaTarget> onLambdaTarget);
+ Consumer<? super DexClassAndMethod> onMethodTarget,
+ Consumer<? super LookupLambdaTarget> onLambdaTarget);
+
+ public abstract void forEachFailureDependency(
+ Consumer<? super DexEncodedMethod> methodCausingFailureConsumer);
public static LookupResultSuccess createResult(
- Map<DexEncodedMethod, DexClassAndMethod> methodTargets,
+ DexClassAndMethodSet methodTargets,
List<LookupLambdaTarget> lambdaTargets,
+ List<DexEncodedMethod> methodsCausingFailure,
LookupResultCollectionState state) {
- return new LookupResultSuccess(methodTargets, lambdaTargets, state);
+ return new LookupResultSuccess(methodTargets, lambdaTargets, methodsCausingFailure, state);
}
public static LookupResultFailure createFailedResult() {
@@ -54,23 +60,31 @@
private static final LookupResultSuccess EMPTY_INSTANCE =
new LookupResultSuccess(
- Collections.emptyMap(),
+ DexClassAndMethodSet.empty(),
+ Collections.emptyList(),
Collections.emptyList(),
LookupResultCollectionState.Incomplete);
- private final Map<DexEncodedMethod, DexClassAndMethod> methodTargets;
+ private final DexClassAndMethodSet methodTargets;
private final List<LookupLambdaTarget> lambdaTargets;
+ private final List<DexEncodedMethod> methodsCausingFailure;
private LookupResultCollectionState state;
private LookupResultSuccess(
- Map<DexEncodedMethod, DexClassAndMethod> methodTargets,
+ DexClassAndMethodSet methodTargets,
List<LookupLambdaTarget> lambdaTargets,
+ List<DexEncodedMethod> methodsCausingFailure,
LookupResultCollectionState state) {
this.methodTargets = methodTargets;
this.lambdaTargets = lambdaTargets;
+ this.methodsCausingFailure = methodsCausingFailure;
this.state = state;
}
+ public static Builder builder() {
+ return new Builder();
+ }
+
public boolean isEmpty() {
return methodTargets.isEmpty() && lambdaTargets.isEmpty();
}
@@ -85,14 +99,21 @@
@Override
public void forEach(
- Consumer<DexClassAndMethod> onMethodTarget, Consumer<LookupLambdaTarget> onLambdaTarget) {
- methodTargets.forEach((ignore, method) -> onMethodTarget.accept(method));
+ Consumer<? super DexClassAndMethod> onMethodTarget,
+ Consumer<? super LookupLambdaTarget> onLambdaTarget) {
+ methodTargets.forEach(onMethodTarget);
lambdaTargets.forEach(onLambdaTarget);
}
+ @Override
+ public void forEachFailureDependency(
+ Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) {
+ methodsCausingFailure.forEach(methodCausingFailureConsumer);
+ }
+
public boolean contains(DexEncodedMethod method) {
// Containment of a method in the lookup results only pertains to the method targets.
- return methodTargets.containsKey(method);
+ return methodTargets.contains(method);
}
@Override
@@ -124,7 +145,7 @@
}
// TODO(b/150932978): Check lambda targets implementation methods.
if (methodTargets.size() == 1) {
- return methodTargets.values().iterator().next();
+ return methodTargets.iterator().next();
} else if (lambdaTargets.size() == 1) {
return lambdaTargets.get(0);
}
@@ -135,6 +156,38 @@
Complete,
Incomplete,
}
+
+ public static class Builder {
+
+ private final DexClassAndMethodSet methodTargets = DexClassAndMethodSet.create();
+ private final List<LookupLambdaTarget> lambdaTargets = new ArrayList<>();
+ private final List<DexEncodedMethod> methodsCausingFailure = new ArrayList<>();
+ private LookupResultCollectionState state;
+
+ public Builder addMethodTarget(DexClassAndMethod methodTarget) {
+ methodTargets.add(methodTarget);
+ return this;
+ }
+
+ public Builder addLambdaTarget(LookupLambdaTarget lambdaTarget) {
+ lambdaTargets.add(lambdaTarget);
+ return this;
+ }
+
+ public Builder addMethodCausingFailure(DexEncodedMethod methodCausingFailure) {
+ methodsCausingFailure.add(methodCausingFailure);
+ return this;
+ }
+
+ public Builder setState(LookupResultCollectionState state) {
+ this.state = state;
+ return this;
+ }
+
+ public LookupResultSuccess build() {
+ return new LookupResultSuccess(methodTargets, lambdaTargets, methodsCausingFailure, state);
+ }
+ }
}
public static class LookupResultFailure extends LookupResult {
@@ -157,8 +210,15 @@
@Override
public void forEach(
- Consumer<DexClassAndMethod> onMethodTarget, Consumer<LookupLambdaTarget> onLambdaTarget) {
+ Consumer<? super DexClassAndMethod> onMethodTarget,
+ Consumer<? super LookupLambdaTarget> onLambdaTarget) {
// Nothing to iterate for a failed lookup.
}
+
+ @Override
+ public void forEachFailureDependency(
+ Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) {
+ // TODO: record and emit failure dependencies.
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
index c2cb1d4..b425a2a 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
+
+import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess.LookupResultCollectionState;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -10,12 +13,9 @@
import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.OptionalBool;
-import java.util.ArrayList;
+import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
import java.util.Collection;
import java.util.Collections;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
@@ -148,7 +148,9 @@
DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo);
public abstract LookupTarget lookupVirtualDispatchTarget(
- LambdaDescriptor lambdaInstance, AppInfoWithClassHierarchy appInfo);
+ LambdaDescriptor lambdaInstance,
+ AppInfoWithClassHierarchy appInfo,
+ Consumer<? super DexEncodedMethod> methodCausingFailureConsumer);
/** Result for a resolution that succeeds with a known declaration/definition. */
public static class SingleResolutionResult extends MethodResolutionResult
@@ -431,46 +433,47 @@
boolean isIncomplete =
pinnedPredicate.isPinned(resolvedHolder) && pinnedPredicate.isPinned(resolvedMethod);
return LookupResult.createResult(
- Collections.singletonMap(
- resolvedMethod, DexClassAndMethod.create(resolvedHolder, resolvedMethod)),
+ DexClassAndMethodSet.create(getResolutionPair()),
+ Collections.emptyList(),
Collections.emptyList(),
isIncomplete
? LookupResultCollectionState.Incomplete
: LookupResultCollectionState.Complete);
}
assert resolvedMethod.isNonPrivateVirtualMethod();
- Map<DexEncodedMethod, DexClassAndMethod> methodTargets = new IdentityHashMap<>();
- List<LookupLambdaTarget> lambdaTargets = new ArrayList<>();
+ LookupResultSuccess.Builder resultBuilder = LookupResultSuccess.builder();
LookupCompletenessHelper incompleteness = new LookupCompletenessHelper(pinnedPredicate);
instantiatedInfo.forEachInstantiatedSubType(
initialResolutionHolder.type,
subClass -> {
incompleteness.checkClass(subClass);
DexClassAndMethod dexClassAndMethod =
- lookupVirtualDispatchTarget(subClass, appInfo, resolvedHolder.type);
+ lookupVirtualDispatchTarget(
+ subClass, appInfo, resolvedHolder.type, resultBuilder::addMethodCausingFailure);
if (dexClassAndMethod != null) {
incompleteness.checkDexClassAndMethod(dexClassAndMethod);
addVirtualDispatchTarget(
- dexClassAndMethod, resolvedHolder.isInterface(), methodTargets);
+ dexClassAndMethod, resolvedHolder.isInterface(), resultBuilder);
}
},
lambda -> {
assert resolvedHolder.isInterface()
|| resolvedHolder.type == appInfo.dexItemFactory().objectType;
- LookupTarget target = lookupVirtualDispatchTarget(lambda, appInfo);
+ LookupTarget target =
+ lookupVirtualDispatchTarget(
+ lambda, appInfo, resultBuilder::addMethodCausingFailure);
if (target != null) {
if (target.isLambdaTarget()) {
- lambdaTargets.add(target.asLambdaTarget());
+ resultBuilder.addLambdaTarget(target.asLambdaTarget());
} else {
addVirtualDispatchTarget(
- target.asMethodTarget(), resolvedHolder.isInterface(), methodTargets);
+ target.asMethodTarget(), resolvedHolder.isInterface(), resultBuilder);
}
}
});
- return LookupResult.createResult(
- methodTargets,
- lambdaTargets,
- incompleteness.computeCollectionState(resolvedMethod.getReference(), appInfo));
+ return resultBuilder
+ .setState(incompleteness.computeCollectionState(resolvedMethod.getReference(), appInfo))
+ .build();
}
@Override
@@ -532,7 +535,7 @@
private static void addVirtualDispatchTarget(
DexClassAndMethod target,
boolean holderIsInterface,
- Map<DexEncodedMethod, DexClassAndMethod> result) {
+ LookupResultSuccess.Builder resultBuilder) {
DexEncodedMethod targetMethod = target.getDefinition();
assert !targetMethod.isPrivateMethod();
if (holderIsInterface) {
@@ -559,17 +562,17 @@
// }
//
if (targetMethod.isDefaultMethod()) {
- result.putIfAbsent(targetMethod, target);
+ resultBuilder.addMethodTarget(target);
}
// Default methods are looked up when looking at a specific subtype that does not override
// them. Otherwise, we would look up default methods that are actually never used.
// However, we have to add bridge methods, otherwise we can remove a bridge that will be
// used.
if (!targetMethod.accessFlags.isAbstract() && targetMethod.accessFlags.isBridge()) {
- result.putIfAbsent(targetMethod, target);
+ resultBuilder.addMethodTarget(target);
}
} else {
- result.putIfAbsent(targetMethod, target);
+ resultBuilder.addMethodTarget(target);
}
}
@@ -583,18 +586,21 @@
InstantiatedObject instance, AppInfoWithClassHierarchy appInfo) {
return instance.isClass()
? lookupVirtualDispatchTarget(instance.asClass(), appInfo)
- : lookupVirtualDispatchTarget(instance.asLambda(), appInfo);
+ : lookupVirtualDispatchTarget(instance.asLambda(), appInfo, emptyConsumer());
}
@Override
public DexClassAndMethod lookupVirtualDispatchTarget(
DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo) {
- return lookupVirtualDispatchTarget(dynamicInstance, appInfo, initialResolutionHolder.type);
+ return lookupVirtualDispatchTarget(
+ dynamicInstance, appInfo, initialResolutionHolder.type, emptyConsumer());
}
@Override
public LookupTarget lookupVirtualDispatchTarget(
- LambdaDescriptor lambdaInstance, AppInfoWithClassHierarchy appInfo) {
+ LambdaDescriptor lambdaInstance,
+ AppInfoWithClassHierarchy appInfo,
+ Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) {
if (lambdaInstance.getMainMethod().match(resolvedMethod)) {
DexMethod methodReference = lambdaInstance.implHandle.asMethod();
DexClass holder = appInfo.definitionForHolder(methodReference);
@@ -605,11 +611,15 @@
}
return new LookupLambdaTarget(lambdaInstance, method);
}
- return lookupMaximallySpecificDispatchTarget(lambdaInstance, appInfo);
+ return lookupMaximallySpecificDispatchTarget(
+ lambdaInstance, appInfo, methodCausingFailureConsumer);
}
private DexClassAndMethod lookupVirtualDispatchTarget(
- DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo, DexType resolutionHolder) {
+ DexClass dynamicInstance,
+ AppInfoWithClassHierarchy appInfo,
+ DexType resolutionHolder,
+ Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) {
assert appInfo.isSubtype(dynamicInstance.type, resolutionHolder)
: dynamicInstance.type + " is not a subtype of " + resolutionHolder;
// TODO(b/148591377): Enable this assertion.
@@ -618,7 +628,7 @@
if (resolvedMethod.isPrivateMethod()) {
// If the resolved reference is private there is no dispatch.
// This is assuming that the method is accessible, which implies self/nest access.
- return DexClassAndMethod.create(resolvedHolder, resolvedMethod);
+ return getResolutionPair();
}
boolean allowPackageBlocked = resolvedMethod.accessFlags.isPackagePrivate();
DexClass current = dynamicInstance;
@@ -645,17 +655,46 @@
if (!resolvedHolder.isInterface()) {
return null;
}
- return lookupMaximallySpecificDispatchTarget(dynamicInstance, appInfo);
+ return lookupMaximallySpecificDispatchTarget(
+ dynamicInstance, appInfo, methodCausingFailureConsumer);
}
private DexClassAndMethod lookupMaximallySpecificDispatchTarget(
- DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo) {
- return appInfo.lookupMaximallySpecificMethod(dynamicInstance, resolvedMethod.getReference());
+ DexClass dynamicInstance,
+ AppInfoWithClassHierarchy appInfo,
+ Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) {
+ MethodResolutionResult maximallySpecificResolutionResult =
+ appInfo.resolveMaximallySpecificTarget(dynamicInstance, resolvedMethod.getReference());
+ if (maximallySpecificResolutionResult.isSingleResolution()) {
+ return maximallySpecificResolutionResult.getResolutionPair();
+ }
+ if (maximallySpecificResolutionResult.isFailedResolution()) {
+ maximallySpecificResolutionResult
+ .asFailedResolution()
+ .forEachFailureDependency(methodCausingFailureConsumer);
+ return null;
+ }
+ assert maximallySpecificResolutionResult.isArrayCloneMethodResult();
+ return null;
}
private DexClassAndMethod lookupMaximallySpecificDispatchTarget(
- LambdaDescriptor lambdaDescriptor, AppInfoWithClassHierarchy appInfo) {
- return appInfo.lookupMaximallySpecificMethod(lambdaDescriptor, resolvedMethod.getReference());
+ LambdaDescriptor lambdaDescriptor,
+ AppInfoWithClassHierarchy appInfo,
+ Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) {
+ MethodResolutionResult maximallySpecificResolutionResult =
+ appInfo.resolveMaximallySpecificTarget(lambdaDescriptor, resolvedMethod.getReference());
+ if (maximallySpecificResolutionResult.isSingleResolution()) {
+ return maximallySpecificResolutionResult.getResolutionPair();
+ }
+ if (maximallySpecificResolutionResult.isFailedResolution()) {
+ maximallySpecificResolutionResult
+ .asFailedResolution()
+ .forEachFailureDependency(methodCausingFailureConsumer);
+ return null;
+ }
+ assert maximallySpecificResolutionResult.isArrayCloneMethodResult();
+ return null;
}
/**
@@ -773,7 +812,9 @@
@Override
public DexClassAndMethod lookupVirtualDispatchTarget(
- LambdaDescriptor lambdaInstance, AppInfoWithClassHierarchy appInfo) {
+ LambdaDescriptor lambdaInstance,
+ AppInfoWithClassHierarchy appInfo,
+ Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) {
return null;
}
}
@@ -823,7 +864,8 @@
return this;
}
- public void forEachFailureDependency(Consumer<DexEncodedMethod> methodCausingFailureConsumer) {
+ public void forEachFailureDependency(
+ Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) {
// Default failure has no dependencies.
}
@@ -871,7 +913,8 @@
}
@Override
- public void forEachFailureDependency(Consumer<DexEncodedMethod> methodCausingFailureConsumer) {
+ public void forEachFailureDependency(
+ Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) {
this.methodsCausingError.forEach(methodCausingFailureConsumer);
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java
index 72990cb..07c487f 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java
@@ -130,7 +130,7 @@
if (resolutionResult.isFailedResolution()) {
// TODO(b/190154391): Do we need to propagate argument information to the first
// virtual method above the inaccessible method in the class hierarchy?
- assert resolutionResult.isIllegalAccessErrorResult(subclass, appView.appInfo());
+ assert resolutionResult.asFailedResolution().hasMethodsCausingError();
return;
}
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 c4e0518..52d59bb 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -10,6 +10,7 @@
import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentifier;
import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
import static java.util.Collections.emptySet;
import com.android.tools.r8.Diagnostic;
@@ -63,6 +64,7 @@
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.InvalidCode;
import com.android.tools.r8.graph.LookupLambdaTarget;
+import com.android.tools.r8.graph.LookupResult;
import com.android.tools.r8.graph.LookupTarget;
import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.MethodResolutionResult;
@@ -378,7 +380,7 @@
private final ProgramMethodSet pendingReflectiveUses = ProgramMethodSet.createLinked();
/** Mapping of types to the resolved methods for that type along with the context. */
- private final Map<DexProgramClass, Map<ResolutionSearchKey, Set<DexProgramClass>>>
+ private final Map<DexProgramClass, Map<ResolutionSearchKey, ProgramMethodSet>>
reachableVirtualTargets = new IdentityHashMap<>();
/** Collection of keep requirements for the program. */
@@ -2460,7 +2462,7 @@
}
}
- private Map<ResolutionSearchKey, Set<DexProgramClass>> getReachableVirtualTargets(
+ private Map<ResolutionSearchKey, ProgramMethodSet> getReachableVirtualTargets(
DexProgramClass clazz) {
return reachableVirtualTargets.getOrDefault(clazz, Collections.emptyMap());
}
@@ -2480,26 +2482,46 @@
assert false : "Should not be null";
return;
}
- contexts.forEach(
- context ->
- singleResolution
- .lookupVirtualDispatchTargets(
- context,
- appInfo,
- (type, subTypeConsumer, lambdaConsumer) -> {
- assert appInfo.isSubtype(currentClass.type, type);
- instantiation.apply(subTypeConsumer, lambdaConsumer);
- },
- definition ->
- keepInfo.isPinned(definition.getReference(), appInfo, options))
- .forEach(
- target ->
- markVirtualDispatchTargetAsLive(
- target,
- programMethod ->
- graphReporter.reportReachableMethodAsLive(
- singleResolution.getResolvedMethod().getReference(),
- programMethod))));
+ Map<DexProgramClass, List<ProgramMethod>> contextsByClass = new IdentityHashMap<>();
+ for (ProgramMethod context : contexts) {
+ contextsByClass
+ .computeIfAbsent(context.getHolder(), ignoreKey(ArrayList::new))
+ .add(context);
+ }
+ contextsByClass.forEach(
+ (contextHolder, contextsInHolder) -> {
+ LookupResult lookupResult =
+ singleResolution.lookupVirtualDispatchTargets(
+ contextHolder,
+ appInfo,
+ (type, subTypeConsumer, lambdaConsumer) -> {
+ assert appInfo.isSubtype(currentClass.type, type);
+ instantiation.apply(subTypeConsumer, lambdaConsumer);
+ },
+ definition ->
+ keepInfo.isPinned(definition.getReference(), appInfo, options));
+ lookupResult.forEach(
+ target ->
+ markVirtualDispatchTargetAsLive(
+ target,
+ programMethod ->
+ graphReporter.reportReachableMethodAsLive(
+ singleResolution.getResolvedMethod().getReference(),
+ programMethod)));
+ lookupResult.forEachFailureDependency(
+ method -> {
+ DexProgramClass clazz =
+ getProgramClassOrNull(method.getHolderType(), contextHolder);
+ if (clazz != null) {
+ failedMethodResolutionTargets.add(method.getReference());
+ for (ProgramMethod context : contextsInHolder) {
+ markMethodAsTargeted(
+ new ProgramMethod(clazz, method),
+ KeepReason.invokedFrom(context));
+ }
+ }
+ });
+ });
});
}
@@ -2886,7 +2908,7 @@
}
private void markVirtualMethodAsReachable(
- DexMethod method, boolean interfaceInvoke, ProgramDefinition context, KeepReason reason) {
+ DexMethod method, boolean interfaceInvoke, ProgramMethod context, KeepReason reason) {
if (method.holder.isArrayType()) {
// This is an array type, so the actual class will be generated at runtime. We treat this
// like an invoke on a direct subtype of java.lang.Object that has no further subtypes.
@@ -2924,9 +2946,9 @@
// If the method has already been marked, just report the new reason for the resolved target and
// save the context to ensure correct lookup of virtual dispatch targets.
ResolutionSearchKey resolutionSearchKey = new ResolutionSearchKey(method, interfaceInvoke);
- Set<DexProgramClass> seenContexts = getReachableVirtualTargets(holder).get(resolutionSearchKey);
+ ProgramMethodSet seenContexts = getReachableVirtualTargets(holder).get(resolutionSearchKey);
if (seenContexts != null) {
- seenContexts.add(contextHolder);
+ seenContexts.add(context);
graphReporter.registerMethod(resolution.getResolvedMethod(), reason);
return;
}
@@ -2948,8 +2970,8 @@
// The method resolved and is accessible, so currently live overrides become live.
reachableVirtualTargets
.computeIfAbsent(holder, ignoreArgument(HashMap::new))
- .computeIfAbsent(resolutionSearchKey, ignoreArgument(Sets::newIdentityHashSet))
- .add(contextHolder);
+ .computeIfAbsent(resolutionSearchKey, ignoreArgument(ProgramMethodSet::create))
+ .add(context);
resolution
.lookupVirtualDispatchTargets(
@@ -4603,7 +4625,7 @@
virtualMethod -> {
keepInfo.joinMethod(
virtualMethod, joiner -> joiner.disallowOptimization().disallowShrinking());
- markVirtualMethodAsReachable(virtualMethod.getReference(), true, clazz, reason);
+ markVirtualMethodAsReachable(virtualMethod.getReference(), true, method, reason);
});
}
}
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleResolutionWithFailingDispatchTest.java b/src/test/java/com/android/tools/r8/resolution/SingleResolutionWithFailingDispatchTest.java
index 7d47371..a5acf15 100644
--- a/src/test/java/com/android/tools/r8/resolution/SingleResolutionWithFailingDispatchTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/SingleResolutionWithFailingDispatchTest.java
@@ -33,7 +33,7 @@
.addProgramClasses(Main.class, I.class, J.class)
.addProgramClassFileData(getProgramClassFileData())
.run(parameters.getRuntime(), Main.class)
- .apply(runResult -> inspectRunResult(runResult, false));
+ .apply(this::inspectRunResult);
}
@Test
@@ -45,12 +45,11 @@
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
- .apply(runResult -> inspectRunResult(runResult, true));
+ .apply(this::inspectRunResult);
}
- private void inspectRunResult(TestRunResult<?> runResult, boolean isR8) {
- if (parameters.isCfRuntime(CfVm.JDK11)
- || (isR8 && parameters.canUseDefaultAndStaticInterfaceMethods())) {
+ private void inspectRunResult(TestRunResult<?> runResult) {
+ if (parameters.isCfRuntime(CfVm.JDK11)) {
runResult.assertFailureWithErrorThatThrows(AbstractMethodError.class);
} else {
runResult.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);