Add a test for improving tree shaking in presence of uninitialized types
Bug: 132669230
Change-Id: I6739a0851824978a6ea7034960ad28bcb278fa09
diff --git a/src/test/java/com/android/tools/r8/regress/B76025099.java b/src/test/java/com/android/tools/r8/regress/B76025099.java
index 9158a4e..4f0b5ec 100644
--- a/src/test/java/com/android/tools/r8/regress/B76025099.java
+++ b/src/test/java/com/android/tools/r8/regress/B76025099.java
@@ -133,7 +133,7 @@
CodeInspector inspector = new CodeInspector(processedApp);
ClassSubject impl = inspector.clazz(Impl.class);
assertThat(impl, isPresent());
- MethodSubject init = impl.init(ImmutableList.of("java.lang.String"));
+ MethodSubject init = impl.init("java.lang.String");
assertThat(init, isPresent());
Iterator<InstructionSubject> iterator = init.iterateInstructions();
diff --git a/src/test/java/com/android/tools/r8/shaking/UninitializedInstantiatedTypeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/UninitializedInstantiatedTypeShakingTest.java
new file mode 100644
index 0000000..7a9be4a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/UninitializedInstantiatedTypeShakingTest.java
@@ -0,0 +1,101 @@
+// 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.shaking;
+
+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.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+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 UninitializedInstantiatedTypeShakingTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().build();
+ }
+
+ public UninitializedInstantiatedTypeShakingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(UninitializedInstantiatedTypeShakingTest.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getRuntime())
+ .compile()
+ .inspect(UninitializedInstantiatedTypeShakingTest::verifyOutput)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("A.<clinit>()", "B.method()");
+ }
+
+ private static void verifyOutput(CodeInspector inspector) {
+ ClassSubject classSubject = inspector.clazz(A.class);
+ assertThat(classSubject.init(), not(isPresent()));
+ // TODO(b/132669230): A.method() should be pruned.
+ assertThat(classSubject.uniqueMethodWithName("method"), isPresent());
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ // Instantiate A. The instantiation cannot be removed because the class initialization of A
+ // has side effects. However, the constructor call to A.<init>() can be removed, since it has
+ // no side effects.
+ new A();
+
+ // Invoke I.m(). Since we remove the call to A.<init>(), there are no more initialized
+ // instances of A. Therefore, the method invocation below should never be able to hit A.m().
+ I obj = System.currentTimeMillis() >= 0 ? new B() : new C();
+ obj.method();
+ }
+ }
+
+ interface I {
+
+ void method();
+ }
+
+ static class A implements I {
+
+ static {
+ System.out.println("A.<clinit>()");
+ }
+
+ @Override
+ public void method() {
+ System.out.println("A.method()");
+ }
+ }
+
+ static class B implements I {
+
+ @Override
+ public void method() {
+ System.out.println("B.method()");
+ }
+ }
+
+ static class C implements I {
+
+ @Override
+ public void method() {
+ System.out.println("C.method()");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index 6863b0e..6773501 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -152,8 +152,7 @@
CodeInspector inspector = new CodeInspector(ToolHelper.runR8(builder.build()));
ClassSubject clazz = inspector.clazz(getJavacGeneratedClassName(testClass));
assertTrue(clazz.isPresent());
- assertEquals(forceProguardCompatibility && hasDefaultConstructor,
- clazz.init(ImmutableList.of()).isPresent());
+ assertEquals(forceProguardCompatibility && hasDefaultConstructor, clazz.init().isPresent());
// Check the Proguard compatibility rules generated.
ProguardConfigurationParser parser =
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultctor/ExternalizableTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultctor/ExternalizableTest.java
index bc3c3bd..2a8f1cd 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultctor/ExternalizableTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/defaultctor/ExternalizableTest.java
@@ -317,7 +317,7 @@
CodeInspector codeInspector = new CodeInspector(processedApp, proguardMap);
ClassSubject classSubject = codeInspector.clazz(ExternalizableDataClass.class);
assertThat(classSubject, isPresent());
- MethodSubject init = classSubject.init(ImmutableList.of());
+ MethodSubject init = classSubject.init();
assertThat(init, isPresent());
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalKeepIfKeptTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalKeepIfKeptTest.java
index c5d2ca1..715e565 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalKeepIfKeptTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalKeepIfKeptTest.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import java.util.Collections;
import java.util.List;
import org.junit.Assume;
import org.junit.Test;
@@ -64,7 +63,7 @@
assertEquals(0, classSubject.allFields().size());
// TODO(b/132318799): Full mode no-marker should not keep <init>() when not specified.
assertEquals(useMarker ? 0 : 1, classSubject.allMethods().size());
- assertEquals(!useMarker, classSubject.init(Collections.emptyList()).isPresent());
+ assertEquals(!useMarker, classSubject.init().isPresent());
});
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index 1bf79b7..8cd704f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -14,6 +14,7 @@
import com.google.common.collect.ImmutableList;
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -72,6 +73,10 @@
return method("void", "<init>", parameters);
}
+ public MethodSubject init(String... parameters) {
+ return init(Arrays.asList(parameters));
+ }
+
public MethodSubject method(MethodSignature signature) {
return method(signature.type, signature.name, ImmutableList.copyOf(signature.parameters));
}