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)
+  }
 }
+