Add desugar dependency edges for synthesized classes.
Bug: 138988172
Change-Id: Iaf207c397086db735291896b822dd3e13edb0e62
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 7f4c043..328d8e2 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
@@ -26,7 +26,6 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue;
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;
@@ -397,15 +396,17 @@
if (appView.rewritePrefix.hasRewrittenType(dexClass.type)) {
return null;
}
- ResolutionResult resolutionResult =
- appView.appInfo().resolveMaximallySpecificMethods(dexClass, invokedMethod);
- if (!resolutionResult.isSingleResolution()) {
+ DexEncodedMethod singleTarget =
+ appView
+ .appInfo()
+ .resolveMaximallySpecificMethods(dexClass, invokedMethod)
+ .getSingleTarget();
+ 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
// interfaces.
return null;
}
- DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
if (!singleTarget.isAbstract() && isEmulatedInterface(singleTarget.method.holder)) {
return singleTarget.method.holder;
}
@@ -1143,10 +1144,16 @@
assert !dependency.isLibraryClass();
DesugarGraphConsumer consumer = options.desugarGraphConsumer;
if (consumer != null) {
- Origin dependentOrigin = dependent.getOrigin();
Origin dependencyOrigin = dependency.getOrigin();
- if (dependentOrigin != dependencyOrigin) {
- consumer.accept(dependentOrigin, dependencyOrigin);
+ java.util.Collection<DexProgramClass> dependents = dependent.getSynthesizedFrom();
+ if (dependents == null || dependents.isEmpty()) {
+ dependents = Collections.singletonList(dependent);
+ }
+ for (DexProgramClass clazz : dependents) {
+ Origin dependentOrigin = clazz.getOrigin();
+ if (dependentOrigin != dependencyOrigin) {
+ consumer.accept(dependentOrigin, dependencyOrigin);
+ }
}
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/InterfaceToImplementingClassDependencyTest.java b/src/test/java/com/android/tools/r8/desugar/graph/InterfaceToImplementingClassDependencyTest.java
index 7f76cad..705410a 100644
--- a/src/test/java/com/android/tools/r8/desugar/graph/InterfaceToImplementingClassDependencyTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/graph/InterfaceToImplementingClassDependencyTest.java
@@ -36,7 +36,7 @@
// Emtpy.
}
- public class A implements I {
+ public static class A implements I {
// Empty.
}
@@ -80,8 +80,8 @@
.assertSuccessWithOutputLines("Hello World!");
// If API level indicates desugaring is needed check the edges are reported.
if (parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel()) {
- assertEquals(1, consumer.totalEdgeCount());
assertTrue(consumer.contains(originI, originA));
+ assertEquals(1, consumer.totalEdgeCount());
} else {
assertEquals(0, consumer.totalEdgeCount());
}
diff --git a/src/test/java/com/android/tools/r8/desugar/graph/LambdaDependencyTest.java b/src/test/java/com/android/tools/r8/desugar/graph/LambdaDependencyTest.java
new file mode 100644
index 0000000..d7f39e5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/graph/LambdaDependencyTest.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.desugar.graph;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.D8TestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class LambdaDependencyTest extends TestBase {
+
+ public interface I {
+ void foo();
+ }
+
+ public static class A {
+ void bar(I i) {
+ i.foo();
+ }
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ new A().bar(() -> System.out.println("lambda!"));
+ }
+ }
+
+ // Test runner follows.
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ private final TestParameters parameters;
+
+ public LambdaDependencyTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addProgramClasses(I.class, A.class, TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("lambda!");
+ } else {
+ D8TestBuilder builder = testForD8();
+ DesugarGraphTestConsumer consumer = new DesugarGraphTestConsumer();
+ builder.getBuilder().setDesugarGraphConsumer(consumer);
+ Origin originI = DesugarGraphUtils.addClassWithOrigin(I.class, builder);
+ Origin originA = DesugarGraphUtils.addClassWithOrigin(A.class, builder);
+ Origin originMain = DesugarGraphUtils.addClassWithOrigin(TestClass.class, builder);
+ builder
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("lambda!");
+ // If API level indicates desugaring is needed check the edges are reported.
+ if (parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel()) {
+ // Generated lambda class in TestClass.main depends on potential default methods in I.
+ assertTrue(consumer.contains(originI, originMain));
+ assertEquals(1, consumer.totalEdgeCount());
+ } else {
+ assertEquals(0, consumer.totalEdgeCount());
+ }
+ }
+ }
+}