Model kotlin annotation arguments
Bug: 158124557
Bug: 155053894
Change-Id: I7d57209f149e891c0f2c68494c44b6381ab2798f
diff --git a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
index 07f6b0b..b16b8b6 100644
--- a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
+++ b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
@@ -43,6 +43,7 @@
public static final String arrayBinaryName = NAME + "/Array";
public static final String anyDescriptor = "L" + NAME + "/Any;";
+ public static final String anyName = NAME + "/Any";
}
// Mappings from JVM types to Kotlin types (of type DexType)
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java
new file mode 100644
index 0000000..6a9b23d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationArgumentInfo.java
@@ -0,0 +1,210 @@
+// 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;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.kotlin.Kotlin.ClassClassifiers;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
+import com.android.tools.r8.utils.Box;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import kotlinx.metadata.KmAnnotation;
+import kotlinx.metadata.KmAnnotationArgument;
+import kotlinx.metadata.KmAnnotationArgument.AnnotationValue;
+import kotlinx.metadata.KmAnnotationArgument.ArrayValue;
+import kotlinx.metadata.KmAnnotationArgument.EnumValue;
+import kotlinx.metadata.KmAnnotationArgument.KClassValue;
+
+abstract class KotlinAnnotationArgumentInfo implements EnqueuerMetadataTraceable {
+
+ private static final Map<String, KotlinAnnotationArgumentInfo> EMPTY_ARGUMENTS =
+ ImmutableMap.of();
+
+ abstract KmAnnotationArgument<?> rewrite(
+ AppView<AppInfoWithLiveness> appView, NamingLens namingLens);
+
+ private static KotlinAnnotationArgumentInfo createArgument(
+ KmAnnotationArgument<?> arg, DexItemFactory factory) {
+ if (arg instanceof KClassValue) {
+ return KotlinAnnotationClassValueInfo.create((KClassValue) arg, factory);
+ } else if (arg instanceof EnumValue) {
+ return KotlinAnnotationEnumValueInfo.create((EnumValue) arg, factory);
+ } else if (arg instanceof AnnotationValue) {
+ return KotlinAnnotationAnnotationValueInfo.create((AnnotationValue) arg, factory);
+ } else if (arg instanceof ArrayValue) {
+ return KotlinAnnotationArrayValueInfo.create((ArrayValue) arg, factory);
+ } else {
+ return KotlinAnnotationPrimitiveArgumentInfo.create(arg);
+ }
+ }
+
+ static Map<String, KotlinAnnotationArgumentInfo> create(
+ Map<String, KmAnnotationArgument<?>> arguments, DexItemFactory factory) {
+ if (arguments.isEmpty()) {
+ return EMPTY_ARGUMENTS;
+ }
+ LinkedHashMap<String, KotlinAnnotationArgumentInfo> modeled = new LinkedHashMap<>();
+ arguments.forEach((key, arg) -> modeled.put(key, createArgument(arg, factory)));
+ return modeled;
+ }
+
+ private static class KotlinAnnotationClassValueInfo extends KotlinAnnotationArgumentInfo {
+
+ private final KotlinTypeReference value;
+
+ private KotlinAnnotationClassValueInfo(KotlinTypeReference value) {
+ this.value = value;
+ }
+
+ private static KotlinAnnotationClassValueInfo create(KClassValue arg, DexItemFactory factory) {
+ return new KotlinAnnotationClassValueInfo(
+ KotlinTypeReference.fromBinaryName(arg.getValue(), factory));
+ }
+
+ @Override
+ public void trace(DexDefinitionSupplier definitionSupplier) {
+ value.trace(definitionSupplier);
+ }
+
+ @Override
+ KmAnnotationArgument<?> rewrite(AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ return new KClassValue(
+ value.toRenamedBinaryNameOrDefault(appView, namingLens, ClassClassifiers.anyName));
+ }
+ }
+
+ private static class KotlinAnnotationEnumValueInfo extends KotlinAnnotationArgumentInfo {
+
+ private final KotlinTypeReference enumClassName;
+ private final String enumEntryName;
+
+ private KotlinAnnotationEnumValueInfo(KotlinTypeReference enumClassName, String enumEntryName) {
+ this.enumClassName = enumClassName;
+ this.enumEntryName = enumEntryName;
+ }
+
+ private static KotlinAnnotationEnumValueInfo create(EnumValue arg, DexItemFactory factory) {
+ return new KotlinAnnotationEnumValueInfo(
+ KotlinTypeReference.fromBinaryName(arg.getEnumClassName(), factory),
+ arg.getEnumEntryName());
+ }
+
+ @Override
+ public void trace(DexDefinitionSupplier definitionSupplier) {
+ enumClassName.trace(definitionSupplier);
+ }
+
+ @Override
+ KmAnnotationArgument<?> rewrite(AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ return new EnumValue(
+ enumClassName.toRenamedBinaryNameOrDefault(appView, namingLens, ClassClassifiers.anyName),
+ enumEntryName);
+ }
+ }
+
+ private static class KotlinAnnotationAnnotationValueInfo extends KotlinAnnotationArgumentInfo {
+
+ private final KotlinAnnotationInfo value;
+
+ private KotlinAnnotationAnnotationValueInfo(KotlinAnnotationInfo value) {
+ this.value = value;
+ }
+
+ private static KotlinAnnotationAnnotationValueInfo create(
+ AnnotationValue arg, DexItemFactory factory) {
+ return new KotlinAnnotationAnnotationValueInfo(
+ KotlinAnnotationInfo.create(arg.getValue(), factory));
+ }
+
+ @Override
+ public void trace(DexDefinitionSupplier definitionSupplier) {
+ value.trace(definitionSupplier);
+ }
+
+ @Override
+ KmAnnotationArgument<?> rewrite(AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ Box<KmAnnotation> rewrittenAnnotation = new Box<>();
+ value.rewrite(rewrittenAnnotation::set, appView, namingLens);
+ if (rewrittenAnnotation.isSet()) {
+ return new AnnotationValue(rewrittenAnnotation.get());
+ }
+ return null;
+ }
+ }
+
+ private static class KotlinAnnotationArrayValueInfo extends KotlinAnnotationArgumentInfo {
+
+ private static final KotlinAnnotationArrayValueInfo EMPTY =
+ new KotlinAnnotationArrayValueInfo(ImmutableList.of());
+
+ private final List<KotlinAnnotationArgumentInfo> value;
+
+ private KotlinAnnotationArrayValueInfo(List<KotlinAnnotationArgumentInfo> value) {
+ this.value = value;
+ }
+
+ private static KotlinAnnotationArrayValueInfo create(ArrayValue arg, DexItemFactory factory) {
+ if (arg.getValue().isEmpty()) {
+ return EMPTY;
+ }
+ ImmutableList.Builder<KotlinAnnotationArgumentInfo> builder = ImmutableList.builder();
+ for (KmAnnotationArgument<?> argument : arg.getValue()) {
+ builder.add(createArgument(argument, factory));
+ }
+ return new KotlinAnnotationArrayValueInfo(builder.build());
+ }
+
+ @Override
+ public void trace(DexDefinitionSupplier definitionSupplier) {
+ for (KotlinAnnotationArgumentInfo kotlinAnnotationArgumentInfo : value) {
+ kotlinAnnotationArgumentInfo.trace(definitionSupplier);
+ }
+ }
+
+ @Override
+ KmAnnotationArgument<?> rewrite(AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ List<KmAnnotationArgument<?>> rewrittenArguments = new ArrayList<>();
+ for (KotlinAnnotationArgumentInfo kotlinAnnotationArgumentInfo : value) {
+ KmAnnotationArgument<?> rewrittenArg =
+ kotlinAnnotationArgumentInfo.rewrite(appView, namingLens);
+ if (rewrittenArg != null) {
+ rewrittenArguments.add(rewrittenArg);
+ }
+ }
+ return new ArrayValue(rewrittenArguments);
+ }
+ }
+
+ private static class KotlinAnnotationPrimitiveArgumentInfo extends KotlinAnnotationArgumentInfo {
+
+ private final KmAnnotationArgument<?> argument;
+
+ private KotlinAnnotationPrimitiveArgumentInfo(KmAnnotationArgument<?> argument) {
+ this.argument = argument;
+ }
+
+ private static KotlinAnnotationPrimitiveArgumentInfo create(KmAnnotationArgument<?> argument) {
+ return new KotlinAnnotationPrimitiveArgumentInfo(argument);
+ }
+
+ @Override
+ public void trace(DexDefinitionSupplier definitionSupplier) {
+ // Nothing to trace
+ }
+
+ @Override
+ KmAnnotationArgument<?> rewrite(AppView<AppInfoWithLiveness> appView, NamingLens namingLens) {
+ return argument;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
index 90ed44a..fd4d087 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinAnnotationInfo.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.shaking.EnqueuerMetadataTraceable;
import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.ImmutableList;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import kotlinx.metadata.KmAnnotation;
@@ -23,19 +24,18 @@
private static final List<KotlinAnnotationInfo> EMPTY_ANNOTATIONS = ImmutableList.of();
private final KotlinTypeReference annotationType;
- // TODO(b/155053894): Model KmAnnotationArgument.
- private final Map<String, KmAnnotationArgument<?>> arguments;
+ private final Map<String, KotlinAnnotationArgumentInfo> arguments;
private KotlinAnnotationInfo(
- KotlinTypeReference annotationType, Map<String, KmAnnotationArgument<?>> arguments) {
+ KotlinTypeReference annotationType, Map<String, KotlinAnnotationArgumentInfo> arguments) {
this.annotationType = annotationType;
this.arguments = arguments;
}
- private static KotlinAnnotationInfo create(KmAnnotation annotation, DexItemFactory factory) {
+ static KotlinAnnotationInfo create(KmAnnotation annotation, DexItemFactory factory) {
return new KotlinAnnotationInfo(
KotlinTypeReference.fromBinaryName(annotation.getClassName(), factory),
- annotation.getArguments());
+ KotlinAnnotationArgumentInfo.create(annotation.getArguments(), factory));
}
static List<KotlinAnnotationInfo> create(List<KmAnnotation> annotations, DexItemFactory factory) {
@@ -60,13 +60,20 @@
return;
}
String classifier = DescriptorUtils.descriptorToKotlinClassifier(renamedDescriptor);
- KmAnnotation annotation = new KmAnnotation(classifier, arguments);
- visitorProvider.get(annotation);
+ Map<String, KmAnnotationArgument<?>> rewrittenArguments = new LinkedHashMap<>();
+ arguments.forEach(
+ (key, arg) -> {
+ KmAnnotationArgument<?> rewrittenArg = arg.rewrite(appView, namingLens);
+ if (rewrittenArg != null) {
+ rewrittenArguments.put(key, rewrittenArg);
+ }
+ });
+ visitorProvider.get(new KmAnnotation(classifier, rewrittenArguments));
}
@Override
public void trace(DexDefinitionSupplier definitionSupplier) {
annotationType.trace(definitionSupplier);
- // TODO(b/155053894): Trace annotation arguments.
+ arguments.forEach((ignored, arg) -> arg.trace(definitionSupplier));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
index 6fae341..11c47de 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
@@ -8,7 +8,6 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isNotRenamed;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -36,7 +35,7 @@
@RunWith(Parameterized.class)
public class MetadataRewriteAnnotationTest extends KotlinMetadataTestBase {
- private final String EXPECTED_SMOKE =
+ private final String EXPECTED =
StringUtils.lines(
"class com.android.tools.r8.kotlin.metadata.annotation_lib.Foo",
"class com.android.tools.r8.kotlin.metadata.annotation_lib.Foo",
@@ -50,9 +49,14 @@
"class com.android.tools.r8.kotlin.metadata.annotation_lib.Foo",
"DOWN",
"class com.android.tools.r8.kotlin.metadata.annotation_lib.Foo",
- "UP");
+ "UP",
+ "Top most",
+ "class com.android.tools.r8.kotlin.metadata.annotation_lib.Foo",
+ "DOWN",
+ "com.android.tools.r8.kotlin.metadata.annotation_lib.Foo");
private static final String PKG_LIB = PKG + ".annotation_lib";
private static final String PKG_APP = PKG + ".annotation_app";
+ private static final String FOO_ORIGINAL_NAME = PKG_LIB + ".Foo";
private static final String FOO_FINAL_NAME = "a.b.c";
@Parameterized.Parameters(name = "{0} target: {1}")
@@ -97,21 +101,23 @@
ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar)
.addClasspath(output)
.run(parameters.getRuntime(), PKG_APP + ".MainKt")
- .assertSuccessWithOutput(EXPECTED_SMOKE);
+ .assertSuccessWithOutput(EXPECTED);
}
@Test
public void testMetadataForLib() throws Exception {
- String fooTypeName = PKG_LIB + ".Foo";
Path libJar =
testForR8(parameters.getBackend())
.addProgramFiles(libJars.get(targetVersion))
/// Keep the annotations
.addKeepClassAndMembersRules(PKG_LIB + ".AnnoWithClassAndEnum")
.addKeepClassAndMembersRules(PKG_LIB + ".AnnoWithClassArr")
+ .addKeepRules("-keep class " + PKG_LIB + ".Nested { *** kept(); } ")
+ .addKeepRules("-keep class " + PKG_LIB + ".Nested { *** message(); } ")
+ // .addKeepRules("-keep class " + PKG_LIB + ".Nested { *** kept; *** getKept(); } ")
// Keep Foo but rename to test arguments
- .addKeepClassAndMembersRulesWithAllowObfuscation(fooTypeName)
- .addApplyMapping(fooTypeName + " -> " + FOO_FINAL_NAME + ":")
+ .addKeepClassAndMembersRulesWithAllowObfuscation(FOO_ORIGINAL_NAME)
+ .addApplyMapping(FOO_ORIGINAL_NAME + " -> " + FOO_FINAL_NAME + ":")
// Keep Direction but rename the enum
.addKeepClassAndMembersRules(PKG_LIB + ".Direction")
// Keep Bar and Baz and Quux because we are directly reflecting on them
@@ -120,7 +126,6 @@
.addKeepClassAndMembersRules(PKG_LIB + ".Quux")
// Keep the static class for the type alias
.addKeepClassAndMembersRules(PKG_LIB + ".LibKt")
- .addKeepRuntimeVisibleAnnotations()
.addKeepAttributes(
ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS,
ProguardKeepAttributes.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS,
@@ -139,8 +144,7 @@
ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar)
.addProgramFiles(output)
.run(parameters.getRuntime(), PKG_APP + ".MainKt")
- // TODO(b/155053894): Fails because reflect cannot find the class.
- .assertFailureWithErrorThatMatches(containsString("KotlinReflectionInternalError"));
+ .assertSuccessWithOutput(EXPECTED.replace(FOO_ORIGINAL_NAME, FOO_FINAL_NAME));
}
private void inspect(CodeInspector inspector) {
@@ -171,9 +175,8 @@
Map<String, KmAnnotationArgument<?>> arguments = annotation.getArguments();
assertEquals(1, arguments.size());
ArrayValue classes = (ArrayValue) arguments.get("classes");
- // TODO(b/155053894): This should be renamed.
assertEquals(
- "KClassValue(value=" + foo.getOriginalBinaryName() + ")",
+ "KClassValue(value=" + foo.getFinalBinaryName() + ")",
classes.getValue().get(0).toString());
assertEquals(
"KClassValue(value=" + bar.getFinalBinaryName() + ")",
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/annotation_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/annotation_app/main.kt
index 34d39b8..d7ed237 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/annotation_app/main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/annotation_app/main.kt
@@ -8,7 +8,9 @@
import com.android.tools.r8.kotlin.metadata.annotation_lib.AnnoWithClassArr
import com.android.tools.r8.kotlin.metadata.annotation_lib.Bar
import com.android.tools.r8.kotlin.metadata.annotation_lib.Baz
+import com.android.tools.r8.kotlin.metadata.annotation_lib.Nested
import com.android.tools.r8.kotlin.metadata.annotation_lib.Quux
+import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.primaryConstructor
import kotlin.reflect.full.valueParameters
@@ -23,6 +25,14 @@
// https://youtrack.jetbrains.com/issue/KT-21489
Quux::methodWithTypeAnnotations
.returnType.arguments.get(0).type?.annotations?.get(0)?.printAnnoWithClassAndEnum()
+ val nested = Quux::methodWithNestedAnnotations.returnType.arguments[0].type?.annotations?.get(0) as Nested
+ println(nested.message)
+ nested.kept.printAnnoWithClassAndEnum()
+ if (nested::class::memberProperties.get().any { it.name.equals("notKept") }) {
+ println("com.android.tools.r8.kotlin.metadata.annotation_lib.Foo")
+ } else {
+ println("a.b.c")
+ }
}
fun Annotation.printAnnoWithClassArr() {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/annotation_lib/lib.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/annotation_lib/lib.kt
index 38a574c..0c6ff6c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/annotation_lib/lib.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/annotation_lib/lib.kt
@@ -17,9 +17,10 @@
@Retention(AnnotationRetention.RUNTIME)
annotation class AnnoWithClassAndEnum(val clazz : KClass<*>, val direction : Direction)
-@Target(AnnotationTarget.CLASS, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.TYPEALIAS)
+@Target(AnnotationTarget.CLASS, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.TYPEALIAS,
+ AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
-annotation class AnnoWithClassArr(val classes : Array<KClass<*>>)
+annotation class AnnoWithClassArr(val classes: Array<KClass<*>>)
class Foo
@@ -38,10 +39,23 @@
@AnnoWithClassArr([Foo::class, Bar::class])
typealias Qux = Foo
+annotation class AnnoNotKept
+
+@Target(AnnotationTarget.TYPE)
+annotation class Nested(
+ val message: String,
+ val kept: AnnoWithClassAndEnum,
+ val notKept: AnnoNotKept
+)
class Quux {
fun methodWithTypeAnnotations() : Array<@AnnoWithClassAndEnum(Foo::class, Direction.UP) Int> {
return arrayOf(1)
}
+
+ fun methodWithNestedAnnotations() : Array<@Nested("Top most", AnnoWithClassAndEnum(Foo::class, Direction.DOWN), AnnoNotKept()) Int> {
+ return arrayOf(1)
+ }
}
+