[Metadata] Add tests for kotlin value classes
Bug: 187781614
Change-Id: I893888e2ca2bcbdcc7874ede8ff149aad6d5ed7d
diff --git a/build.gradle b/build.gradle
index 01d41fc..c38b5a0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -273,7 +273,8 @@
apiUsageSampleCompile "com.google.guava:guava:$guavaVersion"
kotlinR8TestResourcesCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
errorprone("com.google.errorprone:error_prone_core:$errorproneVersion")
- testImplementation "org.jetbrains.kotlin:kotlin-reflect:1.3.31"
+ testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
+ testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
}
def r8LibPath = "$buildDir/libs/r8lib.jar"
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
index e59e318..71b041c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
@@ -32,11 +32,7 @@
private static final KotlinCompileMemoizer libJars =
getCompileMemoizer(
- getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib"))
- .configure(
- kotlinCompilerTool -> {
- kotlinCompilerTool.addClasspathFiles(ToolHelper.getClassPathForTests());
- });
+ getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib"));
private final TestParameters parameters;
@Parameterized.Parameters(name = "{0}, {1}")
@@ -74,13 +70,14 @@
Path libJar =
testForR8(parameters.getBackend())
.addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
- .enableInliningAnnotations()
.addClasspathFiles(
ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinAnnotationJar(kotlinc))
.addKeepRules(
"-keep class " + PKG_LIB + ".Sub { <init>(); *** kept(); *** keptProperty; }")
+ .addKeepRules("-neverinline class * { @" + PKG_LIB + ".NeverInline *; }")
.addKeepClassAndMembersRules(PKG_LIB + ".SubUser")
.addKeepRuntimeVisibleAnnotations()
+ .enableProguardTestOptions()
.noMinification()
.compile()
.inspect(this::checkPruned)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java
new file mode 100644
index 0000000..e5c7a51
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteValueClassTest.java
@@ -0,0 +1,139 @@
+// 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.ToolHelper.getKotlinAnnotationJar;
+import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertNull;
+
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.kotlin.KotlinMetadataWriter;
+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.FoundClassSubject;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import junit.framework.TestCase;
+import kotlinx.metadata.jvm.KotlinClassHeader;
+import kotlinx.metadata.jvm.KotlinClassMetadata;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MetadataRewriteValueClassTest extends KotlinMetadataTestBase {
+
+ private static final String EXPECTED = StringUtils.lines("Hello, John Doe", "42", "UInt(x=42)");
+ private static final String PKG_LIB = PKG + ".value_class_lib";
+ private static final String PKG_APP = PKG + ".value_class_app";
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}, {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withCfRuntimes().build(),
+ // TODO(b/193765243): This should be run for all later compilers.
+ getKotlinTestParameters()
+ .withCompiler(KotlinCompilerVersion.KOTLINC_1_5_0)
+ .withTargetVersion(KotlinTargetVersion.JAVA_8)
+ .build());
+ }
+
+ public MetadataRewriteValueClassTest(
+ TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
+ this.parameters = parameters;
+ }
+
+ private static final KotlinCompileMemoizer kotlincLibJar =
+ getCompileMemoizer(
+ getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib"));
+
+ @Test
+ public void smokeTest() throws Exception {
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+ .addClasspathFiles(kotlincLibJar.getForConfiguration(kotlinc, targetVersion))
+ .addSourceFiles(
+ getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+ testForJvm()
+ .addRunClasspathFiles(
+ getKotlinStdlibJar(kotlinc), kotlincLibJar.getForConfiguration(kotlinc, targetVersion))
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG_APP + ".MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testMetadataForLib() throws Exception {
+ Path libJar =
+ testForR8(parameters.getBackend())
+ .addClasspathFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+ .addProgramFiles(kotlincLibJar.getForConfiguration(kotlinc, targetVersion))
+ .addKeepAllClassesRule()
+ .addKeepAttributes(
+ ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS,
+ ProguardKeepAttributes.SIGNATURE,
+ ProguardKeepAttributes.INNER_CLASSES,
+ ProguardKeepAttributes.ENCLOSING_METHOD)
+ .addOptionsModification(
+ internalOptions -> {
+ internalOptions.testing.keepMetadataInR8IfNotRewritten = false;
+ })
+ .compile()
+ .inspect(this::inspect)
+ .writeToZip();
+ Path main =
+ kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(
+ getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+ testForJvm()
+ .addRunClasspathFiles(
+ getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar)
+ .addClasspath(main)
+ .run(parameters.getRuntime(), PKG_APP + ".MainKt")
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ private void inspect(CodeInspector inspector) throws IOException {
+ CodeInspector stdLibInspector =
+ new CodeInspector(kotlincLibJar.getForConfiguration(kotlinc, targetVersion));
+ for (FoundClassSubject clazzSubject : stdLibInspector.allClasses()) {
+ ClassSubject r8Clazz = inspector.clazz(clazzSubject.getOriginalName());
+ assertThat(r8Clazz, isPresent());
+ KotlinClassMetadata originalMetadata = clazzSubject.getKotlinClassMetadata();
+ KotlinClassMetadata rewrittenMetadata = r8Clazz.getKotlinClassMetadata();
+ if (originalMetadata == null) {
+ assertNull(rewrittenMetadata);
+ continue;
+ }
+ TestCase.assertNotNull(rewrittenMetadata);
+ KotlinClassHeader originalHeader = originalMetadata.getHeader();
+ KotlinClassHeader rewrittenHeader = rewrittenMetadata.getHeader();
+ TestCase.assertEquals(originalHeader.getKind(), rewrittenHeader.getKind());
+ TestCase.assertEquals(originalHeader.getPackageName(), rewrittenHeader.getPackageName());
+ // We cannot assert equality of the data since it may be ordered differently. Instead we use
+ // the KotlinMetadataWriter.
+ String expected = KotlinMetadataWriter.kotlinMetadataToString("", originalMetadata);
+ String actual = KotlinMetadataWriter.kotlinMetadataToString("", rewrittenMetadata);
+ TestCase.assertEquals(expected, actual);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/pruned_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/pruned_lib/lib.kt
index a7672fd..7cb8107 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/pruned_lib/lib.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/pruned_lib/lib.kt
@@ -4,7 +4,7 @@
package com.android.tools.r8.kotlin.metadata.pruned_lib
-import com.android.tools.r8.NeverInline
+annotation class NeverInline
// The Base class will be removed during compilation
open class Base
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/value_class_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/value_class_app/main.kt
new file mode 100644
index 0000000..476c6e1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/value_class_app/main.kt
@@ -0,0 +1,13 @@
+// Copyright (c) 2021, 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.value_class_app
+
+import com.android.tools.r8.kotlin.metadata.value_class_lib.*
+
+fun main() {
+ Name("John Doe").prettyPrint()
+ compute(42)
+ compute(UInt(42))
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/value_class_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/value_class_lib/lib.kt
new file mode 100644
index 0000000..0c4d392
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/value_class_lib/lib.kt
@@ -0,0 +1,39 @@
+// 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.value_class_lib
+
+// These examples are from the kotlinlang.org website.
+
+interface Printable {
+ fun prettyPrint()
+}
+
+@JvmInline
+value class Name(val s: String) : Printable {
+
+ init {
+ require(s.length > 0) { }
+ }
+
+ val length: Int
+ get() = s.length
+
+ override fun prettyPrint() {
+ println("Hello, $s")
+ }
+}
+
+@JvmInline
+value class UInt(val x: Int)
+
+// Represented as 'public final void compute(int x)' on the JVM
+fun compute(x: Int) {
+ println(x);
+}
+
+// Also represented as 'public final void compute(int x)' on the JVM thus name is mangled!
+fun compute(x: UInt) {
+ println(x);
+}