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));
   }