Refactor helpers for obtaining the maximally specific interface method.
Change-Id: Ifa226c7685ca777acd03df0bb5f9578c9fc634a8
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 141f742..eb972ca 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -17,6 +17,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -402,16 +403,32 @@
* 5.4.3.3 of the JVM Spec</a>. As this is the same for interfaces and classes, we share one
* implementation.
*/
- ResolutionResult resolveMethodStep3(DexClass clazz, DexMethod method) {
- MaximallySpecificMethodsBuilder builder = new MaximallySpecificMethodsBuilder(clazz);
- resolveMethodStep3Helper(clazz, method, builder);
- return builder.resolve();
+ private ResolutionResult resolveMethodStep3(DexClass clazz, DexMethod method) {
+ MaximallySpecificMethodsBuilder builder = new MaximallySpecificMethodsBuilder();
+ resolveMethodStep3Helper(method, clazz, builder);
+ 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, clazz, builder);
+ return builder.lookup();
}
/** Helper method that builds the set of maximally specific methods. */
private void resolveMethodStep3Helper(
- DexClass clazz, DexMethod method, MaximallySpecificMethodsBuilder builder) {
- for (DexType iface : clazz.interfaces.values) {
+ DexMethod method, DexClass clazz, MaximallySpecificMethodsBuilder builder) {
+ resolveMethodStep3Helper(
+ method, clazz.superType, Arrays.asList(clazz.interfaces.values), builder);
+ }
+
+ private void resolveMethodStep3Helper(
+ DexMethod method,
+ DexType superType,
+ List<DexType> interfaces,
+ MaximallySpecificMethodsBuilder builder) {
+ for (DexType iface : interfaces) {
DexClass definiton = definitionFor(iface);
if (definiton == null) {
// Ignore missing interface definitions.
@@ -424,14 +441,14 @@
builder.addCandidate(definiton, result, this);
} else {
// Look at the super-interfaces of this class and keep searching.
- resolveMethodStep3Helper(definiton, method, builder);
+ resolveMethodStep3Helper(method, definiton, builder);
}
}
// Now look at indirect super interfaces.
- if (clazz.superType != null) {
- DexClass superClass = definitionFor(clazz.superType);
+ if (superType != null) {
+ DexClass superClass = definitionFor(superType);
if (superClass != null) {
- resolveMethodStep3Helper(superClass, method, builder);
+ resolveMethodStep3Helper(method, superClass, builder);
}
}
}
@@ -579,8 +596,6 @@
private static class MaximallySpecificMethodsBuilder {
- private final DexClass initialResolutionHolder;
-
// The set of actual maximally specific methods.
// This set is linked map so that in the case where a number of methods remain a deterministic
// choice can be made. The map is from definition classes to their maximally specific method, or
@@ -589,10 +604,6 @@
// prior to writing.
LinkedHashMap<DexClass, DexEncodedMethod> maximallySpecificMethods = new LinkedHashMap<>();
- public MaximallySpecificMethodsBuilder(DexClass initialResolutionHolder) {
- this.initialResolutionHolder = initialResolutionHolder;
- }
-
void addCandidate(DexClass holder, DexEncodedMethod method, AppInfo appInfo) {
// If this candidate is already a candidate or it is shadowed, then no need to continue.
if (maximallySpecificMethods.containsKey(holder)) {
@@ -629,16 +640,26 @@
}
}
- ResolutionResult resolve() {
+ DexClassAndMethod lookup() {
+ SingleResolutionResult result = internalResolve(null).asSingleResolution();
+ return result != null
+ ? DexClassAndMethod.create(result.getResolvedHolder(), result.getResolvedMethod())
+ : null;
+ }
+
+ ResolutionResult resolve(DexClass initialResolutionHolder) {
+ assert initialResolutionHolder != null;
+ return internalResolve(initialResolutionHolder);
+ }
+
+ private ResolutionResult internalResolve(DexClass initialResolutionHolder) {
if (maximallySpecificMethods.isEmpty()) {
return NoSuchMethodResult.INSTANCE;
}
// Fast path in the common case of a single method.
if (maximallySpecificMethods.size() == 1) {
- Entry<DexClass, DexEncodedMethod> first =
- maximallySpecificMethods.entrySet().iterator().next();
- return new SingleResolutionResult(
- initialResolutionHolder, first.getKey(), first.getValue());
+ return singleResultHelper(
+ initialResolutionHolder, maximallySpecificMethods.entrySet().iterator().next());
}
Entry<DexClass, DexEncodedMethod> firstMaximallySpecificMethod = null;
List<Entry<DexClass, DexEncodedMethod>> nonAbstractMethods =
@@ -659,21 +680,24 @@
// If there are no non-abstract methods, then any candidate will suffice as a target.
// For deterministic resolution, we return the first mapped method (of the linked map).
if (nonAbstractMethods.isEmpty()) {
- return new SingleResolutionResult(
- initialResolutionHolder,
- firstMaximallySpecificMethod.getKey(),
- firstMaximallySpecificMethod.getValue());
+ return singleResultHelper(initialResolutionHolder, firstMaximallySpecificMethod);
}
// If there is exactly one non-abstract method (a default method) it is the resolution target.
if (nonAbstractMethods.size() == 1) {
- Entry<DexClass, DexEncodedMethod> entry = nonAbstractMethods.get(0);
- return new SingleResolutionResult(
- initialResolutionHolder, entry.getKey(), entry.getValue());
+ return singleResultHelper(initialResolutionHolder, nonAbstractMethods.get(0));
}
return IncompatibleClassResult.create(ListUtils.map(nonAbstractMethods, Entry::getValue));
}
}
+ private static SingleResolutionResult singleResultHelper(
+ DexClass initialResolutionResult, Entry<DexClass, DexEncodedMethod> entry) {
+ return new SingleResolutionResult(
+ initialResolutionResult != null ? initialResolutionResult : entry.getKey(),
+ entry.getKey(),
+ entry.getValue());
+ }
+
// TODO(b/149190785): Remove once fixed.
public void enableDefinitionForAssert() {}
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 cefb55a..e0e8269 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -7,7 +7,6 @@
import static com.android.tools.r8.utils.TraversalContinuation.BREAK;
import static com.android.tools.r8.utils.TraversalContinuation.CONTINUE;
-import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.utils.TraversalContinuation;
import com.google.common.collect.Sets;
import java.util.ArrayDeque;
@@ -207,16 +206,8 @@
* Helper method used for emulated interface resolution (not in JVM specifications). The result
* may be abstract.
*/
- public ResolutionResult resolveMaximallySpecificMethods(DexClass clazz, DexMethod method) {
- assert !clazz.type.isArrayType();
- if (clazz.isInterface()) {
- // Look for exact method on interface.
- DexEncodedMethod result = clazz.lookupMethod(method);
- if (result != null) {
- return new SingleResolutionResult(clazz, clazz, result);
- }
- }
- return resolveMethodStep3(clazz, method);
+ public DexClassAndMethod lookupMaximallySpecificMethod(DexClass clazz, DexMethod method) {
+ return lookupMaximallySpecificTarget(clazz, method);
}
/**
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
index 281b58a..ded00bd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -11,7 +11,7 @@
private final DexClass holder;
private final DexEncodedMethod method;
- protected DexClassAndMethod(DexClass holder, DexEncodedMethod method) {
+ DexClassAndMethod(DexClass holder, DexEncodedMethod method) {
assert holder.type == method.method.holder;
this.holder = holder;
this.method = method;
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 1002cd5..4c5e239 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -286,7 +286,10 @@
}
// 4. Otherwise, it is the single maximally specific method:
if (target == null) {
- target = appInfo.resolveMaximallySpecificMethods(initialType, method).getSingleTarget();
+ DexClassAndMethod result = appInfo.lookupMaximallySpecificMethod(initialType, method);
+ if (result != null) {
+ target = result.getMethod();
+ }
}
if (target == null) {
return null;
@@ -439,22 +442,14 @@
if (!resolvedHolder.isInterface()) {
return null;
}
- DexEncodedMethod maximalSpecific =
- lookupMaximallySpecificDispatchTarget(dynamicInstance, appView);
- return maximalSpecific == null
- ? null
- : DexClassAndMethod.create(
- appView.definitionFor(maximalSpecific.method.holder), maximalSpecific);
+ return lookupMaximallySpecificDispatchTarget(dynamicInstance, appView);
}
- private DexEncodedMethod lookupMaximallySpecificDispatchTarget(
+ private DexClassAndMethod lookupMaximallySpecificDispatchTarget(
DexProgramClass dynamicInstance, AppView<? extends AppInfoWithClassHierarchy> appView) {
- ResolutionResult maximallySpecificResult =
- appView.appInfo().resolveMaximallySpecificMethods(dynamicInstance, resolvedMethod.method);
- if (maximallySpecificResult.isSingleResolution()) {
- return maximallySpecificResult.asSingleResolution().resolvedMethod;
- }
- return null;
+ return appView
+ .appInfo()
+ .lookupMaximallySpecificMethod(dynamicInstance, resolvedMethod.method);
}
/**
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 2f54714..76e97df 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexLibraryClass;
@@ -319,14 +320,10 @@
// If target is a non-interface library class it may be an emulated interface.
if (!libraryHolder.isInterface()) {
// Here we use step-3 of resolution to find a maximally specific default interface method.
- target =
- appView
- .appInfo()
- .resolveMaximallySpecificMethods(libraryHolder, method)
- .getSingleTarget();
- if (target != null && rewriter.isEmulatedInterface(target.method.holder)) {
- targetHolder = appView.definitionFor(target.method.holder);
- addForward.accept(targetHolder, target);
+ DexClassAndMethod result =
+ appView.appInfo().lookupMaximallySpecificMethod(libraryHolder, method);
+ if (result != null && rewriter.isEmulatedInterface(result.getHolder().type)) {
+ addForward.accept(result.getHolder(), result.getMethod());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 0e8cbae..f6693eb 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -399,11 +400,18 @@
if (appView.rewritePrefix.hasRewrittenType(dexClass.type, appView)) {
return null;
}
- DexEncodedMethod singleTarget =
- appView
- .appInfo()
- .resolveMaximallySpecificMethods(dexClass, invokedMethod)
- .getSingleTarget();
+ DexEncodedMethod singleTarget = null;
+ if (dexClass.isInterface()) {
+ // Look for exact method on the interface.
+ singleTarget = dexClass.lookupMethod(invokedMethod);
+ }
+ if (singleTarget == null) {
+ DexClassAndMethod result =
+ appView.appInfo().lookupMaximallySpecificMethod(dexClass, invokedMethod);
+ if (result != null) {
+ singleTarget = result.getMethod();
+ }
+ }
if (singleTarget == null) {
// At this point we are in a library class. Failures can happen with NoSuchMethod if a
// library class implement a method with same signature but not related to emulated
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 68f1749..6bce9c1 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexDebugEvent;
import com.android.tools.r8.graph.DexDebugEvent.AdvancePC;
@@ -35,7 +36,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.GraphLense;
-import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.kotlin.KotlinSourceDebugExtensionParser;
import com.android.tools.r8.kotlin.KotlinSourceDebugExtensionParser.Result;
@@ -309,7 +309,7 @@
// they may be bridges for interface methods with covariant return types.
sortMethods(methods);
// TODO(b/149360203): Reenable assert.
- // assert verifyMethodsAreKeptDirectlyOrIndirectly(appView, methods);
+ assert true || verifyMethodsAreKeptDirectlyOrIndirectly(appView, methods);
}
boolean identityMapping =
@@ -457,15 +457,14 @@
}
// We use the same name for interface names even if it has different types.
DexProgramClass clazz = appView.definitionForProgramType(method.method.holder);
- ResolutionResult resolutionResult =
- appView.appInfo().resolveMaximallySpecificMethods(clazz, method.method);
- if (resolutionResult.isFailedResolution()) {
+ DexClassAndMethod lookupResult =
+ appView.appInfo().lookupMaximallySpecificMethod(clazz, method.method);
+ if (lookupResult == null) {
// We cannot rename methods we cannot look up.
continue;
}
String errorString = method.method.qualifiedName() + " is not kept but is overloaded";
- assert resolutionResult.isSingleResolution() : errorString;
- assert resolutionResult.asSingleResolution().getResolvedHolder().isInterface() : errorString;
+ assert lookupResult.getHolder().isInterface() : errorString;
assert originalName == null || originalName.equals(method.method.name) : errorString;
originalName = method.method.name;
}
diff --git a/src/test/java/com/android/tools/r8/desugar/LambdaWithDefaultMethodsTest.java b/src/test/java/com/android/tools/r8/desugar/LambdaWithDefaultMethodsTest.java
index 3b4f5f1..ce7a552 100644
--- a/src/test/java/com/android/tools/r8/desugar/LambdaWithDefaultMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/LambdaWithDefaultMethodsTest.java
@@ -79,7 +79,8 @@
public static void main(String[] args) {
// Target the default method, causing it to be marked reachable.
// This is done directly in main to ensure that it is the first thing hit in tracing.
- new A().bar();
+ I i = new A();
+ i.bar();
// Create a call-site instance that will need to identify the default method as live.
// The creation is outlined to ensure that it is not hit before the method is reachable.
runDefault(createLambda());