Desugared lib: use resolution
Bug:145787526
Change-Id: Ie43bcaeeb6dbeea54680fb8fa7747cbfed92d416
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 cf3b100..393c334 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -363,7 +363,7 @@
return result;
}
// Finally Step 3:
- return resolveMethodStep3(clazz, method, clazz);
+ return resolveMethodStep3(clazz, method);
}
/**
@@ -396,15 +396,29 @@
}
/**
+ * 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);
+ }
+
+ /**
* Implements step 3 of <a
* href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.3">Section
* 5.4.3.3 of the JVM Spec</a>. As this is the same for interfaces and classes, we share one
* implementation.
*/
- public ResolutionResult resolveMethodStep3(
- DexClass clazz, DexMethod method, DexClass initialResolutionHolder) {
- MaximallySpecificMethodsBuilder builder =
- new MaximallySpecificMethodsBuilder(initialResolutionHolder);
+ private ResolutionResult resolveMethodStep3(DexClass clazz, DexMethod method) {
+ MaximallySpecificMethodsBuilder builder = new MaximallySpecificMethodsBuilder(clazz);
resolveMethodStep3Helper(clazz, method, builder);
return builder.resolve();
}
@@ -494,7 +508,7 @@
}
// Step 3: Look for maximally-specific superinterface methods or any interface definition.
// This is the same for classes and interfaces.
- return resolveMethodStep3(definition, desc, definition);
+ return resolveMethodStep3(definition, desc);
}
/**
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 e559247..3168dde 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
@@ -324,7 +324,7 @@
target =
appView
.appInfo()
- .resolveMethodStep3(libraryHolder, method, libraryHolder)
+ .resolveMaximallySpecificMethods(libraryHolder, method)
.getSingleTarget();
if (target != null && rewriter.isEmulatedInterface(target.method.holder)) {
targetHolder = appView.definitionFor(target.method.holder);
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 5d01e19..dbf128f 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
@@ -27,6 +27,7 @@
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
+import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -104,8 +105,6 @@
private final Map<DexType, DexType> emulatedInterfaces = new IdentityHashMap<>();
// The emulatedMethod set is there to avoid doing the emulated look-up too often.
private final Set<DexString> emulatedMethods = Sets.newIdentityHashSet();
- private ConcurrentHashMap<DexMethod, DexType> nearestEmulatedInterfaceCache =
- new ConcurrentHashMap<>();
// All forwarding methods generated during desugaring. We don't synchronize access
// to this collection since it is only filled in ClassProcessor running synchronously.
@@ -273,7 +272,7 @@
new InvokeStatic(defaultAsMethodOfCompanionClass(amendedMethod),
invokeSuper.outValue(), invokeSuper.arguments()));
} else {
- DexType dexType = nearestEmulatedInterfaceOrNull(invokedMethod);
+ DexType dexType = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
if (dexType != null) {
// That invoke super may not resolve since the super method may not be present
// since it's in the emulated interface. We need to force resolution. If it resolves
@@ -374,7 +373,7 @@
if (instruction.isInvokeVirtual() || instruction.isInvokeInterface()) {
InvokeMethod invokeMethod = instruction.asInvokeMethod();
DexMethod invokedMethod = invokeMethod.getInvokedMethod();
- DexType dexType = nearestEmulatedInterfaceOrNull(invokedMethod);
+ DexType dexType = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
if (dexType != null) {
rewriteCurrentInstructionToEmulatedInterfaceCall(
dexType, invokedMethod, invokeMethod, instructions);
@@ -384,7 +383,7 @@
}
}
- private DexType nearestEmulatedInterfaceOrNull(DexMethod invokedMethod) {
+ private DexType maximallySpecificEmulatedInterfaceOrNull(DexMethod invokedMethod) {
// Here we try to avoid doing the expensive look-up on all invokes.
if (!emulatedMethods.contains(invokedMethod.name)) {
return null;
@@ -394,20 +393,30 @@
if (dexClass == null) {
return null;
}
- // TODO(b/120884788): Make sure program class are looked up before library class for
- // CoreLib compilation or look again into all desugared library emulation.
- // Outside of core libraries, only library classes are rewritten. In core libraries,
- // some classes are present both as program and library class, and definitionFor
- // answers the program class so this is not true.
+ // TODO(b/120884788): Make sure program class are looked up before library class.
+ // Since program classes are desugared, no need to rewrite invokes which can target only
+ // program types.
if (!appView.options().isDesugaredLibraryCompilation() && !dexClass.isLibraryClass()) {
return null;
}
- // We always rewrite interfaces, but classes are rewritten only if they are not already
- // desugared (CoreLibrary classes efficient implementation).
- if (!dexClass.isInterface() && isInDesugaredLibrary(dexClass)) {
+ // Since desugared library classes are desugared, no need to rewrite invokes which can target
+ // only such classes program types.
+ if (appView.rewritePrefix.hasRewrittenType(dexClass.type)) {
return null;
}
- return nearestEmulatedInterfaceImplementingWithCache(invokedMethod);
+ ResolutionResult resolutionResult =
+ appView.appInfo().resolveMaximallySpecificMethods(dexClass, invokedMethod);
+ if (!resolutionResult.hasSingleTarget()) {
+ // 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
+ // interfaces.
+ return null;
+ }
+ DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
+ if (!singleTarget.isAbstract() && isEmulatedInterface(singleTarget.method.holder)) {
+ return singleTarget.method.holder;
+ }
+ return null;
}
private void rewriteCurrentInstructionToEmulatedInterfaceCall(
@@ -426,133 +435,7 @@
}
}
- private DexType nearestEmulatedInterfaceImplementingWithCache(DexMethod method) {
- DexType sentinel = DexItemFactory.nullValueType;
- if (nearestEmulatedInterfaceCache.containsKey(method)) {
- DexType dexType = nearestEmulatedInterfaceCache.get(method);
- if (dexType == sentinel) {
- return null;
- }
- return dexType;
- } else {
- DexType value = nearestEmulatedInterfaceImplementing(method);
- DexType putValue = value == null ? sentinel : value;
- nearestEmulatedInterfaceCache.put(method, putValue);
- return value;
- }
- }
-
- private DexType nearestEmulatedInterfaceImplementing(DexMethod method) {
- // Find the nearest emulated interface implementing method in a non abstract way.
- // Answers null if none.
- if (!method.holder.isClassType()) {
- return null;
- }
- // 1. Direct match against the interface for invokeInterface.
- if (isMatchingEmulatedInterface(method.holder, method)) {
- return method.holder;
- }
- List<DexType> foundInterfaces = new ArrayList<>();
- Set<DexType> foundEmulatedInterfaces = Sets.newIdentityHashSet();
- // 2. Walk superclass hierarchy to find implemented interfaces, pick the minimal of them.
- DexType current = method.holder;
- DexClass currentClass = appView.definitionFor(current);
- while (currentClass != null) {
- for (DexType itf : currentClass.interfaces.values) {
- if (isMatchingEmulatedInterface(itf, method)) {
- foundEmulatedInterfaces.add(itf);
- } else if (foundEmulatedInterfaces.isEmpty()) {
- foundInterfaces.add(itf);
- }
- }
- current = currentClass.superType;
- currentClass = current == null ? null : appView.definitionFor(current);
- }
- if (!foundEmulatedInterfaces.isEmpty()) {
- return minimalInterfaceOf(foundEmulatedInterfaces);
- }
- // 3. Walk the interfaces hierachies to find implemented interfaces, pick the minimal of them.
- LinkedList<DexType> workList = new LinkedList<>(foundInterfaces);
- while (!workList.isEmpty()) {
- currentClass = appView.definitionFor(workList.removeFirst());
- if (currentClass != null) {
- for (DexType itf : currentClass.interfaces.values) {
- if (isMatchingEmulatedInterface(itf, method)) {
- foundEmulatedInterfaces.add(itf);
- } else if (!foundInterfaces.contains(itf)) {
- foundInterfaces.add(itf);
- workList.add(itf);
- }
- }
- }
- }
- if (!foundEmulatedInterfaces.isEmpty()) {
- return minimalInterfaceOf(foundEmulatedInterfaces);
- }
- return null;
- }
-
- private boolean isMatchingEmulatedInterface(DexType itf, DexMethod method) {
- DexClass dexClass = appView.definitionFor(itf);
- DexEncodedMethod encodedMethod = dexClass == null ? null : dexClass.lookupMethod(method);
- return emulatedInterfaces.containsKey(itf)
- && encodedMethod != null
- && !encodedMethod.isAbstract();
- }
-
- private DexType minimalInterfaceOf(Set<DexType> interfaces) {
- assert interfaces.size() > 0;
- if (interfaces.size() == 1) {
- return interfaces.iterator().next();
- }
- // We may have two classes which appears unrelated due to a missing interface in the list,
- // i.e., A implements B implements C, but B is not implementing the method.
- // We look up interface hierarchy here for all interfaces to determine interfaces with children.
- // The unique interface without children is returned (nearest interface).
- final ArrayList<DexType> hasChildren = new ArrayList<>();
- for (DexType anInterface : interfaces) {
- LinkedList<DexType> workList = new LinkedList<>();
- workList.add(anInterface);
- while (!workList.isEmpty()) {
- DexType itf = workList.removeFirst();
- DexClass itfClass = appView.definitionFor(itf);
- if (itfClass == null) {
- continue;
- }
- for (DexType superItf : itfClass.interfaces.values) {
- if (interfaces.contains(superItf)) {
- hasChildren.add(superItf);
- } else {
- workList.add(superItf);
- }
- }
- }
- }
- DexType result = null;
- for (DexType anInterface : interfaces) {
- if (!hasChildren.contains(anInterface)) {
- if (result != null) {
- throw new CompilationError(
- "Multiple emulated interfaces, non related to each other, "
- + "implementing the same default method ("
- + anInterface
- + ","
- + result
- + ")");
- }
- result = anInterface;
- }
- }
- if (result == null) {
- throw new CompilationError(
- "All emulated interfaces "
- + Arrays.toString(interfaces.toArray())
- + " inherit from each other.");
- }
- return result;
- }
-
- boolean isNonDesugaredLibraryClass(DexClass clazz) {
+ private boolean isNonDesugaredLibraryClass(DexClass clazz) {
return clazz.isLibraryClass() && !isInDesugaredLibrary(clazz);
}