Don't use single target lookup results on library classes
Bug: 145645482
Change-Id: Ia042e128e4de8c175afbcafcf2072486e237412a
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 83d6bd1..69e1930 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -1026,7 +1026,9 @@
DexProgramClass refinedHolder =
(refinedReceiverIsStrictSubType ? definitionFor(refinedReceiverType) : holder)
.asProgramClass();
- assert refinedHolder != null;
+ if (refinedHolder == null) {
+ return null;
+ }
assert !refinedHolder.isInterface();
if (method.isSingleVirtualMethodCached(refinedReceiverType)) {
return method.getSingleVirtualMethodCache(refinedReceiverType);
diff --git a/src/test/java/com/android/tools/r8/resolution/LibraryExtendsProgramRefinedReceiverIsLibraryClass.java b/src/test/java/com/android/tools/r8/resolution/LibraryExtendsProgramRefinedReceiverIsLibraryClass.java
new file mode 100644
index 0000000..db1864c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/LibraryExtendsProgramRefinedReceiverIsLibraryClass.java
@@ -0,0 +1,113 @@
+// 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.resolution;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class LibraryExtendsProgramRefinedReceiverIsLibraryClass extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ // Only test on CF, as the test use additional runtime classpath classes.
+ return getTestParameters().withCfRuntimes().build();
+ }
+
+ // Regression test for b/145645482. Resolution issue related to lookupSingleVirtualTarget for
+ // library extending program. The concrete issue hit in the Android Platform build this was in
+ // a debug build, where the "debug write" instruction introduces an "alias" causing the
+ // "receiver lower bound" to be unknown (null). With a receiver type of ProgramClass and a
+ // "refined receiver type" of LibraryClass (lattice type of the debug write instruction) the
+ // issue appeared.
+ //
+ // However, with a phi the same could happen in release mode. Again the "refined receiver type"
+ // becomes LibraryClass (lattice type of the phi).
+
+ public LibraryExtendsProgramRefinedReceiverIsLibraryClass(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void regression145645482DebugMode() throws Exception {
+ testForR8Compat(parameters.getBackend())
+ .addLibraryFiles(runtimeJar(parameters.getBackend()))
+ .addLibraryClasses(LibraryClass.class)
+ .addProgramClasses(ProgramClass.class, ProgramTestRunnerWithoutPhi.class)
+ .enableInliningAnnotations()
+ .addKeepClassRules(ProgramClass.class)
+ .addKeepMainRule(ProgramTestRunnerWithoutPhi.class)
+ .setMinApi(parameters.getApiLevel())
+ .debug()
+ .compile()
+ .addRunClasspathClasses(LibraryClass.class)
+ .run(parameters.getRuntime(), ProgramTestRunnerWithoutPhi.class)
+ .assertSuccessWithOutput(StringUtils.lines("SUCCESS"));
+ }
+
+ @Test
+ public void regression145645482ReleaseMode() throws Exception {
+ testForR8Compat(parameters.getBackend())
+ .addLibraryFiles(runtimeJar(parameters.getBackend()))
+ .addLibraryClasses(LibraryClass.class)
+ .addProgramClasses(ProgramClass.class, ProgramTestRunnerWithPhi.class)
+ .enableInliningAnnotations()
+ .addKeepClassRules(ProgramClass.class)
+ .addKeepMainRule(ProgramTestRunnerWithPhi.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addRunClasspathClasses(LibraryClass.class)
+ .run(parameters.getRuntime(), ProgramTestRunnerWithPhi.class)
+ .assertSuccessWithOutput(StringUtils.lines("SUCCESS"));
+ }
+
+ static class ProgramClass {
+ public void addTestSuite(Class<?> suite) {
+ }
+ }
+
+ static class LibraryClass extends ProgramClass {
+ @Override
+ public void addTestSuite(Class<?> suite) {
+ super.addTestSuite(suite);
+ }
+ }
+
+ static class ProgramTestRunnerWithoutPhi {
+ @NeverInline
+ public static ProgramClass test() {
+ ProgramClass suite = new LibraryClass();
+ suite.addTestSuite(ProgramTestRunnerWithoutPhi.class);
+ return suite;
+ }
+
+ public static void main(String[] args) {
+ ProgramTestRunnerWithoutPhi.test();
+ System.out.println("SUCCESS");
+ }
+ }
+
+ static class ProgramTestRunnerWithPhi {
+ @NeverInline
+ public static ProgramClass test(ProgramClass otherSuite) {
+ ProgramClass suite = System.currentTimeMillis() > 0 ? new LibraryClass(): otherSuite;
+ suite.addTestSuite(ProgramTestRunnerWithPhi.class);
+ return suite;
+ }
+
+ public static void main(String[] args) {
+ ProgramTestRunnerWithPhi.test(null);
+ System.out.println("SUCCESS");
+ }
+ }
+}