Replace ResolutionResult hasSingleTarget() by isSingleResolution().
This CL also moves the implementation of existing lookup methods on
ResolutionResult to the SingleResolutionResult class.
Change-Id: Ibff85c3f2aee4405c9e83bd5f9db9f736b55d963
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
index bd71028..bdea265 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -47,14 +47,11 @@
return null;
}
+ /** Short-hand to get the single resolution method if resolution finds it, null otherwise. */
public final DexEncodedMethod getSingleTarget() {
return isSingleResolution() ? asSingleResolution().getResolvedMethod() : null;
}
- public final boolean hasSingleTarget() {
- return isSingleResolution();
- }
-
public abstract boolean isAccessibleFrom(DexProgramClass context, AppInfoWithSubtyping appInfo);
public abstract boolean isAccessibleForVirtualDispatchFrom(
@@ -64,114 +61,19 @@
public abstract boolean isValidVirtualTargetForDynamicDispatch();
- public DexEncodedMethod lookupInvokeSuperTarget(DexType context, AppInfo appInfo) {
- return null;
- }
+ /** Lookup the single target of an invoke-super on this resolution result if possible. */
+ public abstract DexEncodedMethod lookupInvokeSuperTarget(DexType context, AppInfo appInfo);
- public Set<DexEncodedMethod> lookupVirtualDispatchTargets(
+ public final Set<DexEncodedMethod> lookupVirtualDispatchTargets(
boolean isInterface, AppInfoWithSubtyping appInfo) {
return isInterface ? lookupInterfaceTargets(appInfo) : lookupVirtualTargets(appInfo);
}
- // TODO(b/140204899): Leverage refined receiver type if available.
- public Set<DexEncodedMethod> lookupVirtualTargets(AppInfoWithSubtyping appInfo) {
- assert isValidVirtualTarget(appInfo.app().options);
- // First add the target for receiver type method.type.
- DexEncodedMethod encodedMethod = getSingleTarget();
- Set<DexEncodedMethod> result = SetUtils.newIdentityHashSet(encodedMethod);
- // Add all matching targets from the subclass hierarchy.
- DexMethod method = encodedMethod.method;
- // TODO(b/140204899): Instead of subtypes of holder, we could iterate subtypes of refined
- // receiver type if available.
- for (DexType type : appInfo.subtypes(method.holder)) {
- DexClass clazz = appInfo.definitionFor(type);
- if (!clazz.isInterface()) {
- ResolutionResult methods = appInfo.resolveMethodOnClass(clazz, method);
- DexEncodedMethod target = methods.getSingleTarget();
- if (target != null && target.isVirtualMethod()) {
- result.add(target);
- }
- }
- }
- return result;
- }
+ public abstract Set<DexEncodedMethod> lookupVirtualTargets(AppInfoWithSubtyping appInfo);
- // TODO(b/140204899): Leverage refined receiver type if available.
- public Set<DexEncodedMethod> lookupInterfaceTargets(AppInfoWithSubtyping appInfo) {
- assert isValidVirtualTarget(appInfo.app().options);
- Set<DexEncodedMethod> result = Sets.newIdentityHashSet();
- if (hasSingleTarget()) {
- // Add default interface methods to the list of targets.
- //
- // This helps to make sure we take into account synthesized lambda classes
- // that we are not aware of. Like in the following example, we know that all
- // classes, XX in this case, override B::bar(), but there are also synthesized
- // classes for lambda which don't, so we still need default method to be live.
- //
- // public static void main(String[] args) {
- // X x = () -> {};
- // x.bar();
- // }
- //
- // interface X {
- // void foo();
- // default void bar() { }
- // }
- //
- // class XX implements X {
- // public void foo() { }
- // public void bar() { }
- // }
- //
- DexEncodedMethod singleTarget = getSingleTarget();
- if (singleTarget.hasCode()) {
- DexProgramClass holder =
- asProgramClassOrNull(appInfo.definitionFor(singleTarget.method.holder));
- if (appInfo.hasAnyInstantiatedLambdas(holder)) {
- result.add(singleTarget);
- }
- }
- }
+ public abstract Set<DexEncodedMethod> lookupInterfaceTargets(AppInfoWithSubtyping appInfo);
- DexEncodedMethod encodedMethod = getSingleTarget();
- DexMethod method = encodedMethod.method;
- Consumer<DexEncodedMethod> addIfNotAbstract =
- m -> {
- if (!m.accessFlags.isAbstract()) {
- result.add(m);
- }
- };
- // 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.
- Consumer<DexEncodedMethod> addIfNotAbstractAndBridge =
- m -> {
- if (!m.accessFlags.isAbstract() && m.accessFlags.isBridge()) {
- result.add(m);
- }
- };
-
- // TODO(b/140204899): Instead of subtypes of holder, we could iterate subtypes of refined
- // receiver type if available.
- for (DexType type : appInfo.subtypes(method.holder)) {
- DexClass clazz = appInfo.definitionFor(type);
- if (clazz.isInterface()) {
- ResolutionResult targetMethods = appInfo.resolveMethodOnInterface(clazz, method);
- if (targetMethods.hasSingleTarget()) {
- addIfNotAbstractAndBridge.accept(targetMethods.getSingleTarget());
- }
- } else {
- ResolutionResult targetMethods = appInfo.resolveMethodOnClass(clazz, method);
- if (targetMethods.hasSingleTarget()) {
- addIfNotAbstract.accept(targetMethods.getSingleTarget());
- }
- }
- }
- return result;
- }
-
+ /** Result for a resolution that succeeds with a known declaration/definition. */
public static class SingleResolutionResult extends ResolutionResult {
private final DexClass initialResolutionHolder;
private final DexClass resolvedHolder;
@@ -297,21 +199,128 @@
? resolution.resolvedMethod
: null;
}
- }
-
- public abstract static class EmptyResult extends ResolutionResult {
@Override
+ // TODO(b/140204899): Leverage refined receiver type if available.
public Set<DexEncodedMethod> lookupVirtualTargets(AppInfoWithSubtyping appInfo) {
+ assert isValidVirtualTarget(appInfo.app().options);
+ // First add the target for receiver type method.type.
+ DexEncodedMethod encodedMethod = getSingleTarget();
+ Set<DexEncodedMethod> result = SetUtils.newIdentityHashSet(encodedMethod);
+ // Add all matching targets from the subclass hierarchy.
+ DexMethod method = encodedMethod.method;
+ // TODO(b/140204899): Instead of subtypes of holder, we could iterate subtypes of refined
+ // receiver type if available.
+ for (DexType type : appInfo.subtypes(method.holder)) {
+ DexClass clazz = appInfo.definitionFor(type);
+ if (!clazz.isInterface()) {
+ ResolutionResult methods = appInfo.resolveMethodOnClass(clazz, method);
+ DexEncodedMethod target = methods.getSingleTarget();
+ if (target != null && target.isVirtualMethod()) {
+ result.add(target);
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ // TODO(b/140204899): Leverage refined receiver type if available.
+ public Set<DexEncodedMethod> lookupInterfaceTargets(AppInfoWithSubtyping appInfo) {
+ assert isValidVirtualTarget(appInfo.app().options);
+ Set<DexEncodedMethod> result = Sets.newIdentityHashSet();
+ if (isSingleResolution()) {
+ // Add default interface methods to the list of targets.
+ //
+ // This helps to make sure we take into account synthesized lambda classes
+ // that we are not aware of. Like in the following example, we know that all
+ // classes, XX in this case, override B::bar(), but there are also synthesized
+ // classes for lambda which don't, so we still need default method to be live.
+ //
+ // public static void main(String[] args) {
+ // X x = () -> {};
+ // x.bar();
+ // }
+ //
+ // interface X {
+ // void foo();
+ // default void bar() { }
+ // }
+ //
+ // class XX implements X {
+ // public void foo() { }
+ // public void bar() { }
+ // }
+ //
+ DexEncodedMethod singleTarget = getSingleTarget();
+ if (singleTarget.hasCode()) {
+ DexProgramClass holder =
+ asProgramClassOrNull(appInfo.definitionFor(singleTarget.method.holder));
+ if (appInfo.hasAnyInstantiatedLambdas(holder)) {
+ result.add(singleTarget);
+ }
+ }
+ }
+
+ DexEncodedMethod encodedMethod = getSingleTarget();
+ DexMethod method = encodedMethod.method;
+ Consumer<DexEncodedMethod> addIfNotAbstract =
+ m -> {
+ if (!m.accessFlags.isAbstract()) {
+ result.add(m);
+ }
+ };
+ // 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.
+ Consumer<DexEncodedMethod> addIfNotAbstractAndBridge =
+ m -> {
+ if (!m.accessFlags.isAbstract() && m.accessFlags.isBridge()) {
+ result.add(m);
+ }
+ };
+
+ // TODO(b/140204899): Instead of subtypes of holder, we could iterate subtypes of refined
+ // receiver type if available.
+ for (DexType type : appInfo.subtypes(method.holder)) {
+ DexClass clazz = appInfo.definitionFor(type);
+ if (clazz.isInterface()) {
+ ResolutionResult targetMethods = appInfo.resolveMethodOnInterface(clazz, method);
+ if (targetMethods.isSingleResolution()) {
+ addIfNotAbstractAndBridge.accept(targetMethods.getSingleTarget());
+ }
+ } else {
+ ResolutionResult targetMethods = appInfo.resolveMethodOnClass(clazz, method);
+ if (targetMethods.isSingleResolution()) {
+ addIfNotAbstract.accept(targetMethods.getSingleTarget());
+ }
+ }
+ }
+ return result;
+ }
+ }
+
+ abstract static class EmptyResult extends ResolutionResult {
+
+ @Override
+ public final DexEncodedMethod lookupInvokeSuperTarget(DexType context, AppInfo appInfo) {
return null;
}
@Override
- public Set<DexEncodedMethod> lookupInterfaceTargets(AppInfoWithSubtyping appInfo) {
+ public final Set<DexEncodedMethod> lookupVirtualTargets(AppInfoWithSubtyping appInfo) {
+ return null;
+ }
+
+ @Override
+ public final Set<DexEncodedMethod> lookupInterfaceTargets(AppInfoWithSubtyping appInfo) {
return null;
}
}
+ /** Singleton result for the special case resolving the array clone() method. */
public static class ArrayCloneMethodResult extends EmptyResult {
static final ArrayCloneMethodResult INSTANCE = new ArrayCloneMethodResult();
@@ -342,6 +351,7 @@
}
}
+ /** Base class for all types of failed resolutions. */
public abstract static class FailedResolutionResult extends EmptyResult {
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
index e5ace97..1049fb8 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
@@ -336,7 +336,7 @@
DexMethod method = instruction.getInvokedMethod();
ResolutionResult resolutionResult =
appView.appInfo().resolveMethodOnInterface(method.holder, method);
- if (!resolutionResult.hasSingleTarget()) {
+ if (!resolutionResult.isSingleResolution()) {
return false;
}
DexType holder = resolutionResult.getSingleTarget().method.holder;
@@ -394,7 +394,7 @@
}
ResolutionResult resolutionResult =
appView.appInfo().resolveMethod(superType, method, instruction.itf);
- if (!resolutionResult.hasSingleTarget()) {
+ if (!resolutionResult.isSingleResolution()) {
return false;
}
DexType holder = resolutionResult.getSingleTarget().method.holder;
@@ -430,7 +430,7 @@
DexMethod method = instruction.getInvokedMethod();
ResolutionResult resolutionResult =
appView.appInfo().resolveMethodOnClass(method.holder, method);
- if (!resolutionResult.hasSingleTarget()) {
+ if (!resolutionResult.isSingleResolution()) {
return false;
}
DexType holder = resolutionResult.getSingleTarget().method.holder;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index 484c4e3..e7d2ab1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -110,7 +110,7 @@
if (refinedReceiverType != staticReceiverType) {
ResolutionResult refinedResolution =
appView.appInfo().resolveMethod(refinedReceiverType, method);
- if (refinedResolution.hasSingleTarget()) {
+ if (refinedResolution.isSingleResolution()) {
DexEncodedMethod refinedTarget = refinedResolution.getSingleTarget();
Set<DexEncodedMethod> result = Sets.newIdentityHashSet();
for (DexEncodedMethod target : targets) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index d9d65e5..407742e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -175,7 +175,7 @@
DexItemFactory dexItemFactory = appView.dexItemFactory();
ResolutionResult finalizeResolutionResult =
appView.appInfo().resolveMethod(clazz, dexItemFactory.objectMethods.finalize);
- if (finalizeResolutionResult.hasSingleTarget()) {
+ if (finalizeResolutionResult.isSingleResolution()) {
DexMethod finalizeMethod = finalizeResolutionResult.getSingleTarget().method;
if (finalizeMethod != dexItemFactory.enumMethods.finalize
&& finalizeMethod != dexItemFactory.objectMethods.finalize) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
index 49b11a5..e83a17a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
@@ -93,7 +93,7 @@
}
// If the resolution ended up with a single target, check if it is a library override.
// And if so, bail out early (to avoid expensive target lookup).
- if (resolutionResult.hasSingleTarget()
+ if (resolutionResult.isSingleResolution()
&& isLibraryMethodOrLibraryMethodOverride(resolutionResult.getSingleTarget())) {
continue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 582de88..f420071 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -851,7 +851,7 @@
// We should not inline a method if the invocation has type interface or virtual and the
// signature of the invocation resolves to a private or static method.
ResolutionResult resolutionResult = appView.appInfo().resolveMethod(callee.holder, callee);
- if (resolutionResult.hasSingleTarget()
+ if (resolutionResult.isSingleResolution()
&& !resolutionResult.getSingleTarget().isVirtualMethod()) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
index 7d93488..8ea3d93 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -100,7 +100,7 @@
}
// If the method does not have a direct renaming, return the resolutions mapping.
ResolutionResult resolutionResult = appView.appInfo().resolveMethod(method.holder, method);
- if (resolutionResult.hasSingleTarget()) {
+ if (resolutionResult.isSingleResolution()) {
return renaming.getOrDefault(resolutionResult.getSingleTarget().method, method.name);
}
// If resolution fails, the method must be renamed consistently with the targets that give rise
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 e2641d1..347ae0f 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -887,7 +887,7 @@
// overrides the kept method.
if (isPinned(clazz.type)) {
ResolutionResult resolutionResult = resolveMethod(clazz, method);
- if (resolutionResult.hasSingleTarget()) {
+ if (resolutionResult.isSingleResolution()) {
DexEncodedMethod resolutionTarget = resolutionResult.getSingleTarget();
return !resolutionTarget.isProgramMethod(this)
|| resolutionTarget.isLibraryMethodOverride().isPossiblyTrue()
@@ -1041,10 +1041,10 @@
// from the runtime type of the receiver.
if (receiverLowerBoundType != null) {
if (receiverLowerBoundType.getClassType() == refinedReceiverType) {
- if (resolutionResult.hasSingleTarget()
+ if (resolutionResult.isSingleResolution()
&& resolutionResult.isValidVirtualTargetForDynamicDispatch()) {
ResolutionResult refinedResolutionResult = resolveMethod(refinedReceiverType, method);
- if (refinedResolutionResult.hasSingleTarget()
+ if (refinedResolutionResult.isSingleResolution()
&& refinedResolutionResult.isValidVirtualTargetForDynamicDispatch()) {
return validateSingleVirtualTarget(
refinedResolutionResult.getSingleTarget(), resolutionResult.getSingleTarget());
@@ -1083,7 +1083,7 @@
// First get the target for the holder type.
ResolutionResult topMethod = resolveMethodOnClass(holder, method);
// We might hit none or multiple targets. Both make this fail at runtime.
- if (!topMethod.hasSingleTarget() || !topMethod.isValidVirtualTarget(options())) {
+ if (!topMethod.isSingleResolution() || !topMethod.isValidVirtualTarget(options())) {
method.setSingleVirtualMethodCache(refinedReceiverType, null);
return null;
}
@@ -1225,10 +1225,10 @@
if (receiverLowerBoundType != null) {
if (receiverLowerBoundType.getClassType() == refinedReceiverType) {
ResolutionResult resolutionResult = resolveMethod(method.holder, method, true);
- if (resolutionResult.hasSingleTarget()
+ if (resolutionResult.isSingleResolution()
&& resolutionResult.isValidVirtualTargetForDynamicDispatch()) {
ResolutionResult refinedResolutionResult = resolveMethod(refinedReceiverType, method);
- if (refinedResolutionResult.hasSingleTarget()
+ if (refinedResolutionResult.isSingleResolution()
&& refinedResolutionResult.isValidVirtualTargetForDynamicDispatch()) {
return validateSingleVirtualTarget(
refinedResolutionResult.getSingleTarget(), resolutionResult.getSingleTarget());
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 fee13db..24b5004 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -533,7 +533,7 @@
ResolutionResult resolutionResult =
appView.appInfo().resolveMethod(originalClazz, method.method);
if (!resolutionResult.isValidVirtualTarget(appView.options())
- || !resolutionResult.hasSingleTarget()) {
+ || !resolutionResult.isSingleResolution()) {
return;
}
DexEncodedMethod methodToKeep = resolutionResult.getSingleTarget();
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 9f4b3e3..77de8e2 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1244,7 +1244,7 @@
// Returns the method that shadows the given method, or null if method is not shadowed.
private DexEncodedMethod findMethodInTarget(DexEncodedMethod method) {
ResolutionResult resolutionResult = appInfo.resolveMethod(target, method.method);
- if (!resolutionResult.hasSingleTarget()) {
+ if (!resolutionResult.isSingleResolution()) {
// May happen in case of missing classes, or if multiple implementations were found.
abortMerge = true;
return null;