Add reproduction of removed fields in kotlin.Metadata
Bug: 161230424
Change-Id: I666ca2f39f230c82e53608f2af7bb45b4773bec3
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java
new file mode 100644
index 0000000..41a60a4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java
@@ -0,0 +1,103 @@
+// 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.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.kotlin.metadata.metadata_pruned_fields.Main;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+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;
+
+/** This is a reproduction of b/161230424. */
+@RunWith(Parameterized.class)
+public class MetadataPrunedFieldsTest extends KotlinMetadataTestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0} target: {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), KotlinTargetVersion.values());
+ }
+
+ public MetadataPrunedFieldsTest(TestParameters parameters, KotlinTargetVersion targetVersion) {
+ super(targetVersion);
+ this.parameters = parameters;
+ }
+
+ private static Map<KotlinTargetVersion, Path> libJars = new HashMap<>();
+
+ @BeforeClass
+ public static void createLibJar() throws Exception {
+ String baseLibFolder = PKG_PREFIX + "/metadata_pruned_fields";
+ for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
+ Path baseLibJar =
+ kotlinc(KOTLINC, targetVersion)
+ .addSourceFiles(getKotlinFileInTest(baseLibFolder, "Methods"))
+ .compile();
+ libJars.put(targetVersion, baseLibJar);
+ }
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ final R8TestRunResult runResult =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(ToolHelper.getKotlinStdlibJar())
+ .addProgramFiles(libJars.get(targetVersion))
+ .addProgramClassFileData(Main.dump())
+ .addKeepRules("-keep class " + PKG + ".metadata_pruned_fields.MethodsKt { *; }")
+ .addKeepRules("-keep class kotlin.Metadata { *** pn(); }")
+ .addKeepMainRule(Main.class)
+ .allowDiagnosticWarningMessages()
+ .setMinApi(parameters.getApiLevel())
+ .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .compile()
+ .inspect(
+ codeInspector -> {
+ final ClassSubject clazz = codeInspector.clazz("kotlin.Metadata");
+ assertThat(clazz, isPresent());
+ assertThat(clazz.uniqueMethodWithName("pn"), isPresent());
+ assertThat(clazz.uniqueMethodWithName("d1"), not(isPresent()));
+ assertThat(clazz.uniqueMethodWithName("d2"), not(isPresent()));
+ assertThat(clazz.uniqueMethodWithName("bv"), not(isPresent()));
+ })
+ .assertAllWarningMessagesMatch(
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .run(parameters.getRuntime(), Main.class);
+ if (parameters.getRuntime().isCf()) {
+ runResult.assertSuccessWithOutputLines("", "Hello World!");
+ return;
+ }
+ final DexVm vm = parameters.getRuntime().asDex().getVm();
+ if (vm.isOlderThanOrEqual(DexVm.ART_4_4_4_HOST)) {
+ runResult.assertFailureWithErrorThatMatches(
+ containsString("java.lang.RuntimeException: failure in processEncodedAnnotation"));
+ } else if (vm.isOlderThanOrEqual(DexVm.ART_6_0_1_HOST)) {
+ runResult.assertFailureWithErrorThatMatches(
+ containsString("java.lang.IncompatibleClassChangeError: Couldn't find kotlin.Metadata."));
+ } else {
+ runResult.assertFailureWithErrorThatMatches(containsString("java.lang.NullPointerException"));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/metadata_pruned_fields/Main.java b/src/test/java/com/android/tools/r8/kotlin/metadata/metadata_pruned_fields/Main.java
new file mode 100644
index 0000000..10ebf6b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/metadata_pruned_fields/Main.java
@@ -0,0 +1,81 @@
+// 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.metadata_pruned_fields;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+public class Main implements Opcodes {
+
+ // The dump is generated from the code below, which cannot compile because of a missing
+ // reference to MethodsKt.
+ //
+ // public static void main(String[] args) {
+ // final kotlin.Metadata annotation = MethodsKt.class.getAnnotation(kotlin.Metadata.class);
+ // System.out.println(annotation.pn());
+ // MethodsKt.staticMethod();
+ // }
+
+ 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/metadata_pruned_fields/Main",
+ null,
+ "java/lang/Object",
+ new String[] {});
+
+ {
+ 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(
+ Type.getType("Lcom/android/tools/r8/kotlin/metadata/metadata_pruned_fields/MethodsKt;"));
+ methodVisitor.visitLdcInsn(Type.getType("Lkotlin/Metadata;"));
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL,
+ "java/lang/Class",
+ "getAnnotation",
+ "(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;",
+ false);
+ methodVisitor.visitTypeInsn(CHECKCAST, "kotlin/Metadata");
+ methodVisitor.visitVarInsn(ASTORE, 1);
+ methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+ methodVisitor.visitVarInsn(ALOAD, 1);
+ methodVisitor.visitMethodInsn(
+ INVOKEINTERFACE, "kotlin/Metadata", "pn", "()Ljava/lang/String;", true);
+ methodVisitor.visitMethodInsn(
+ INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+ methodVisitor.visitMethodInsn(
+ INVOKESTATIC,
+ "com/android/tools/r8/kotlin/metadata/metadata_pruned_fields/MethodsKt",
+ "staticMethod",
+ "()V",
+ false);
+ methodVisitor.visitInsn(RETURN);
+ methodVisitor.visitMaxs(2, 2);
+ methodVisitor.visitEnd();
+ }
+ classWriter.visitEnd();
+
+ return classWriter.toByteArray();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/metadata_pruned_fields/Methods.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/metadata_pruned_fields/Methods.kt
new file mode 100644
index 0000000..5f67164
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/metadata_pruned_fields/Methods.kt
@@ -0,0 +1,9 @@
+// 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.metadata_pruned_fields
+
+fun staticMethod() {
+ println("Hello World!")
+}
\ No newline at end of file