Update a few resolution clients to account for multiple results
Bug: b/230289235
Change-Id: I41c99e1de1fa83894a39cd5c038c36901d38be41
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
index 9630887..a6384e1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
@@ -80,7 +80,8 @@
private boolean shouldRewriteInvokeToThrow(
CfInvoke invoke, MethodResolutionResult resolutionResult) {
- if (resolutionResult.isArrayCloneMethodResult()) {
+ if (resolutionResult.isArrayCloneMethodResult()
+ || resolutionResult.isMultiMethodResolutionResult()) {
return false;
}
if (resolutionResult.isFailedResolution()) {
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 05ad88c..a39d9c7 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -1140,13 +1140,16 @@
}
private void disableClosedWorldReasoning(DexMethod reference, ProgramMethod context) {
- SingleResolutionResult<?> resolutionResult =
- resolveMethod(reference, context, KeepReason.methodHandleReferencedIn(context));
- if (resolutionResult != null && resolutionResult.getResolvedHolder().isProgramClass()) {
- applyMinimumKeepInfoWhenLiveOrTargeted(
- resolutionResult.getResolvedProgramMethod(),
- KeepMethodInfo.newEmptyJoiner().disallowClosedWorldReasoning());
- }
+ resolveMethod(reference, context, KeepReason.methodHandleReferencedIn(context))
+ .forEachMethodResolutionResult(
+ resolutionResult -> {
+ if (resolutionResult.isSingleResolution()
+ && resolutionResult.asSingleResolution().getResolvedHolder().isProgramClass()) {
+ applyMinimumKeepInfoWhenLiveOrTargeted(
+ resolutionResult.getResolvedProgramMethod(),
+ KeepMethodInfo.newEmptyJoiner().disallowClosedWorldReasoning());
+ }
+ });
}
void traceCheckCast(DexType type, ProgramMethod currentMethod, boolean ignoreCompatRules) {
@@ -2296,56 +2299,68 @@
return fieldResolutionResult;
}
- private SingleResolutionResult<?> resolveMethod(
+ private MethodResolutionResult resolveMethod(
DexMethod method, ProgramDefinition context, KeepReason reason) {
// Record the references in case they are not program types.
MethodResolutionResult resolutionResult =
appInfo.unsafeResolveMethodDueToDexFormatLegacy(method);
- if (resolutionResult.isFailedResolution()) {
- markFailedMethodResolutionTargets(
- method, resolutionResult.asFailedResolution(), context, reason);
- recordMethodReference(method, context, this::recordFoundClass, this::reportMissingClass);
- } else {
- recordMethodReference(method, context);
- }
- return resolutionResult.asSingleResolution();
+ resolutionResult.visitMethodResolutionResults(
+ result -> recordMethodReference(method, context),
+ failedResult -> {
+ markFailedMethodResolutionTargets(
+ method, resolutionResult.asFailedResolution(), context, reason);
+ recordMethodReference(method, context, this::recordFoundClass, this::reportMissingClass);
+ });
+ return resolutionResult;
}
- private SingleResolutionResult<?> resolveMethod(
+ private MethodResolutionResult resolveMethod(
DexMethod method, ProgramDefinition context, KeepReason reason, boolean interfaceInvoke) {
// Record the references in case they are not program types.
- MethodResolutionResult resolutionResult = appInfo.resolveMethodLegacy(method, interfaceInvoke);
- if (resolutionResult.isSingleResolution()) {
- recordMethodReference(
- method, resolutionResult.getResolutionPair().asProgramDerivedContext(context));
- } else {
- assert resolutionResult.isFailedResolution();
- markFailedMethodResolutionTargets(
- method, resolutionResult.asFailedResolution(), context, reason);
- recordMethodReference(method, context, this::recordFoundClass, this::reportMissingClass);
- }
- return resolutionResult.asSingleResolution();
+ MethodResolutionResult methodResolutionResult =
+ appInfo.resolveMethodLegacy(method, interfaceInvoke);
+ methodResolutionResult.forEachMethodResolutionResult(
+ resolutionResult -> {
+ if (resolutionResult.isSingleResolution()) {
+ recordMethodReference(
+ method, resolutionResult.getResolutionPair().asProgramDerivedContext(context));
+ } else {
+ assert resolutionResult.isFailedResolution();
+ markFailedMethodResolutionTargets(
+ method, resolutionResult.asFailedResolution(), context, reason);
+ recordMethodReference(
+ method, context, this::recordFoundClass, this::reportMissingClass);
+ }
+ });
+ return methodResolutionResult;
}
private void handleInvokeOfStaticTarget(
DexMethod reference, ProgramDefinition context, KeepReason reason) {
- SingleResolutionResult<?> resolution = resolveMethod(reference, context, reason);
- if (resolution == null || resolution.getResolvedHolder().isNotProgramClass()) {
- return;
- }
- DexProgramClass clazz = resolution.getResolvedHolder().asProgramClass();
- DexEncodedMethod encodedMethod = resolution.getResolvedMethod();
+ resolveMethod(reference, context, reason)
+ .forEachMethodResolutionResult(
+ resolutionResult -> {
+ if (!resolutionResult.isSingleResolution()) {
+ return;
+ }
+ SingleResolutionResult<?> resolution = resolutionResult.asSingleResolution();
+ if (resolution.getResolvedHolder().isNotProgramClass()) {
+ return;
+ }
+ DexProgramClass clazz = resolution.getResolvedHolder().asProgramClass();
+ DexEncodedMethod encodedMethod = resolution.getResolvedMethod();
- // We have to mark the resolved method as targeted even if it cannot actually be invoked
- // to make sure the invocation will keep failing in the appropriate way.
- ProgramMethod method = new ProgramMethod(clazz, encodedMethod);
- markMethodAsTargeted(method, reason);
+ // We have to mark the resolved method as targeted even if it cannot actually be
+ // invoked to make sure the invocation will keep failing in the appropriate way.
+ ProgramMethod method = new ProgramMethod(clazz, encodedMethod);
+ markMethodAsTargeted(method, reason);
- // Only mark methods for which invocation will succeed at runtime live.
- if (encodedMethod.isStatic()) {
- markDirectAndIndirectClassInitializersAsLive(clazz);
- markDirectStaticOrConstructorMethodAsLive(method, reason);
- }
+ // Only mark methods for which invocation will succeed at runtime live.
+ if (encodedMethod.isStatic()) {
+ markDirectAndIndirectClassInitializersAsLive(clazz);
+ markDirectStaticOrConstructorMethodAsLive(method, reason);
+ }
+ });
}
void markDirectAndIndirectClassInitializersAsLive(DexProgramClass clazz) {
@@ -2793,55 +2808,61 @@
getReachableVirtualTargets(currentClass)
.forEach(
(resolutionSearchKey, contexts) -> {
- SingleResolutionResult<?> singleResolution =
- appInfo
- .resolveMethodLegacy(
- resolutionSearchKey.method, resolutionSearchKey.isInterface)
- .asSingleResolution();
- if (singleResolution == null) {
- assert false : "Should not be null";
- return;
- }
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));
- }
- }
- });
- });
+ appInfo
+ .resolveMethodLegacy(resolutionSearchKey.method, resolutionSearchKey.isInterface)
+ .forEachMethodResolutionResult(
+ resolutionResult -> {
+ SingleResolutionResult<?> singleResolution =
+ resolutionResult.asSingleResolution();
+ if (singleResolution == null) {
+ assert false : "Should not be null";
+ return;
+ }
+ 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));
+ }
+ }
+ });
+ });
+ });
});
}
@@ -3245,76 +3266,82 @@
return;
}
- SingleResolutionResult<?> resolution = resolveMethod(method, context, reason, interfaceInvoke);
- if (resolution == null) {
- return;
- }
+ resolveMethod(method, context, reason, interfaceInvoke)
+ .forEachMethodResolutionResult(
+ resolutionResult -> {
+ if (!resolutionResult.isSingleResolution()) {
+ return;
+ }
+ SingleResolutionResult<?> resolution = resolutionResult.asSingleResolution();
+ // Note that all virtual methods derived from library methods are kept regardless of
+ // being reachable, so the following only needs to consider reachable targets in the
+ // program.
+ // TODO(b/70160030): Revise this to support tree shaking library methods on
+ // non-escaping types.
+ DexProgramClass initialResolutionHolder =
+ resolution.getInitialResolutionHolder().asProgramClass();
+ if (initialResolutionHolder == null) {
+ recordMethodReference(method, context);
+ return;
+ }
- // Note that all virtual methods derived from library methods are kept regardless of being
- // reachable, so the following only needs to consider reachable targets in the program.
- // TODO(b/70160030): Revise this to support tree shaking library methods on non-escaping types.
- DexProgramClass initialResolutionHolder =
- resolution.getInitialResolutionHolder().asProgramClass();
- if (initialResolutionHolder == null) {
- recordMethodReference(method, context);
- return;
- }
+ if (resolution.getResolvedHolder().isNotProgramClass()) {
+ // TODO(b/70160030): If the resolution is on a library method, then the keep edge
+ // needs to go directly to the target method in the program. Thus this method will
+ // need to ensure that 'reason' is not already reported (eg, must be delayed /
+ // non-witness) and report that for each possible target edge below.
+ return;
+ }
- if (resolution.getResolvedHolder().isNotProgramClass()) {
- // TODO(b/70160030): If the resolution is on a library method, then the keep edge needs to go
- // directly to the target method in the program. Thus this method will need to ensure that
- // 'reason' is not already reported (eg, must be delayed / non-witness) and report that for
- // each possible target edge below.
- return;
- }
+ DexProgramClass contextHolder = context.getContextClass();
+ // 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);
+ ProgramMethodSet seenContexts =
+ getReachableVirtualTargets(initialResolutionHolder).get(resolutionSearchKey);
+ if (seenContexts != null) {
+ seenContexts.add(context);
+ graphReporter.registerMethod(resolution.getResolvedMethod(), reason);
+ return;
+ }
- DexProgramClass contextHolder = context.getContextClass();
- // 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);
- ProgramMethodSet seenContexts =
- getReachableVirtualTargets(initialResolutionHolder).get(resolutionSearchKey);
- if (seenContexts != null) {
- seenContexts.add(context);
- graphReporter.registerMethod(resolution.getResolvedMethod(), reason);
- return;
- }
+ if (Log.ENABLED) {
+ Log.verbose(getClass(), "Marking virtual method `%s` as reachable.", method);
+ }
- if (Log.ENABLED) {
- Log.verbose(getClass(), "Marking virtual method `%s` as reachable.", method);
- }
+ // We have to mark the resolution targeted, even if it does not become live, we
+ // need at least an abstract version of it so that it can be targeted.
+ DexProgramClass resolvedHolder = resolution.getResolvedHolder().asProgramClass();
+ DexEncodedMethod resolvedMethod = resolution.getResolvedMethod();
+ markMethodAsTargeted(new ProgramMethod(resolvedHolder, resolvedMethod), reason);
+ if (resolution.isAccessibleForVirtualDispatchFrom(contextHolder, appInfo).isFalse()) {
+ // Not accessible from this context, so this call will cause a runtime exception.
+ return;
+ }
- // We have to mark the resolution targeted, even if it does not become live, we
- // need at least an abstract version of it so that it can be targeted.
- DexProgramClass resolvedHolder = resolution.getResolvedHolder().asProgramClass();
- DexEncodedMethod resolvedMethod = resolution.getResolvedMethod();
- markMethodAsTargeted(new ProgramMethod(resolvedHolder, resolvedMethod), reason);
- if (resolution.isAccessibleForVirtualDispatchFrom(contextHolder, appInfo).isFalse()) {
- // Not accessible from this context, so this call will cause a runtime exception.
- return;
- }
+ // The method resolved and is accessible, so currently live overrides become live.
+ reachableVirtualTargets
+ .computeIfAbsent(initialResolutionHolder, ignoreArgument(HashMap::new))
+ .computeIfAbsent(resolutionSearchKey, ignoreArgument(ProgramMethodSet::create))
+ .add(context);
- // The method resolved and is accessible, so currently live overrides become live.
- reachableVirtualTargets
- .computeIfAbsent(initialResolutionHolder, ignoreArgument(HashMap::new))
- .computeIfAbsent(resolutionSearchKey, ignoreArgument(ProgramMethodSet::create))
- .add(context);
-
- resolution
- .lookupVirtualDispatchTargets(
- contextHolder,
- appInfo,
- (type, subTypeConsumer, lambdaConsumer) ->
- objectAllocationInfoCollection.forEachInstantiatedSubType(
- type, subTypeConsumer, lambdaConsumer, appInfo),
- definition -> keepInfo.isPinned(definition.getReference(), appInfo, options))
- .forEach(
- target ->
- markVirtualDispatchTargetAsLive(
- target,
- programMethod ->
- graphReporter.reportReachableMethodAsLive(
- resolvedMethod.getReference(), programMethod)));
+ resolution
+ .lookupVirtualDispatchTargets(
+ contextHolder,
+ appInfo,
+ (type, subTypeConsumer, lambdaConsumer) ->
+ objectAllocationInfoCollection.forEachInstantiatedSubType(
+ type, subTypeConsumer, lambdaConsumer, appInfo),
+ definition -> keepInfo.isPinned(definition.getReference(), appInfo, options))
+ .forEach(
+ target ->
+ markVirtualDispatchTargetAsLive(
+ target,
+ programMethod ->
+ graphReporter.reportReachableMethodAsLive(
+ resolvedMethod.getReference(), programMethod)));
+ });
}
private void markVirtualDispatchTargetAsLive(
@@ -3392,49 +3419,59 @@
// Package protected due to entry point from worklist.
void markSuperMethodAsReachable(DexMethod reference, ProgramMethod from) {
KeepReason reason = KeepReason.targetedBySuperFrom(from);
- SingleResolutionResult<?> resolution = resolveMethod(reference, from, reason);
- if (resolution == null) {
- return;
- }
- // If the resolution is in the program, mark it targeted.
- if (resolution.getResolvedHolder().isProgramClass()) {
- markMethodAsTargeted(
- new ProgramMethod(
- resolution.getResolvedHolder().asProgramClass(), resolution.getResolvedMethod()),
- reason);
- }
- // If invoke target is invalid (inaccessible or not an instance-method) record it and stop.
- DexClassAndMethod target = resolution.lookupInvokeSuperTarget(from.getHolder(), appInfo);
- if (target == null) {
- failedMethodResolutionTargets.add(resolution.getResolvedMethod().getReference());
- analyses.forEach(
- analyses ->
- analyses.notifyFailedMethodResolutionTarget(
- resolution.getResolvedMethod(), workList));
- return;
- }
+ resolveMethod(reference, from, reason)
+ .forEachMethodResolutionResult(
+ resolutionResult -> {
+ if (!resolutionResult.isSingleResolution()) {
+ return;
+ }
+ SingleResolutionResult<?> resolution = resolutionResult.asSingleResolution();
+ // If the resolution is in the program, mark it targeted.
+ if (resolution.getResolvedHolder().isProgramClass()) {
+ markMethodAsTargeted(
+ new ProgramMethod(
+ resolution.getResolvedHolder().asProgramClass(),
+ resolution.getResolvedMethod()),
+ reason);
+ }
+ // If invoke target is invalid (inaccessible or not an instance-method) record it and
+ // stop.
+ DexClassAndMethod target =
+ resolution.lookupInvokeSuperTarget(from.getHolder(), appInfo);
+ if (target == null) {
+ failedMethodResolutionTargets.add(resolution.getResolvedMethod().getReference());
+ analyses.forEach(
+ analyses ->
+ analyses.notifyFailedMethodResolutionTarget(
+ resolution.getResolvedMethod(), workList));
+ return;
+ }
- DexProgramClass clazz = target.getHolder().asProgramClass();
- if (clazz == null) {
- return;
- }
+ DexProgramClass clazz = target.getHolder().asProgramClass();
+ if (clazz == null) {
+ return;
+ }
- ProgramMethod method = target.asProgramMethod();
+ ProgramMethod method = target.asProgramMethod();
- if (Log.ENABLED) {
- Log.verbose(
- getClass(), "Adding super constraint from `%s` to `%s`", from, target.getReference());
- }
- if (superInvokeDependencies
- .computeIfAbsent(from.getDefinition(), ignore -> ProgramMethodSet.create())
- .add(method)) {
- if (liveMethods.contains(from)) {
- markMethodAsTargeted(method, KeepReason.invokedViaSuperFrom(from));
- if (!target.getAccessFlags().isAbstract()) {
- markVirtualMethodAsLive(method, KeepReason.invokedViaSuperFrom(from));
- }
- }
- }
+ if (Log.ENABLED) {
+ Log.verbose(
+ getClass(),
+ "Adding super constraint from `%s` to `%s`",
+ from,
+ target.getReference());
+ }
+ if (superInvokeDependencies
+ .computeIfAbsent(from.getDefinition(), ignore -> ProgramMethodSet.create())
+ .add(method)) {
+ if (liveMethods.contains(from)) {
+ markMethodAsTargeted(method, KeepReason.invokedViaSuperFrom(from));
+ if (!target.getAccessFlags().isAbstract()) {
+ markVirtualMethodAsLive(method, KeepReason.invokedViaSuperFrom(from));
+ }
+ }
+ }
+ });
}
// Returns the set of live types.