Move testClassesHaveBeenMerged() to ClassesHaveBeenMergedTest
Change-Id: Ibc0c9bdcc225e27584cbdc746bed32ed7c85a174
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 6bf8757..59c7b8e 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -30,6 +30,7 @@
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.base.Predicates;
@@ -398,6 +399,10 @@
return appInfo.options();
}
+ public TestingOptions testing() {
+ return options().testing;
+ }
+
public RootSet rootSet() {
return rootSet;
}
@@ -466,6 +471,9 @@
public void setVerticallyMergedClasses(VerticallyMergedClasses verticallyMergedClasses) {
assert this.verticallyMergedClasses == null;
this.verticallyMergedClasses = verticallyMergedClasses;
+ if (testing().verticallyMergedClassesConsumer != null) {
+ testing().verticallyMergedClassesConsumer.accept(dexItemFactory(), verticallyMergedClasses);
+ }
}
public EnumValueInfoMapCollection unboxedEnums() {
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 1633feb..eb2d165 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -35,6 +35,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.conversion.MethodProcessingId;
@@ -1222,6 +1223,9 @@
new DefaultRepackagingConfiguration(
appView.dexItemFactory(), appView.options().getProguardConfiguration());
+ public BiConsumer<DexItemFactory, VerticallyMergedClasses> verticallyMergedClassesConsumer =
+ null;
+
public Consumer<Deque<SortedProgramMethodSet>> waveModifier = waves -> {};
/**
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index d7bebd8..a92fefd 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1648,6 +1648,10 @@
return path;
}
+ public static DexType toDexType(Class<?> clazz, DexItemFactory dexItemFactory) {
+ return dexItemFactory.createType(descriptor(clazz));
+ }
+
public static String binaryName(Class<?> clazz) {
return DescriptorUtils.getBinaryNameFromJavaType(typeName(clazz));
}
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 3e5ba5f..1fa6604 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -10,6 +10,8 @@
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.debug.DebugTestConfig;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.testing.AndroidBuildVersion;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
@@ -26,6 +28,7 @@
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -103,6 +106,12 @@
});
}
+ public T addVerticallyMergedClassesInspector(
+ BiConsumer<DexItemFactory, VerticallyMergedClasses> inspector) {
+ return addOptionsModification(
+ options -> options.testing.verticallyMergedClassesConsumer = inspector);
+ }
+
public CR compile() throws CompilationFailedException {
AndroidAppConsumers sink = new AndroidAppConsumers();
builder.setProgramConsumer(sink.wrapProgramConsumer(programConsumer));
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/ClassesHaveBeenMergedTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/ClassesHaveBeenMergedTest.java
new file mode 100644
index 0000000..8577fae
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/ClassesHaveBeenMergedTest.java
@@ -0,0 +1,186 @@
+// Copyright (c) 2020, 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.classmerging.vertical;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ClassesHaveBeenMergedTest extends VerticalClassMergerTestBase {
+
+ public ClassesHaveBeenMergedTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test
+ public void testClassesHaveBeenMerged() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ClassesHaveBeenMergedTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addVerticallyMergedClassesInspector(this::inspectVerticallyMergedClasses)
+ .enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccess();
+ }
+
+ private void inspectVerticallyMergedClasses(
+ DexItemFactory dexItemFactory, VerticallyMergedClasses verticallyMergedClasses) {
+ assertMergedIntoSubtype(GenericInterface.class, dexItemFactory, verticallyMergedClasses);
+ assertMergedIntoSubtype(GenericAbstractClass.class, dexItemFactory, verticallyMergedClasses);
+ assertMergedIntoSubtype(Outer.SuperClass.class, dexItemFactory, verticallyMergedClasses);
+ assertMergedIntoSubtype(SuperClass.class, dexItemFactory, verticallyMergedClasses);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ assertThat(inspector.clazz(GenericInterfaceImpl.class), isPresent());
+ assertThat(inspector.clazz(Outer.SubClass.class), isPresent());
+ assertThat(inspector.clazz(SubClass.class), isPresent());
+ assertThat(inspector.clazz(GenericInterface.class), not(isPresent()));
+ assertThat(inspector.clazz(GenericAbstractClass.class), not(isPresent()));
+ assertThat(inspector.clazz(Outer.SuperClass.class), not(isPresent()));
+ assertThat(inspector.clazz(SuperClass.class), not(isPresent()));
+ }
+
+ public static class TestClass {
+
+ public static void main(String... args) {
+ GenericInterface<?> iface = new GenericInterfaceImpl();
+ callMethodOnIface(iface);
+ GenericAbstractClass<?> clazz = new GenericAbstractClassImpl();
+ callMethodOnAbstractClass(clazz);
+ Outer outer = new Outer();
+ Outer.SubClass inner = outer.getInstance();
+ System.out.println(outer.getInstance().method());
+ System.out.println(new SubClass(42));
+
+ // Ensure that the instantiations are not dead code eliminated.
+ escape(clazz);
+ escape(iface);
+ escape(inner);
+ escape(outer);
+ }
+
+ private static void callMethodOnIface(GenericInterface<?> iface) {
+ System.out.println(iface.method());
+ }
+
+ private static void callMethodOnAbstractClass(GenericAbstractClass<?> clazz) {
+ System.out.println(clazz.method());
+ System.out.println(clazz.otherMethod());
+ }
+
+ @NeverInline
+ static void escape(Object o) {
+ if (System.currentTimeMillis() < 0) {
+ System.out.println(o);
+ }
+ }
+ }
+
+ public abstract static class GenericAbstractClass<T> {
+
+ public abstract T method();
+
+ public T otherMethod() {
+ return null;
+ }
+ }
+
+ public static class GenericAbstractClassImpl extends GenericAbstractClass<String> {
+
+ @Override
+ public String method() {
+ return "Hello from GenericAbstractClassImpl";
+ }
+
+ @Override
+ public String otherMethod() {
+ return "otherMethod";
+ }
+ }
+
+ public interface GenericInterface<T> {
+
+ T method();
+ }
+
+ @NoHorizontalClassMerging
+ public static class GenericInterfaceImpl implements GenericInterface<String> {
+
+ @Override
+ public String method() {
+ return "method";
+ }
+ }
+
+ public static class SuperClass {
+
+ private final int field;
+
+ public SuperClass(int field) {
+ this.field = field;
+ }
+
+ public int getField() {
+ return field;
+ }
+ }
+
+ public static class SubClass extends SuperClass {
+
+ private int field;
+
+ public SubClass(int field) {
+ this(field, field + 100);
+ }
+
+ public SubClass(int one, int other) {
+ super(one);
+ field = other;
+ }
+
+ public String toString() {
+ return "is " + field + " " + getField();
+ }
+ }
+
+ static class Outer {
+
+ /**
+ * This class is package private to trigger the generation of bridge methods for the visibility
+ * change of methods from public subtypes.
+ */
+ static class SuperClass {
+
+ public String method() {
+ return "Method in SuperClass.";
+ }
+ }
+
+ @NoHorizontalClassMerging
+ public static class SubClass extends SuperClass {
+ // Intentionally left empty.
+ }
+
+ public SubClass getInstance() {
+ return new SubClass();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index e831c02..37e4bc6 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -57,7 +57,6 @@
import java.nio.file.Paths;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.IntStream;
@@ -101,7 +100,7 @@
}
private void runR8(Path proguardConfig, Consumer<InternalOptions> optionsConsumer)
- throws IOException, ExecutionException, CompilationFailedException {
+ throws IOException, CompilationFailedException {
inspector =
testForR8(parameters.getBackend())
.addProgramFiles(EXAMPLE_JAR)
@@ -124,19 +123,6 @@
);
@Test
- public void testClassesHaveBeenMerged() throws Throwable {
- expectThrowsWithHorizontalClassMerging();
- runR8(EXAMPLE_KEEP, this::configure);
- // GenericInterface should be merged into GenericInterfaceImpl.
- for (String candidate : CAN_BE_MERGED) {
- assertThat(inspector.clazz(candidate), not(isPresent()));
- }
- assertThat(inspector.clazz("classmerging.GenericInterfaceImpl"), isPresent());
- assertThat(inspector.clazz("classmerging.Outer$SubClass"), isPresent());
- assertThat(inspector.clazz("classmerging.SubClass"), isPresent());
- }
-
- @Test
public void testClassesHaveNotBeenMerged() throws Throwable {
runR8(DONT_OPTIMIZE, null);
for (String candidate : CAN_BE_MERGED) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTestBase.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTestBase.java
new file mode 100644
index 0000000..8aaf61e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTestBase.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2020, 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.classmerging.vertical;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
+import org.junit.runners.Parameterized.Parameters;
+
+public abstract class VerticalClassMergerTestBase extends TestBase {
+
+ protected final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return TestBase.getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public VerticalClassMergerTestBase(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ public static void assertMergedIntoSubtype(
+ Class<?> clazz,
+ DexItemFactory dexItemFactory,
+ VerticallyMergedClasses verticallyMergedClasses) {
+ assertTrue(verticallyMergedClasses.hasBeenMergedIntoSubtype(toDexType(clazz, dexItemFactory)));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithNonVisibleAnnotation.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithNonVisibleAnnotationTest.java
similarity index 78%
rename from src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithNonVisibleAnnotation.java
rename to src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithNonVisibleAnnotationTest.java
index bb33801..795837e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithNonVisibleAnnotation.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithNonVisibleAnnotationTest.java
@@ -14,8 +14,8 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.classmerging.vertical.testclasses.Outer;
-import com.android.tools.r8.classmerging.vertical.testclasses.Outer.Base;
+import com.android.tools.r8.classmerging.vertical.testclasses.VerticalClassMergingWithNonVisibleAnnotationTestClasses;
+import com.android.tools.r8.classmerging.vertical.testclasses.VerticalClassMergingWithNonVisibleAnnotationTestClasses.Base;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -26,7 +26,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class VerticalClassMergingWithNonVisibleAnnotation extends TestBase {
+public class VerticalClassMergingWithNonVisibleAnnotationTest extends TestBase {
private final TestParameters parameters;
@@ -35,18 +35,20 @@
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public VerticalClassMergingWithNonVisibleAnnotation(TestParameters parameters) {
+ public VerticalClassMergingWithNonVisibleAnnotationTest(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
- .addInnerClasses(Outer.class)
+ .addInnerClasses(VerticalClassMergingWithNonVisibleAnnotationTestClasses.class)
.addProgramClasses(Sub.class)
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Sub.class)
- .addKeepClassRules(Outer.class.getPackage().getName() + ".Outer$Private* { *; }")
+ .addKeepClassRules(
+ VerticalClassMergingWithNonVisibleAnnotationTestClasses.class.getTypeName()
+ + "$Private* { *; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
@@ -65,7 +67,8 @@
assertThat(foo, isPresent());
AnnotationSubject privateMethodAnnotation =
foo.annotation(
- Outer.class.getPackage().getName() + ".Outer$PrivateMethodAnnotation");
+ VerticalClassMergingWithNonVisibleAnnotationTestClasses.class.getTypeName()
+ + "$PrivateMethodAnnotation");
assertThat(privateMethodAnnotation, isPresent());
});
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/Outer.java b/src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/VerticalClassMergingWithNonVisibleAnnotationTestClasses.java
similarity index 92%
rename from src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/Outer.java
rename to src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/VerticalClassMergingWithNonVisibleAnnotationTestClasses.java
index 46ef8c8..3b8bf5b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/Outer.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/VerticalClassMergingWithNonVisibleAnnotationTestClasses.java
@@ -8,7 +8,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-public class Outer {
+public class VerticalClassMergingWithNonVisibleAnnotationTestClasses {
@Retention(RetentionPolicy.RUNTIME)
private @interface PrivateClassAnnotation {}