Add tests for vertical class merging
Change-Id: I8d159cd6a3f0017bad4661f5a472cf88b33df2c6
diff --git a/src/test/examples/classmerging/ConflictingInterfaceSignaturesTest.java b/src/test/examples/classmerging/ConflictingInterfaceSignaturesTest.java
new file mode 100644
index 0000000..223ff37
--- /dev/null
+++ b/src/test/examples/classmerging/ConflictingInterfaceSignaturesTest.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2018, 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 classmerging;
+
+public class ConflictingInterfaceSignaturesTest {
+
+ public static void main(String[] args) {
+ A a = new InterfaceImpl();
+ a.foo();
+
+ B b = new InterfaceImpl();
+ b.foo();
+ }
+
+ public interface A {
+ void foo();
+ }
+
+ public interface B {
+ void foo();
+ }
+
+ public static final class InterfaceImpl implements A, B {
+
+ @Override
+ public void foo() {
+ System.out.println("In foo on InterfaceImpl");
+ }
+ }
+}
diff --git a/src/test/examples/classmerging/SimpleInterface.java b/src/test/examples/classmerging/SimpleInterface.java
new file mode 100644
index 0000000..e8ebcab
--- /dev/null
+++ b/src/test/examples/classmerging/SimpleInterface.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2018, 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 classmerging;
+
+public interface SimpleInterface {
+ void foo();
+}
diff --git a/src/test/examples/classmerging/SimpleInterfaceAccessTest.java b/src/test/examples/classmerging/SimpleInterfaceAccessTest.java
new file mode 100644
index 0000000..04b5386
--- /dev/null
+++ b/src/test/examples/classmerging/SimpleInterfaceAccessTest.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2018, 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 classmerging;
+
+import classmerging.pkg.SimpleInterfaceImplRetriever;
+
+public class SimpleInterfaceAccessTest {
+ public static void main(String[] args) {
+ // It is not possible to merge the interface SimpleInterface into SimpleInterfaceImpl, since
+ // this would lead to an illegal class access here.
+ SimpleInterface obj = SimpleInterfaceImplRetriever.getSimpleInterfaceImpl();
+ obj.foo();
+ }
+}
diff --git a/src/test/examples/classmerging/keep-rules.txt b/src/test/examples/classmerging/keep-rules.txt
index 814e889..da6ef55 100644
--- a/src/test/examples/classmerging/keep-rules.txt
+++ b/src/test/examples/classmerging/keep-rules.txt
@@ -7,9 +7,15 @@
-keep public class classmerging.Test {
public static void main(...);
}
+-keep public class classmerging.ConflictingInterfaceSignaturesTest {
+ public static void main(...);
+}
-keep public class classmerging.ExceptionTest {
public static void main(...);
}
+-keep public class classmerging.SimpleInterfaceAccessTest {
+ public static void main(...);
+}
-keep public class classmerging.TemplateMethodTest {
public static void main(...);
}
diff --git a/src/test/examples/classmerging/pkg/SimpleInterfaceImplRetriever.java b/src/test/examples/classmerging/pkg/SimpleInterfaceImplRetriever.java
new file mode 100644
index 0000000..5cfa11e
--- /dev/null
+++ b/src/test/examples/classmerging/pkg/SimpleInterfaceImplRetriever.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2018, 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 classmerging.pkg;
+
+import classmerging.SimpleInterface;
+
+public class SimpleInterfaceImplRetriever {
+
+ public static SimpleInterface getSimpleInterfaceImpl() {
+ return new SimpleInterfaceImpl();
+ }
+
+ // This class is intentionally marked private. It is not possible to merge the interface
+ // SimpleInterface into SimpleInterfaceImpl, since this would lead to an illegal class access
+ // in SimpleInterfaceAccessTest.
+ private static class SimpleInterfaceImpl implements SimpleInterface {
+
+ @Override
+ public void foo() {
+ System.out.println("In foo on SimpleInterfaceImpl");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
index 0977533..cf356df 100644
--- a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
@@ -26,12 +26,14 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
+import org.junit.Ignore;
import org.junit.Test;
public class ClassMergingTest extends TestBase {
@@ -108,6 +110,23 @@
assertTrue(inspector.clazz("classmerging.SubClassThatReferencesSuperMethod").isPresent());
}
+ @Test
+ public void testConflictingInterfaceSignatures() throws Exception {
+ String main = "classmerging.ConflictingInterfaceSignaturesTest";
+ Path[] programFiles =
+ new Path[] {
+ CF_DIR.resolve("ConflictingInterfaceSignaturesTest.class"),
+ CF_DIR.resolve("ConflictingInterfaceSignaturesTest$A.class"),
+ CF_DIR.resolve("ConflictingInterfaceSignaturesTest$B.class"),
+ CF_DIR.resolve("ConflictingInterfaceSignaturesTest$InterfaceImpl.class")
+ };
+ Set<String> preservedClassNames =
+ ImmutableSet.of(
+ "classmerging.ConflictingInterfaceSignaturesTest",
+ "classmerging.ConflictingInterfaceSignaturesTest$InterfaceImpl");
+ runTest(main, programFiles, preservedClassNames);
+ }
+
// If an exception class A is merged into another exception class B, then all exception tables
// should be updated, and class A should be removed entirely.
@Test
@@ -146,6 +165,40 @@
assertEquals(2, numberOfMoveExceptionInstructions);
}
+ @Ignore("b/73958515")
+ @Test
+ public void testNoIllegalClassAccess() throws Exception {
+ String main = "classmerging.SimpleInterfaceAccessTest";
+ Path[] programFiles =
+ new Path[] {
+ CF_DIR.resolve("SimpleInterface.class"),
+ CF_DIR.resolve("SimpleInterfaceAccessTest.class"),
+ CF_DIR.resolve("pkg/SimpleInterfaceImplRetriever.class"),
+ CF_DIR.resolve("pkg/SimpleInterfaceImplRetriever$SimpleInterfaceImpl.class")
+ };
+ // SimpleInterface cannot be merged into SimpleInterfaceImpl because SimpleInterfaceImpl
+ // is in a different package and is not public.
+ ImmutableSet<String> preservedClassNames =
+ ImmutableSet.of(
+ "classmerging.SimpleInterface",
+ "classmerging.SimpleInterfaceAccessTest",
+ "classmerging.pkg.SimpleInterfaceImplRetriever",
+ "classmerging.pkg.SimpleInterfaceImplRetriever$SimpleInterfaceImpl");
+ runTest(main, programFiles, preservedClassNames);
+ // If access modifications are allowed then SimpleInterface should be merged into
+ // SimpleInterfaceImpl.
+ preservedClassNames =
+ ImmutableSet.of(
+ "classmerging.SimpleInterfaceAccessTest",
+ "classmerging.pkg.SimpleInterfaceImplRetriever",
+ "classmerging.pkg.SimpleInterfaceImplRetriever$SimpleInterfaceImpl");
+ runTest(
+ main,
+ programFiles,
+ preservedClassNames,
+ getProguardConfig(EXAMPLE_KEEP, "-allowaccessmodification"));
+ }
+
@Test
public void testTemplateMethodPattern() throws Exception {
String main = "classmerging.TemplateMethodTest";
@@ -163,8 +216,14 @@
private DexInspector runTest(String main, Path[] programFiles, Set<String> preservedClassNames)
throws Exception {
+ return runTest(main, programFiles, preservedClassNames, getProguardConfig(EXAMPLE_KEEP, null));
+ }
+
+ private DexInspector runTest(
+ String main, Path[] programFiles, Set<String> preservedClassNames, String proguardConfig)
+ throws Exception {
AndroidApp input = readProgramFiles(programFiles);
- AndroidApp output = compileWithR8(input, EXAMPLE_KEEP, this::configure);
+ AndroidApp output = compileWithR8(input, proguardConfig, this::configure);
DexInspector inspector = new DexInspector(output);
// Check that all classes in [preservedClassNames] are in fact preserved.
for (String className : preservedClassNames) {
@@ -181,4 +240,16 @@
assertEquals(runOnArt(compileWithD8(input), main), runOnArt(output, main));
return inspector;
}
+
+ private String getProguardConfig(Path path, String additionalRules) throws IOException {
+ StringBuilder builder = new StringBuilder();
+ for (String line : Files.readAllLines(path)) {
+ builder.append(line);
+ builder.append(System.lineSeparator());
+ }
+ if (additionalRules != null) {
+ builder.append(additionalRules);
+ }
+ return builder.toString();
+ }
}