Move kotlin metadata removal to annotation remover

Change-Id: Ic72a22405c87b50123bb4b549385c54417813f55
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 7095940..ada1c8a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -346,6 +346,7 @@
     this.genericSignature = genericSignature;
   }
 
+  @Override
   public void clearGenericSignature() {
     this.genericSignature = FieldTypeSignature.noSignature();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
index f596146..e1bb25e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
@@ -28,6 +28,8 @@
 
   public abstract void clearKotlinInfo();
 
+  public abstract void clearGenericSignature();
+
   public DexType getHolderType() {
     return getReference().getHolderType();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 9051374..434b091 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -650,10 +650,6 @@
     this.kotlinMemberInfo = kotlinMemberInfo;
   }
 
-  public void clearKotlinMemberInfo() {
-    kotlinMemberInfo = getNoKotlinInfo();
-  }
-
   public boolean isKotlinFunction() {
     return kotlinMemberInfo.isFunction();
   }
@@ -1504,6 +1500,7 @@
     this.genericSignature = genericSignature;
   }
 
+  @Override
   public void clearGenericSignature() {
     this.genericSignature = MethodTypeSignature.noSignature();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 315b8a1..6be5551 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -472,10 +472,14 @@
 
   public void setKotlinInfo(KotlinClassLevelInfo kotlinInfo) {
     assert kotlinInfo != null;
-    assert this.kotlinInfo == getNoKotlinInfo() || kotlinInfo == getNoKotlinInfo();
+    assert this.kotlinInfo == getNoKotlinInfo();
     this.kotlinInfo = kotlinInfo;
   }
 
+  public void clearKotlinInfo() {
+    this.kotlinInfo = getNoKotlinInfo();
+  }
+
   @Override
   boolean internalClassOrInterfaceMayHaveInitializationSideEffects(
       AppView<?> appView,
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index f4aa43b..5a58189 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -101,7 +101,7 @@
 
   @Override
   public void clearKotlinInfo() {
-    getDefinition().clearKotlinMemberInfo();
+    getDefinition().clearKotlinInfo();
   }
 
   public MethodPosition getPosition() {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
index 2907d72..70a51ed 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataEnqueuerExtension.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.kotlin;
 
 import static com.android.tools.r8.kotlin.KotlinClassMetadataReader.hasKotlinClassMetadataAnnotation;
-import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
@@ -23,6 +22,7 @@
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.shaking.Enqueuer;
 import com.android.tools.r8.shaking.Enqueuer.EnqueuerDefinitionSupplier;
+import com.android.tools.r8.shaking.KeepClassInfo;
 import com.google.common.collect.Sets;
 import java.util.Set;
 
@@ -49,33 +49,31 @@
 
   @Override
   public void done(Enqueuer enqueuer) {
-    DexType kotlinMetadataType = appView.dexItemFactory().kotlinMetadataType;
-    DexClass kotlinMetadataClass =
-        appView.appInfo().definitionForWithoutExistenceAssert(kotlinMetadataType);
     // In the first round of tree shaking build up all metadata such that it can be traced later.
-    boolean keepMetadata =
-        (kotlinMetadataClass == null
-                || kotlinMetadataClass.isNotProgramClass()
-                || enqueuer.isPinned(kotlinMetadataType))
-            && appView
-                .options()
-                .getProguardConfiguration()
-                .getKeepAttributes()
-                .runtimeVisibleAnnotations;
+    boolean keepKotlinMetadata =
+        KeepClassInfo.isKotlinMetadataClassKept(
+            appView.dexItemFactory(),
+            appView.appInfo()::definitionForWithoutExistenceAssert,
+            enqueuer::getKeepInfo);
+    // In the first round of tree shaking build up all metadata such that it can be traced later.
     if (enqueuer.getMode().isInitialTreeShaking()) {
       Set<DexMethod> keepByteCodeFunctions = Sets.newIdentityHashSet();
       Set<DexProgramClass> localOrAnonymousClasses = Sets.newIdentityHashSet();
       enqueuer.forAllLiveClasses(
           clazz -> {
             assert clazz.getKotlinInfo().isNoKotlinInformation();
-            if (!keepMetadata || !enqueuer.isPinned(clazz.getType())) {
+            if (enqueuer
+                .getKeepInfo(clazz)
+                .isKotlinMetadataRemovalAllowed(appView.options(), keepKotlinMetadata)) {
               if (KotlinClassMetadataReader.isLambda(appView, clazz)
                   && clazz.hasClassInitializer()) {
                 feedback.classInitializerMayBePostponed(clazz.getClassInitializer());
               }
-              clazz.setKotlinInfo(getNoKotlinInfo());
+              clazz.clearKotlinInfo();
               clazz.removeAnnotations(
-                  annotation -> annotation.getAnnotationType() == kotlinMetadataType);
+                  annotation ->
+                      annotation.getAnnotationType()
+                          == appView.dexItemFactory().kotlinMetadataType);
             } else {
               clazz.setKotlinInfo(
                   KotlinClassMetadataReader.getKotlinInfo(
@@ -109,17 +107,18 @@
       assert enqueuer.getMode().isFinalTreeShaking();
       enqueuer.forAllLiveClasses(
           clazz -> {
-            if (!enqueuer.isPinned(clazz.getType())) {
-              clazz.setKotlinInfo(getNoKotlinInfo());
+            if (enqueuer
+                .getKeepInfo(clazz)
+                .isKotlinMetadataRemovalAllowed(appView.options(), keepKotlinMetadata)) {
+              clazz.clearKotlinInfo();
               clazz.members().forEach(DexEncodedMember::clearKotlinInfo);
               clazz.removeAnnotations(
-                  annotation -> annotation.getAnnotationType() == kotlinMetadataType);
+                  annotation ->
+                      annotation.getAnnotationType()
+                          == appView.dexItemFactory().kotlinMetadataType);
             } else {
-              boolean shouldHaveKotlinInfo =
-                  keepMetadata
-                      && hasKotlinClassMetadataAnnotation(clazz, definitionsForContext(clazz));
-              boolean hasKotlinInfo = clazz.getKotlinInfo() != getNoKotlinInfo();
-              assert hasKotlinInfo == shouldHaveKotlinInfo;
+              assert hasKotlinClassMetadataAnnotation(clazz, definitionsForContext(clazz))
+                  == !clazz.getKotlinInfo().isNoKotlinInformation();
             }
           });
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index e1eabf2..567b7cb 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -10,7 +10,7 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexDefinition;
 import com.android.tools.r8.graph.DexEncodedAnnotation;
-import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -168,11 +168,12 @@
       stripAttributes(clazz);
       clazz.setAnnotations(
           clazz.annotations().rewrite(annotation -> rewriteAnnotation(clazz, annotation)));
-      // Kotlin properties are split over fields and methods. Check if any is pinned before pruning
-      // the information.
+      // Kotlin metadata for classes are removed in the KotlinMetadataEnqueuerExtension. Kotlin
+      // properties are split over fields and methods. Check if any is pinned before pruning the
+      // information.
       Set<KotlinPropertyInfo> pinnedKotlinProperties = Sets.newIdentityHashSet();
-      clazz.forEachMethod(method -> processMethod(method, clazz, pinnedKotlinProperties));
-      clazz.forEachField(field -> processField(field, clazz, pinnedKotlinProperties));
+      clazz.forEachProgramMember(
+          member -> processMember(member.getDefinition(), clazz, pinnedKotlinProperties));
       clazz.forEachProgramMember(
           member -> {
             KotlinMemberLevelInfo kotlinInfo = member.getKotlinInfo();
@@ -182,40 +183,43 @@
             }
           });
     }
+    assert verifyNoKeptKotlinMembersForClassesWithNoKotlinInfo();
   }
 
-  private void processMethod(
-      DexEncodedMethod method,
-      DexProgramClass clazz,
-      Set<KotlinPropertyInfo> pinnedKotlinProperties) {
-    method.setAnnotations(
-        method.annotations().rewrite(annotation -> rewriteAnnotation(method, annotation)));
-    method.parameterAnnotationsList =
-        method.parameterAnnotationsList.keepIf(this::filterParameterAnnotations);
-    KeepMethodInfo methodInfo = appView.getKeepInfo().getMethodInfo(method, clazz);
-    if (methodInfo.isSignatureAttributeRemovalAllowed(options)) {
-      method.clearGenericSignature();
+  private boolean verifyNoKeptKotlinMembersForClassesWithNoKotlinInfo() {
+    for (DexProgramClass clazz : appView.appInfo().classes()) {
+      if (clazz.getKotlinInfo().isNoKotlinInformation()) {
+        clazz.forEachProgramMember(
+            member -> {
+              assert member.getKotlinInfo().isNoKotlinInformation()
+                  : "Should have pruned kotlin info";
+            });
+      }
     }
-    if (!methodInfo.isPinned() && method.getKotlinInfo().isFunction()) {
-      method.clearKotlinMemberInfo();
-    }
-    if (methodInfo.isPinned() && method.getKotlinInfo().isProperty()) {
-      pinnedKotlinProperties.add(method.getKotlinInfo().asProperty());
-    }
+    return true;
   }
 
-  private void processField(
-      DexEncodedField field,
+  private void processMember(
+      DexEncodedMember<?, ?> member,
       DexProgramClass clazz,
       Set<KotlinPropertyInfo> pinnedKotlinProperties) {
-    field.setAnnotations(
-        field.annotations().rewrite(annotation -> rewriteAnnotation(field, annotation)));
-    KeepFieldInfo fieldInfo = appView.getKeepInfo().getFieldInfo(field, clazz);
-    if (fieldInfo.isSignatureAttributeRemovalAllowed(options)) {
-      field.clearGenericSignature();
+    member.setAnnotations(
+        member.annotations().rewrite(annotation -> rewriteAnnotation(member, annotation)));
+    if (member.isDexEncodedMethod()) {
+      DexEncodedMethod method = member.asDexEncodedMethod();
+      method.parameterAnnotationsList =
+          method.parameterAnnotationsList.keepIf(this::filterParameterAnnotations);
     }
-    if (fieldInfo.isPinned() && field.getKotlinInfo().isProperty()) {
-      pinnedKotlinProperties.add(field.getKotlinInfo().asProperty());
+    KeepMemberInfo<?, ?> memberInfo = appView.getKeepInfo().getMemberInfo(member, clazz);
+    if (memberInfo.isSignatureAttributeRemovalAllowed(options)) {
+      member.clearGenericSignature();
+    }
+    if (!member.getKotlinInfo().isProperty() && memberInfo.isKotlinMetadataRemovalAllowed(clazz)) {
+      member.clearKotlinInfo();
+    }
+    // Postpone removal of kotlin property info until we have seen all fields, setters and getters.
+    if (member.getKotlinInfo().isProperty() && !memberInfo.isKotlinMetadataRemovalAllowed(clazz)) {
+      pinnedKotlinProperties.add(member.getKotlinInfo().asProperty());
     }
   }
 
@@ -266,20 +270,18 @@
     // need to keep the enclosing method and inner classes attributes, if requested. In Proguard
     // compatibility mode we keep these attributes independent of whether the given class is kept.
     // In full mode we remove the attribute if not both sides are kept.
+    KeepClassInfo keepInfo = appView.getKeepInfo().getClassInfo(clazz);
     clazz.removeEnclosingMethodAttribute(
         enclosingMethodAttribute ->
-            appView
-                .getKeepInfo()
-                .getClassInfo(clazz)
-                .isEnclosingMethodAttributeRemovalAllowed(
-                    options, enclosingMethodAttribute, appView));
+            keepInfo.isEnclosingMethodAttributeRemovalAllowed(
+                options, enclosingMethodAttribute, appView));
     // It is important that the call to getEnclosingMethodAttribute is done after we potentially
     // pruned it above.
     clazz.removeInnerClasses(
         attribute ->
             canRemoveInnerClassAttribute(clazz, attribute, clazz.getEnclosingMethodAttribute()));
     if (clazz.getClassSignature().isValid()
-        && appView.getKeepInfo().getClassInfo(clazz).isSignatureAttributeRemovalAllowed(options)) {
+        && keepInfo.isSignatureAttributeRemovalAllowed(options)) {
       clazz.clearClassSignature();
     }
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 1023e70..61db1bf 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -653,8 +653,8 @@
     return clazz;
   }
 
-  public boolean isPinned(DexType type) {
-    return keepInfo.isPinned(type, appInfo);
+  public KeepClassInfo getKeepInfo(DexProgramClass clazz) {
+    return keepInfo.getClassInfo(clazz);
   }
 
   private void addLiveNonProgramType(
diff --git a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
index cc11777..07f625d 100644
--- a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
@@ -21,4 +21,6 @@
   boolean isKeepEnclosingMethodAttributeEnabled();
 
   boolean isKeepInnerClassesAttributeEnabled();
+
+  boolean isKeepRuntimeVisibleAnnotationsEnabled();
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
index 40b2c9e..a12ca25 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
@@ -3,6 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import java.util.function.Function;
+
 /** Immutable keep requirements for a class. */
 public final class KeepClassInfo extends KeepInfo<KeepClassInfo.Builder, KeepClassInfo> {
 
@@ -41,6 +48,31 @@
         && !internalIsAccessModificationRequiredForRepackaging();
   }
 
+  public boolean isKotlinMetadataRemovalAllowed(
+      GlobalKeepInfoConfiguration configuration, boolean kotlinMetadataKept) {
+    return !kotlinMetadataKept
+        || !isPinned()
+        || !configuration.isKeepRuntimeVisibleAnnotationsEnabled();
+  }
+
+  public static boolean isKotlinMetadataClassKept(AppView<?> appView) {
+    return isKotlinMetadataClassKept(
+        appView.dexItemFactory(),
+        appView.appInfo()::definitionForWithoutExistenceAssert,
+        appView.getKeepInfo()::getClassInfo);
+  }
+
+  public static boolean isKotlinMetadataClassKept(
+      DexItemFactory factory,
+      Function<DexType, DexClass> definitionForWithoutExistenceAssert,
+      Function<DexProgramClass, KeepClassInfo> getClassInfo) {
+    DexType kotlinMetadataType = factory.kotlinMetadataType;
+    DexClass kotlinMetadataClass = definitionForWithoutExistenceAssert.apply(kotlinMetadataType);
+    return kotlinMetadataClass == null
+        || kotlinMetadataClass.isNotProgramClass()
+        || getClassInfo.apply(kotlinMetadataClass.asProgramClass()).isPinned();
+  }
+
   @Override
   public boolean isTop() {
     return this.equals(top());
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
index 770e84c..46d8fa2 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.shaking.KeepInfo.Builder;
 
 /** Immutable keep requirements for a member. */
@@ -18,4 +19,10 @@
     return configuration.isRepackagingEnabled()
         && !internalIsAccessModificationRequiredForRepackaging();
   }
+
+  public boolean isKotlinMetadataRemovalAllowed(DexProgramClass holder) {
+    // Checking the holder for missing kotlin information relies on the holder being processed
+    // before members.
+    return holder.getKotlinInfo().isNoKotlinInformation() || !isPinned();
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 0263e28..e08fe12 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -615,6 +615,11 @@
     return proguardConfiguration.getKeepAttributes().innerClasses;
   }
 
+  @Override
+  public boolean isKeepRuntimeVisibleAnnotationsEnabled() {
+    return proguardConfiguration.getKeepAttributes().runtimeVisibleAnnotations;
+  }
+
   /**
    * If any non-static class merging is enabled, information about types referred to by instanceOf
    * and check cast instructions needs to be collected.