Fix inadequate tracing of invoke-dynamic in printuses and tracereferences
Change-Id: I626cbfdf380df6e63f7ef66cc15591570b15020e
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index cba8b55..b2f5a2f 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -3,8 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
-import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -296,11 +294,11 @@
List<DexType> directInterfaces = LambdaDescriptor.getInterfaces(callSite, appInfo);
if (directInterfaces != null) {
for (DexType directInterface : directInterfaces) {
- DexProgramClass clazz = asProgramClassOrNull(appInfo.definitionFor(directInterface));
+ DexClass clazz = appInfo.definitionFor(directInterface);
if (clazz != null) {
- clazz.forEachProgramVirtualMethodMatching(
- definition -> definition.getReference().name.equals(callSite.methodName),
- this::registerMethod);
+ clazz.forEachClassMethodMatching(
+ definition -> definition.getName().equals(callSite.methodName),
+ method -> addMethod(method.getReference()));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index edfa934..7374ad4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -43,7 +43,7 @@
public final DexMethodHandle implHandle;
public final List<DexType> interfaces = new ArrayList<>();
- final Set<DexProto> bridges = Sets.newIdentityHashSet();
+ public final Set<DexProto> bridges = Sets.newIdentityHashSet();
public final DexTypeList captures;
// Used for accessibility analysis and few assertions only.
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
index 5827d53..ee3c33d 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -12,13 +12,16 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue;
@@ -31,6 +34,7 @@
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
@@ -448,6 +452,36 @@
public void registerTypeReference(DexType type) {
addType(graphLens.lookupType(type), referencedFrom);
}
+
+ // Call sites.
+
+ @Override
+ public void registerCallSite(DexCallSite callSite) {
+ super.registerCallSite(callSite);
+
+ // For lambdas that implement an interface, also keep the interface method by simulating an
+ // invoke to it from the current context.
+ LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callSite, appInfo, context);
+ if (descriptor != null) {
+ for (DexType interfaceType : descriptor.interfaces) {
+ DexClass interfaceDefinition = appInfo.definitionFor(interfaceType);
+ if (interfaceDefinition != null) {
+ DexEncodedMethod mainMethod =
+ interfaceDefinition.lookupMethod(descriptor.getMainMethod());
+ if (mainMethod != null) {
+ registerInvokeInterface(mainMethod.getReference());
+ }
+ for (DexProto bridgeProto : descriptor.bridges) {
+ DexEncodedMethod bridgeMethod =
+ interfaceDefinition.lookupMethod(bridgeProto, descriptor.name);
+ if (bridgeMethod != null) {
+ registerInvokeInterface(bridgeMethod.getReference());
+ }
+ }
+ }
+ }
+ }
+ }
}
}
}
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceLambdaImplementedInterfaceIntersectionTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceLambdaImplementedInterfaceIntersectionTest.java
new file mode 100644
index 0000000..e842fea
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceLambdaImplementedInterfaceIntersectionTest.java
@@ -0,0 +1,111 @@
+// Copyright (c) 2021, 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.tracereferences;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+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 TraceReferenceLambdaImplementedInterfaceIntersectionTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public TraceReferenceLambdaImplementedInterfaceIntersectionTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
+ static class SeenReferencesConsumer implements TraceReferencesConsumer {
+
+ private Set<MethodReference> seenMethods = new HashSet<>();
+
+ @Override
+ public void acceptType(TracedClass tracedClass, DiagnosticsHandler handler) {}
+
+ @Override
+ public void acceptField(TracedField tracedField, DiagnosticsHandler handler) {}
+
+ @Override
+ public void acceptMethod(TracedMethod tracedMethod, DiagnosticsHandler handler) {
+ seenMethods.add(tracedMethod.getReference());
+ }
+ }
+
+ @Test
+ public void test() throws Throwable {
+ Path dir = temp.newFolder().toPath();
+ Path targetJar =
+ ZipBuilder.builder(dir.resolve("target.jar"))
+ .addFilesRelative(
+ ToolHelper.getClassPathForTests(),
+ ToolHelper.getClassFileForTestClass(I.class),
+ ToolHelper.getClassFileForTestClass(J.class))
+ .build();
+ Path sourceJar =
+ ZipBuilder.builder(dir.resolve("source.jar"))
+ .addFilesRelative(
+ ToolHelper.getClassPathForTests(), ToolHelper.getClassFileForTestClass(Main.class))
+ .build();
+ SeenReferencesConsumer consumer = new SeenReferencesConsumer();
+
+ TraceReferences.run(
+ TraceReferencesCommand.builder()
+ .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+ .addSourceFiles(sourceJar)
+ .addTargetFiles(targetJar)
+ .setConsumer(consumer)
+ .build());
+
+ ImmutableSet<MethodReference> expectedSet =
+ ImmutableSet.of(
+ Reference.method(
+ Reference.classFromClass(I.class),
+ "m",
+ Collections.emptyList(),
+ Reference.classFromClass(Object.class)),
+ Reference.method(
+ Reference.classFromClass(J.class),
+ "m",
+ Collections.emptyList(),
+ Reference.classFromClass(String.class)));
+ assertEquals(expectedSet, consumer.seenMethods);
+ }
+
+ interface I {
+
+ Object m();
+ }
+
+ interface J {
+
+ String m();
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ I i = (I & J) () -> "Hello";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceLambdaImplementedInterfaceTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceLambdaImplementedInterfaceTest.java
new file mode 100644
index 0000000..8781a3d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceLambdaImplementedInterfaceTest.java
@@ -0,0 +1,96 @@
+// Copyright (c) 2021, 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.tracereferences;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+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 TraceReferenceLambdaImplementedInterfaceTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public TraceReferenceLambdaImplementedInterfaceTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
+ static class SeenReferencesConsumer implements TraceReferencesConsumer {
+
+ private Set<MethodReference> seenMethods = new HashSet<>();
+
+ @Override
+ public void acceptType(TracedClass tracedClass, DiagnosticsHandler handler) {}
+
+ @Override
+ public void acceptField(TracedField tracedField, DiagnosticsHandler handler) {}
+
+ @Override
+ public void acceptMethod(TracedMethod tracedMethod, DiagnosticsHandler handler) {
+ seenMethods.add(tracedMethod.getReference());
+ }
+ }
+
+ @Test
+ public void test() throws Throwable {
+ Path dir = temp.newFolder().toPath();
+ Path targetJar =
+ ZipBuilder.builder(dir.resolve("target.jar"))
+ .addFilesRelative(
+ ToolHelper.getClassPathForTests(), ToolHelper.getClassFileForTestClass(I.class))
+ .build();
+ Path sourceJar =
+ ZipBuilder.builder(dir.resolve("source.jar"))
+ .addFilesRelative(
+ ToolHelper.getClassPathForTests(), ToolHelper.getClassFileForTestClass(Main.class))
+ .build();
+ SeenReferencesConsumer consumer = new SeenReferencesConsumer();
+
+ TraceReferences.run(
+ TraceReferencesCommand.builder()
+ .addLibraryFiles(ToolHelper.getMostRecentAndroidJar())
+ .addSourceFiles(sourceJar)
+ .addTargetFiles(targetJar)
+ .setConsumer(consumer)
+ .build());
+
+ ImmutableSet<MethodReference> expectedSet =
+ ImmutableSet.of(
+ Reference.method(
+ Reference.classFromClass(I.class), "m", Collections.emptyList(), null));
+ assertEquals(expectedSet, consumer.seenMethods);
+ }
+
+ interface I {
+
+ void m();
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ I i = () -> {};
+ }
+ }
+}