Add a test to shrink Kotlin compiler.

Bug: 144859533
Change-Id: Ib9cecc211854a1383c7fd091069e172350a93947
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 55cabed..fb96a96 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -174,6 +174,7 @@
         CfInvoke invoke = (CfInvoke) instruction;
         if (invoke.getMethod().holder.isClassType()) {
           DexClass holder = appView.definitionFor(invoke.getMethod().holder);
+          // TODO(b/144861100): static method in interface?
           assert holder == null || holder.isInterface() == invoke.isInterface();
         }
       }
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNamingState.java b/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
index ccac2d6..c6b7ee0 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNamingState.java
@@ -185,6 +185,7 @@
       if (isDirectMethodCall) {
         return virtualNameCount + directNameCount++;
       } else {
+        // TODO(b/144877828): is it guaranteed?
         assert directNameCount == 0;
         return virtualNameCount++;
       }
diff --git a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
index 4109295..268709d 100644
--- a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -28,21 +28,27 @@
 
   private final CfRuntime jdk;
   private final TestState state;
+  private final String kotlincJar;
   private final List<Path> sources = new ArrayList<>();
   private final List<Path> classpath = new ArrayList<>();
   private Path output = null;
 
-  private KotlinCompilerTool(CfRuntime jdk, TestState state) {
+  private KotlinCompilerTool(CfRuntime jdk, TestState state, Path kotlincJar) {
     this.jdk = jdk;
     this.state = state;
+    this.kotlincJar = kotlincJar == null ? KT_COMPILER : kotlincJar.toString();
   }
 
   public static KotlinCompilerTool create(CfRuntime jdk, TemporaryFolder temp) {
-    return create(jdk, new TestState(temp));
+    return create(jdk, new TestState(temp), null);
   }
 
-  public static KotlinCompilerTool create(CfRuntime jdk, TestState state) {
-    return new KotlinCompilerTool(jdk, state);
+  public static KotlinCompilerTool create (CfRuntime jdk, TemporaryFolder temp, Path kotlincJar) {
+    return create(jdk, new TestState(temp), kotlincJar);
+  }
+
+  public static KotlinCompilerTool create(CfRuntime jdk, TestState state, Path kotlincJar) {
+    return new KotlinCompilerTool(jdk, state, kotlincJar);
   }
 
   public KotlinCompilerTool addSourceFiles(Path files) {
@@ -99,8 +105,8 @@
     cmdline.add(KT_PRELOADER);
     cmdline.add("org.jetbrains.kotlin.preloading.Preloader");
     cmdline.add("-cp");
-    cmdline.add(KT_COMPILER);
-    cmdline.add("org.jetbrains.kotlin.cli.jvm.K2JVMCompiler");
+    cmdline.add(kotlincJar);
+    cmdline.add(ToolHelper.K2JVMCompiler);
     for (Path source : sources) {
       cmdline.add(source.toString());
     }
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 2eb2d42..b90b40c 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -186,6 +186,10 @@
     return KotlinCompilerTool.create(jdk, temp);
   }
 
+  public KotlinCompilerTool kotlinc(CfRuntime jdk, Path kotlincJar) {
+    return KotlinCompilerTool.create(jdk, temp, kotlincJar);
+  }
+
   public static KotlinCompilerTool kotlinc(CfRuntime jdk, TemporaryFolder temp) {
     return KotlinCompilerTool.create(jdk, temp);
   }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 6b224e3..43d08bc 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -129,9 +129,12 @@
       "third_party/rhino-android-1.1.1/rhino-android-1.1.1.jar";
   public static final String RHINO_JAR = "third_party/rhino-1.7.10/rhino-1.7.10.jar";
   static final String KT_PRELOADER = "third_party/kotlin/kotlinc/lib/kotlin-preloader.jar";
-  static final String KT_COMPILER = "third_party/kotlin/kotlinc/lib/kotlin-compiler.jar";
+  public static final String KT_COMPILER = "third_party/kotlin/kotlinc/lib/kotlin-compiler.jar";
+  public static final String K2JVMCompiler = "org.jetbrains.kotlin.cli.jvm.K2JVMCompiler";
   public static final String KT_STDLIB = "third_party/kotlin/kotlinc/lib/kotlin-stdlib.jar";
   public static final String KT_REFLECT = "third_party/kotlin/kotlinc/lib/kotlin-reflect.jar";
+  public static final String KT_SCRIPT_RT =
+      "third_party/kotlin/kotlinc/lib/kotlin-script-runtime.jar";
   private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
   private static final AndroidApiLevel DEFAULT_MIN_SDK = AndroidApiLevel.I;
 
@@ -1284,7 +1287,7 @@
     cmdline.add("org.jetbrains.kotlin.preloading.Preloader");
     cmdline.add("-cp");
     cmdline.add(KT_COMPILER);
-    cmdline.add("org.jetbrains.kotlin.cli.jvm.K2JVMCompiler");
+    cmdline.add(K2JVMCompiler);
     String[] strings = Arrays.stream(filesToCompile).map(Path::toString).toArray(String[]::new);
     Collections.addAll(cmdline, strings);
     cmdline.add("-d");
diff --git a/src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
similarity index 99%
rename from src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java
rename to src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index cafb01b..a19cfec 100644
--- a/src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -1,7 +1,7 @@
 // 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 com.android.tools.r8.cf;
+package com.android.tools.r8.cf.bootstrap;
 
 import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
 import static com.google.common.io.ByteStreams.toByteArray;
diff --git a/src/test/java/com/android/tools/r8/cf/BootstrapTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
similarity index 99%
rename from src/test/java/com/android/tools/r8/cf/BootstrapTest.java
rename to src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
index addc3ec..722e4f6 100644
--- a/src/test/java/com/android/tools/r8/cf/BootstrapTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
@@ -1,7 +1,7 @@
 // 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 com.android.tools.r8.cf;
+package com.android.tools.r8.cf.bootstrap;
 
 import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
 import static com.google.common.io.ByteStreams.toByteArray;
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/Hello.kt b/src/test/java/com/android/tools/r8/cf/bootstrap/Hello.kt
new file mode 100644
index 0000000..fdfc655
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/Hello.kt
@@ -0,0 +1,8 @@
+// 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.cf.bootstrap
+
+fun main(args : Array<String>) {
+  println("I'm Woody. Howdy, howdy, howdy.")
+}
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java
new file mode 100644
index 0000000..cef8fce
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java
@@ -0,0 +1,133 @@
+// 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.cf.bootstrap;
+
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.internal.CompilationTestBase;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.Ignore;
+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 KotlinCompilerTreeShakingTest extends CompilationTestBase {
+
+  private static final String PKG_NAME = KotlinCompilerTreeShakingTest.class.getPackage().getName();
+  private static final Path HELLO_KT =
+      Paths.get(
+          ToolHelper.TESTS_DIR,
+          "java",
+          DescriptorUtils.getBinaryNameFromJavaType(PKG_NAME),
+          "Hello.kt");
+  private static final int MAX_SIZE = (int) (31361268 * 0.4);
+
+  private TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withCfRuntimes().build();
+  }
+
+  public KotlinCompilerTreeShakingTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testForRuntime() throws Exception {
+    // Compile Hello.kt and make sure it works as expected.
+    Path classPathBefore =
+        kotlinc(parameters.getRuntime().asCf())
+            .addSourceFiles(HELLO_KT)
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar())
+        .addClasspath(classPathBefore)
+        .run(parameters.getRuntime(), PKG_NAME + ".HelloKt")
+        .assertSuccessWithOutputLines("I'm Woody. Howdy, howdy, howdy.");
+  }
+
+  @Ignore(
+      "b/136457753: assertion error in static class merger; "
+          + "b/144861100: assertion error in CfBuilder; "
+          + "b/144861881: force-inlining of non-inlineable constructors in vertical class merger; "
+          + "b/144877828: assertion error in method naming state during interface method renaming; "
+          + "b/144859533: umbrella"
+  )
+  @Test
+  public void test() throws Exception {
+    List<Path> libs =
+        ImmutableList.of(
+            ToolHelper.getKotlinStdlibJar(),
+            ToolHelper.getKotlinReflectJar(),
+            Paths.get(ToolHelper.KT_SCRIPT_RT));
+    // Process kotlin-compiler.jar.
+    Path r8ProcessedKotlinc =
+        testForR8(parameters.getBackend())
+            .addLibraryFiles(libs)
+            .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
+            .addProgramFiles(Paths.get(ToolHelper.KT_COMPILER))
+            .addKeepAttributes("*Annotation*")
+            .addKeepClassAndMembersRules(ToolHelper.K2JVMCompiler)
+            .addKeepClassAndMembersRules("**.K2JVMCompilerArguments")
+            .addKeepClassAndMembersRules("**.*Argument*")
+            .addKeepClassAndMembersRules("**.Freezable")
+            .addKeepRules(
+                "-keepclassmembers class * {",
+                "  *** parseCommandLineArguments(...);",
+                "}"
+            )
+            .addKeepRules(
+                "-keepclassmembers,allowoptimization enum * {",
+                "    public static **[] values();",
+                "    public static ** valueOf(java.lang.String);",
+                "}")
+            .addOptionsModification(o -> {
+              // Ignore com.sun.tools.javac.main.JavaCompiler and others
+              // Resulting jar may not be able to deal with .java source files, though.
+              o.ignoreMissingClasses = true;
+              // TODO(b/144861881): force-inlining of non-inlineable constructors.
+              o.enableVerticalClassMerging = false;
+            })
+            .compile()
+            .writeToZip();
+
+    // Copy libraries used by kotlin-compiler.jar so that Preloader can load them.
+    Path dir = r8ProcessedKotlinc.getParent();
+    for (Path lib : libs) {
+      Path newLib = dir.resolve(lib.getFileName());
+      Files.copy(lib, newLib, REPLACE_EXISTING);
+    }
+
+    // TODO(b/144859533): passing `dir` as -kotlin-home.
+    // Compile Hello.kt again with r8-processed kotlin-compiler.jar
+    Path classPathAfter =
+        kotlinc(parameters.getRuntime().asCf(), r8ProcessedKotlinc)
+            .addSourceFiles(HELLO_KT)
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar())
+        .addClasspath(classPathAfter)
+        .run(parameters.getRuntime(), PKG_NAME + ".HelloKt")
+        .assertSuccessWithOutputLines("I'm Woody. Howdy, howdy, howdy.");
+
+    int size = applicationSize(AndroidApp.builder().addProgramFile(r8ProcessedKotlinc).build());
+    assertTrue(size <= MAX_SIZE);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
index 148f165..2533619 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
@@ -14,7 +14,7 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.cf.BootstrapCurrentEqualityTest;
+import com.android.tools.r8.cf.bootstrap.BootstrapCurrentEqualityTest;
 import com.android.tools.r8.desugar.desugaredlibrary.CoreLibDesugarTestBase;
 import java.io.File;
 import java.nio.file.Path;
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
index 8af167c..1f6832c 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
@@ -14,7 +14,7 @@
 import com.android.tools.r8.TestRuntime;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.cf.BootstrapCurrentEqualityTest;
+import com.android.tools.r8.cf.bootstrap.BootstrapCurrentEqualityTest;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;