Reland "Add test for JvmStatic on functions, interfaces and props"

This reverts commit ddfaeca4110d274e29e4676d11f23c67b10d3ac7.

Change-Id: Icfd345359e83b0b8e60b20da98e3e6bed7df58b8
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
new file mode 100644
index 0000000..2d3834b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
@@ -0,0 +1,156 @@
+// 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.kotlin.metadata;
+
+import static com.android.tools.r8.KotlinCompilerTool.KOTLINC;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.kotlin.metadata.jvmstatic_app.MainJava;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.KmClassSubject;
+import com.android.tools.r8.utils.codeinspector.KmFunctionSubject;
+import com.android.tools.r8.utils.codeinspector.KmPropertySubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteJvmStaticTest extends KotlinMetadataTestBase {
+
+  private static final String EXPECTED =
+      StringUtils.lines("Hello, Hello", "Calling func...", "Foo");
+  private static final String PKG_LIB = PKG + ".jvmstatic_lib";
+  private static final String PKG_APP = PKG + ".jvmstatic_app";
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withCfRuntimes().build();
+  }
+
+  public MetadataRewriteJvmStaticTest(TestParameters parameters) {
+    // We are testing static methods on interfaces which requires java 8.
+    super(KotlinTargetVersion.JAVA_8);
+    this.parameters = parameters;
+  }
+
+  private static Path kotlincLibJar = Paths.get("");
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    kotlincLibJar =
+        kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8)
+            .addSourceFiles(
+                getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib"))
+            .compile();
+  }
+
+  @Test
+  public void smokeTest() throws Exception {
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(kotlincLibJar)
+            .addSourceFiles(
+                getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), kotlincLibJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG_APP + ".MainKt")
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void smokeTestJava() throws Exception {
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), kotlincLibJar)
+        .addProgramClassFileData(MainJava.dump())
+        .run(parameters.getRuntime(), MainJava.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  @Test
+  public void testMetadata() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addProgramFiles(kotlincLibJar)
+            .addKeepAllClassesRule()
+            .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+            .compile()
+            .inspect(this::inspect)
+            .writeToZip();
+    testKotlin(libJar);
+    testJava(libJar);
+  }
+
+  private void testKotlin(Path libJar) throws Exception {
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(
+                getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG_APP + ".MainKt")
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  private void testJava(Path libJar) throws Exception {
+    testForJvm()
+        .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar)
+        .addProgramClassFileData(MainJava.dump())
+        .run(parameters.getRuntime(), MainJava.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  private void inspect(CodeInspector inspector) {
+    inspectLib(inspector);
+    inspectInterfaceWithCompanion(inspector);
+  }
+
+  private void inspectLib(CodeInspector inspector) {
+    ClassSubject clazz = inspector.clazz(PKG_LIB + ".Lib");
+    assertThat(clazz, isPresent());
+    KmClassSubject kmClass = clazz.getKmClass();
+    assertThat(kmClass, isPresent());
+    KmFunctionSubject staticFun = kmClass.kmFunctionWithUniqueName("staticFun");
+    assertThat(staticFun, isPresent());
+    assertEquals("staticFun(Lkotlin/jvm/functions/Function0;)V", staticFun.signature().asString());
+    KmPropertySubject staticProp = kmClass.kmPropertyWithUniqueName("staticProp");
+    assertThat(staticProp, isPresent());
+  }
+
+  private void inspectInterfaceWithCompanion(CodeInspector inspector) {
+    ClassSubject itf = inspector.clazz(PKG_LIB + ".InterfaceWithCompanion");
+    assertThat(itf, isPresent());
+    MethodSubject greet = itf.uniqueMethodWithName("greet");
+    assertThat(greet, isPresent());
+
+    ClassSubject itfCompanion = inspector.clazz(PKG_LIB + ".InterfaceWithCompanion$Companion");
+    assertThat(itfCompanion, isPresent());
+    KmClassSubject kmClass = itfCompanion.getKmClass();
+    KmFunctionSubject greetKm = kmClass.kmFunctionWithUniqueName("greet");
+    assertThat(greetKm, isPresent());
+    assertEquals("greet(Ljava/lang/String;)V", greetKm.signature().asString());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/jvmstatic_app/MainJava.java b/src/test/java/com/android/tools/r8/kotlin/metadata/jvmstatic_app/MainJava.java
new file mode 100644
index 0000000..cfae3ef
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/jvmstatic_app/MainJava.java
@@ -0,0 +1,130 @@
+// 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.kotlin.metadata.jvmstatic_app;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+public class MainJava implements Opcodes {
+
+  // The java code cannot reference the kotlin-code when running in gradle, so we have it here
+  // as a dump.
+
+  // public static void main(String[] args) {
+  //   InterfaceWithCompanion.greet("Hello");
+  //   Lib.staticFun(() -> true);
+  //   Lib.setStaticProp("Foo");
+  //   System.out.println(Lib.getStaticProp());
+  // }
+
+  public static byte[] dump() {
+
+    ClassWriter classWriter = new ClassWriter(0);
+    MethodVisitor methodVisitor;
+
+    classWriter.visit(
+        V1_8,
+        ACC_PUBLIC | ACC_SUPER,
+        "com/android/tools/r8/kotlin/metadata/jvmstatic_app/MainJava",
+        null,
+        "java/lang/Object",
+        null);
+
+    classWriter.visitInnerClass(
+        "java/lang/invoke/MethodHandles$Lookup",
+        "java/lang/invoke/MethodHandles",
+        "Lookup",
+        ACC_PUBLIC | ACC_FINAL | ACC_STATIC);
+
+    {
+      methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      methodVisitor.visitCode();
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+      methodVisitor.visitInsn(RETURN);
+      methodVisitor.visitMaxs(1, 1);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor =
+          classWriter.visitMethod(
+              ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+      methodVisitor.visitCode();
+      methodVisitor.visitLdcInsn("Hello");
+      methodVisitor.visitMethodInsn(
+          INVOKESTATIC,
+          "com/android/tools/r8/kotlin/metadata/jvmstatic_lib/InterfaceWithCompanion",
+          "greet",
+          "(Ljava/lang/String;)V",
+          true);
+      methodVisitor.visitInvokeDynamicInsn(
+          "invoke",
+          "()Lkotlin/jvm/functions/Function0;",
+          new Handle(
+              Opcodes.H_INVOKESTATIC,
+              "java/lang/invoke/LambdaMetafactory",
+              "metafactory",
+              "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;",
+              false),
+          new Object[] {
+            Type.getType("()Ljava/lang/Object;"),
+            new Handle(
+                Opcodes.H_INVOKESTATIC,
+                "com/android/tools/r8/kotlin/metadata/jvmstatic_app/MainJava",
+                "lambda$main$0",
+                "()Ljava/lang/Boolean;",
+                false),
+            Type.getType("()Ljava/lang/Boolean;")
+          });
+      methodVisitor.visitMethodInsn(
+          INVOKESTATIC,
+          "com/android/tools/r8/kotlin/metadata/jvmstatic_lib/Lib",
+          "staticFun",
+          "(Lkotlin/jvm/functions/Function0;)V",
+          false);
+      methodVisitor.visitLdcInsn("Foo");
+      methodVisitor.visitMethodInsn(
+          INVOKESTATIC,
+          "com/android/tools/r8/kotlin/metadata/jvmstatic_lib/Lib",
+          "setStaticProp",
+          "(Ljava/lang/String;)V",
+          false);
+      methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      methodVisitor.visitMethodInsn(
+          INVOKESTATIC,
+          "com/android/tools/r8/kotlin/metadata/jvmstatic_lib/Lib",
+          "getStaticProp",
+          "()Ljava/lang/String;",
+          false);
+      methodVisitor.visitMethodInsn(
+          INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+      methodVisitor.visitInsn(RETURN);
+      methodVisitor.visitMaxs(2, 1);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor =
+          classWriter.visitMethod(
+              ACC_PRIVATE | ACC_STATIC | ACC_SYNTHETIC,
+              "lambda$main$0",
+              "()Ljava/lang/Boolean;",
+              null,
+              null);
+      methodVisitor.visitCode();
+      methodVisitor.visitInsn(ICONST_1);
+      methodVisitor.visitMethodInsn(
+          INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
+      methodVisitor.visitInsn(ARETURN);
+      methodVisitor.visitMaxs(1, 0);
+      methodVisitor.visitEnd();
+    }
+    classWriter.visitEnd();
+
+    return classWriter.toByteArray();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/jvmstatic_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/jvmstatic_app/main.kt
new file mode 100644
index 0000000..6bd1218
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/jvmstatic_app/main.kt
@@ -0,0 +1,15 @@
+// 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.kotlin.metadata.jvmstatic_app
+
+import com.android.tools.r8.kotlin.metadata.jvmstatic_lib.InterfaceWithCompanion
+import com.android.tools.r8.kotlin.metadata.jvmstatic_lib.Lib
+
+fun main() {
+  InterfaceWithCompanion.greet("Hello")
+  Lib.staticFun { true }
+  Lib.staticProp = "Foo"
+  println(Lib.staticProp)
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/jvmstatic_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/jvmstatic_lib/lib.kt
new file mode 100644
index 0000000..cc0f1fb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/jvmstatic_lib/lib.kt
@@ -0,0 +1,25 @@
+// 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.kotlin.metadata.jvmstatic_lib
+
+interface InterfaceWithCompanion {
+  companion object {
+    @JvmStatic fun greet(username: String) {
+      println("Hello, $username")
+    }
+  }
+}
+
+object Lib {
+
+  @JvmStatic
+  fun staticFun(func : () -> Boolean) {
+    println("Calling func...")
+    func()
+  }
+
+  @JvmStatic
+  var staticProp : String = ""
+}
\ No newline at end of file