Add test for localDelegatedProperties in R8

Bug: 157988734
Change-Id: Ia1ceedbad10c313cfeedf87b1d672977ef058ab5
diff --git a/build.gradle b/build.gradle
index 919888b..6050eda 100644
--- a/build.gradle
+++ b/build.gradle
@@ -279,6 +279,7 @@
     examplesKotlinCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
     kotlinR8TestResourcesCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
     errorprone("com.google.errorprone:error_prone_core:$errorproneVersion")
+    testImplementation "org.jetbrains.kotlin:kotlin-reflect:1.3.31"
 }
 
 def r8LibPath = "$buildDir/libs/r8lib.jar"
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java
index b96a52c..ce26b77 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java
@@ -76,7 +76,6 @@
     Path libJar =
         testForR8(parameters.getBackend())
             .addProgramFiles(libJars.get(targetVersion))
-            // Allow renaming A to ensure that we rename in the flexible upper bound type.
             .addKeepAllClassesRule()
             .addKeepAllAttributes()
             .compile()
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
new file mode 100644
index 0000000..50007fb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
@@ -0,0 +1,174 @@
+// 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 org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteDelegatedPropertyTest extends KotlinMetadataTestBase {
+
+  private static final String PKG_LIB = PKG + ".delegated_property_lib";
+  private static final String PKG_APP = PKG + ".delegated_property_app";
+  private static final String EXPECTED_MAIN =
+      StringUtils.lines(
+          "foo has been assigned to 'customDelegate' in"
+              + " com.android.tools.r8.kotlin.metadata.delegated_property_lib.Delegates",
+          "foo has been read in CustomDelegate from 'customDelegate' in"
+              + " com.android.tools.r8.kotlin.metadata.delegated_property_lib.Delegates",
+          "foo",
+          "read-only has been read in CustomReadOnlyDelegate from 'customReadOnlyDelegate' in"
+              + " com.android.tools.r8.kotlin.metadata.delegated_property_lib.Delegates",
+          "read-only",
+          "Generating lazy string",
+          "42",
+          "Hello World!",
+          "Hello World!",
+          "Jane Doe",
+          "42",
+          "Checking property for image",
+          "Checking property for text",
+          "image_id",
+          "text_id");
+  private static final String EXPECTED_REFLECT =
+      StringUtils.lines(
+          "foo has been assigned to 'customDelegate' in"
+              + " com.android.tools.r8.kotlin.metadata.delegated_property_lib.Delegates",
+          "foo");
+
+  @Parameterized.Parameters(name = "{0} target: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values());
+  }
+
+  public MetadataRewriteDelegatedPropertyTest(
+      TestParameters parameters, KotlinTargetVersion targetVersion) {
+    super(targetVersion);
+    this.parameters = parameters;
+  }
+
+  private final TestParameters parameters;
+  private static Map<KotlinTargetVersion, Path> libJars = new HashMap<>();
+
+  @BeforeClass
+  public static void createLibJar() throws Exception {
+    for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+      Path baseLibJar =
+          kotlinc(KOTLINC, targetVersion)
+              .addSourceFiles(
+                  getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib"))
+              .compile();
+      libJars.put(targetVersion, baseLibJar);
+    }
+  }
+
+  @Test
+  public void smokeTest() throws Exception {
+    Path libJar = libJars.get(targetVersion);
+    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_MAIN);
+  }
+
+  @Test
+  public void smokeTestReflect() throws Exception {
+    Path libJar = libJars.get(targetVersion);
+    Path output =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(
+                getKotlinFileInTest(
+                    DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main_reflect"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compile();
+    testForJvm()
+        .addRunClasspathFiles(
+            ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar)
+        .addClasspath(output)
+        .run(parameters.getRuntime(), PKG_APP + ".Main_reflectKt")
+        .assertSuccessWithOutput(EXPECTED_REFLECT);
+  }
+
+  @Test
+  public void testMetadataForLib() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addClasspathFiles(ToolHelper.getKotlinStdlibJar())
+            .addProgramFiles(libJars.get(targetVersion))
+            .addKeepRules("-keep class " + PKG_LIB + ".Delegates { *; }")
+            .addKeepRules("-keep class " + PKG_LIB + ".Resource { *; }")
+            .addKeepRules("-keep class " + PKG_LIB + ".User { *; }")
+            .addKeepRules("-keep class " + PKG_LIB + ".ProvidedDelegates { *; }")
+            .compile()
+            // TODO(b/157988734): When we start modeling localDelegatedProperties, inspect the code.
+            .writeToZip();
+    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_MAIN);
+  }
+
+  @Test
+  public void testMetadataForReflect() throws Exception {
+    Path libJar =
+        testForR8(parameters.getBackend())
+            .addClasspathFiles(ToolHelper.getKotlinStdlibJar())
+            .addProgramFiles(libJars.get(targetVersion))
+            .addKeepRules("-keep class " + PKG_LIB + ".Delegates { *; }")
+            .addKeepRules("-keep class " + PKG_LIB + ".Resource { *; }")
+            .addKeepRules("-keep class " + PKG_LIB + ".CustomDelegate { *; }")
+            .compile()
+            .writeToZip();
+    ProcessResult result =
+        kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion)
+            .addClasspathFiles(libJar)
+            .addSourceFiles(
+                getKotlinFileInTest(
+                    DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main_reflect"))
+            .setOutputPath(temp.newFolder().toPath())
+            .compileRaw();
+    assertEquals(1, result.exitCode);
+    assertThat(
+        result.stderr,
+        containsString(
+            "unsupported [reference to the synthetic extension property for a Java get/set"
+                + " method]"));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/delegated_property_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/delegated_property_app/main.kt
new file mode 100644
index 0000000..7bfd637
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/delegated_property_app/main.kt
@@ -0,0 +1,31 @@
+// 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.delegated_property_app
+
+import com.android.tools.r8.kotlin.metadata.delegated_property_lib.Delegates
+import com.android.tools.r8.kotlin.metadata.delegated_property_lib.ProvidedDelegates
+import com.android.tools.r8.kotlin.metadata.delegated_property_lib.Resource
+import com.android.tools.r8.kotlin.metadata.delegated_property_lib.User
+
+fun main() {
+
+  val delegates = Delegates()
+  delegates.customDelegate = Resource("foo");
+  println(delegates.customDelegate)
+  println(delegates.customReadOnlyDelegate)
+  println(delegates.lazyString)
+  println(delegates.localDelegatedProperties { Resource("Hello World!") })
+
+  val user = User(mapOf(
+    "name" to "Jane Doe",
+    "age"  to 42
+  ))
+
+  println(user.name)
+  println(user.age)
+
+  val providedDelegates = ProvidedDelegates()
+  println(providedDelegates.image)
+  println(providedDelegates.text)
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/delegated_property_app/main_reflect.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/delegated_property_app/main_reflect.kt
new file mode 100644
index 0000000..0369a86
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/delegated_property_app/main_reflect.kt
@@ -0,0 +1,21 @@
+// 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.delegated_property_app
+
+import com.android.tools.r8.kotlin.metadata.delegated_property_lib.CustomDelegate
+import com.android.tools.r8.kotlin.metadata.delegated_property_lib.Delegates
+import com.android.tools.r8.kotlin.metadata.delegated_property_lib.Resource
+import kotlin.reflect.KMutableProperty0
+import kotlin.reflect.jvm.isAccessible
+
+fun main() {
+  val delegates = Delegates()
+  delegates.customDelegate = Resource("foo");
+  println(delegates::customDelegate.getResource())
+}
+
+inline fun KMutableProperty0<*>.getResource(): Resource {
+  isAccessible = true
+  return (getDelegate() as CustomDelegate).resource
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/delegated_property_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/delegated_property_lib/lib.kt
new file mode 100644
index 0000000..54c58df
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/delegated_property_lib/lib.kt
@@ -0,0 +1,88 @@
+// 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.delegated_property_lib
+
+import kotlin.properties.ReadOnlyProperty
+import kotlin.reflect.KProperty
+
+class Resource(private var s : String = "") {
+
+  override fun toString(): String {
+    return s;
+  }
+}
+
+class CustomDelegate(var resource: Resource = Resource()) {
+
+  operator fun getValue(thisRef: Any?, property: KProperty<*>): Resource {
+    println("$resource has been read in CustomDelegate from '" +
+            "${property.name}' in ${thisRef?.javaClass?.typeName}")
+    return resource;
+  }
+
+  operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Resource) {
+    println("$value has been assigned to '${property.name}'" +
+            " in ${thisRef?.javaClass?.typeName}")
+    this.resource = value
+  }
+}
+
+class CustomReadOnlyDelegate(private var resource : Resource = Resource("read-only"))
+  : ReadOnlyProperty<Any?, Resource> {
+  override fun getValue(thisRef: Any?, property: KProperty<*>): Resource {
+    println("$resource has been read in CustomReadOnlyDelegate" +
+            " from '${property.name}' in ${thisRef?.javaClass?.typeName}")
+    return resource;
+  }
+}
+
+class Delegates {
+
+  var customDelegate : Resource by CustomDelegate()
+  val customReadOnlyDelegate : Resource by CustomReadOnlyDelegate()
+  val lazyString : String by lazy {
+    println("Generating lazy string")
+    "42"
+  }
+
+  fun localDelegatedProperties(compute: () -> Resource) : Resource {
+    val foo by lazy(compute)
+    println(foo)
+    return foo
+  }
+}
+class User(val map : Map<String, Any?>) {
+  val name : String by map
+  val age: Int by map
+}
+
+class ResourceDelegate(val r : Resource): ReadOnlyProperty<ProvidedDelegates, Resource> {
+  override fun getValue(thisRef: ProvidedDelegates, property: KProperty<*>): Resource {
+    return r
+  }
+}
+
+class ResourceLoader(val id: Resource) {
+  operator fun provideDelegate(
+    thisRef: ProvidedDelegates,
+    prop: KProperty<*>
+  ): ReadOnlyProperty<ProvidedDelegates, Resource> {
+    checkProperty(prop.name)
+    return ResourceDelegate(id)
+  }
+
+  private fun checkProperty(name: String) {
+    println("Checking property for " + name)
+  }
+}
+
+class ProvidedDelegates {
+  fun bindResource(id: String): ResourceLoader {
+    return ResourceLoader(Resource(id))
+  }
+
+  val image by bindResource("image_id")
+  val text by bindResource("text_id")
+}