Create keepanno structure for TypePattern with instanceOf

Change-Id: I9da62f73d0c26bb7d40f56c6e721f527c6cdb830
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/TypePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/TypePattern.java
index 47fdb1d..9413ca3 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/TypePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/TypePattern.java
@@ -34,6 +34,7 @@
    * <ul>
    *   <li>constant
    *   <li>classNamePattern
+   *   <li>instanceOfPattern
    * </ul>
    */
   String name() default "";
@@ -48,6 +49,7 @@
    * <ul>
    *   <li>name
    *   <li>classNamePattern
+   *   <li>instanceOfPattern
    * </ul>
    */
   Class<?> constant() default Object.class;
@@ -60,7 +62,23 @@
    * <ul>
    *   <li>name
    *   <li>constant
+   *   <li>instanceOfPattern
    * </ul>
    */
   ClassNamePattern classNamePattern() default @ClassNamePattern(unqualifiedName = "");
+
+  /**
+   * Define the instance-of with a pattern.
+   *
+   * <p>Mutually exclusive with the following other properties defining type-pattern:
+   *
+   * <ul>
+   *   <li>name
+   *   <li>constant
+   *   <li>classNamePattern
+   * </ul>
+   *
+   * @return The pattern that defines what instance-of the class must be.
+   */
+  InstanceOfPattern instanceOfPattern() default @InstanceOfPattern();
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassNameParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassNameParser.java
index 538c8ca..e0ef6b5 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassNameParser.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassNameParser.java
@@ -69,7 +69,11 @@
         arrayTypePattern -> {
           throw parsingContext.error("Invalid use of array type where class type was expected");
         },
-        classNamePattern -> classNamePattern);
+        classNamePattern -> classNamePattern,
+        instanceOfPattern -> {
+          throw parsingContext.error(
+              "Invalid use of instance of type where class type was expected");
+        });
   }
 
   @Override
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
index c1f8c13..2645b35 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
@@ -466,6 +466,9 @@
                 },
                 clazz -> {
                   writeClassNamePattern(clazz, TypePattern.classNamePattern, v);
+                },
+                instanceOf -> {
+                  writeInstanceOfPattern(instanceOf, v);
                 }));
   }
 
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/TypeParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/TypeParser.java
index a0c0afc..f81610e 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/TypeParser.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/TypeParser.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.keepanno.asm;
 
 import com.android.tools.r8.keepanno.asm.ClassNameParser.ClassNameProperty;
+import com.android.tools.r8.keepanno.asm.InstanceOfParser.InstanceOfProperties;
 import com.android.tools.r8.keepanno.asm.TypeParser.TypeProperty;
 import com.android.tools.r8.keepanno.ast.AnnotationConstants.TypePattern;
 import com.android.tools.r8.keepanno.ast.KeepTypePattern;
@@ -24,7 +25,8 @@
     TYPE_PATTERN,
     TYPE_NAME,
     TYPE_CONSTANT,
-    CLASS_NAME_PATTERN
+    CLASS_NAME_PATTERN,
+    INSTANCE_OF_PATTERN
   }
 
   @Override
@@ -56,6 +58,7 @@
           typeParser.setProperty(TypePattern.name, TypeProperty.TYPE_NAME);
           typeParser.setProperty(TypePattern.constant, TypeProperty.TYPE_CONSTANT);
           typeParser.setProperty(TypePattern.classNamePattern, TypeProperty.CLASS_NAME_PATTERN);
+          typeParser.setProperty(TypePattern.instanceOfPattern, TypeProperty.INSTANCE_OF_PATTERN);
           return new ParserVisitor(
               context,
               typeParser,
@@ -70,6 +73,15 @@
               descriptor,
               value -> setValue.accept(KeepTypePattern.fromClass(value)));
         }
+      case INSTANCE_OF_PATTERN:
+        {
+          InstanceOfParser parser = new InstanceOfParser(getParsingContext());
+          return parser.tryPropertyAnnotation(
+              InstanceOfProperties.PATTERN,
+              name,
+              descriptor,
+              value -> setValue.accept(KeepTypePattern.fromInstanceOf(value)));
+        }
       default:
         return null;
     }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
index 9b0ae29..77643cd 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
@@ -237,6 +237,7 @@
     public static final String name = "name";
     public static final String constant = "constant";
     public static final String classNamePattern = "classNamePattern";
+    public static final String instanceOfPattern = "instanceOfPattern";
   }
 
   public static final class ClassNamePattern {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepArrayTypePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepArrayTypePattern.java
index 6194fb3..5981336 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepArrayTypePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepArrayTypePattern.java
@@ -47,11 +47,14 @@
             () -> {
               throw new KeepEdgeException("No descriptor exists for 'any primitive' array");
             },
-            primitive -> primitive.getDescriptor(),
+            KeepPrimitiveTypePattern::getDescriptor,
             array -> {
               throw new KeepEdgeException("Unexpected nested array");
             },
-            clazz -> clazz.getExactDescriptor());
+            KeepQualifiedClassNamePattern::getExactDescriptor,
+            instanceOf -> {
+              throw new KeepEdgeException("No descriptor exists for instanceOf array");
+            });
   }
 
   @Override
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTypePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTypePattern.java
index a0edf23..7e3c088 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTypePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTypePattern.java
@@ -28,6 +28,10 @@
     return new ClassType(type);
   }
 
+  public static KeepTypePattern fromInstanceOf(KeepInstanceOfPattern pattern) {
+    return new KeepInstanceOf(pattern);
+  }
+
   public static KeepTypePattern fromDescriptor(String typeDescriptor) {
     char c = typeDescriptor.charAt(0);
     if (c == 'L') {
@@ -56,18 +60,21 @@
       Supplier<T> onAny,
       Function<KeepPrimitiveTypePattern, T> onPrimitive,
       Function<KeepArrayTypePattern, T> onArray,
-      Function<KeepQualifiedClassNamePattern, T> onClass);
+      Function<KeepQualifiedClassNamePattern, T> onClass,
+      Function<KeepInstanceOfPattern, T> onInstanceOf);
 
   public final void match(
       Runnable onAny,
       Consumer<KeepPrimitiveTypePattern> onPrimitive,
       Consumer<KeepArrayTypePattern> onArray,
-      Consumer<KeepQualifiedClassNamePattern> onClass) {
+      Consumer<KeepQualifiedClassNamePattern> onClass,
+      Consumer<KeepInstanceOfPattern> onInstanceOf) {
     apply(
         AstUtils.toVoidSupplier(onAny),
         AstUtils.toVoidFunction(onPrimitive),
         AstUtils.toVoidFunction(onArray),
-        AstUtils.toVoidFunction(onClass));
+        AstUtils.toVoidFunction(onClass),
+        AstUtils.toVoidFunction(onInstanceOf));
   }
 
   public boolean isAny() {
@@ -87,7 +94,8 @@
         Supplier<T> onAny,
         Function<KeepPrimitiveTypePattern, T> onPrimitive,
         Function<KeepArrayTypePattern, T> onArray,
-        Function<KeepQualifiedClassNamePattern, T> onClass) {
+        Function<KeepQualifiedClassNamePattern, T> onClass,
+        Function<KeepInstanceOfPattern, T> onInstanceOf) {
       return onAny.get();
     }
 
@@ -152,7 +160,8 @@
         Supplier<T> onAny,
         Function<KeepPrimitiveTypePattern, T> onPrimitive,
         Function<KeepArrayTypePattern, T> onArray,
-        Function<KeepQualifiedClassNamePattern, T> onClass) {
+        Function<KeepQualifiedClassNamePattern, T> onClass,
+        Function<KeepInstanceOfPattern, T> onInstanceOf) {
       return onPrimitive.apply(type);
     }
   }
@@ -169,7 +178,8 @@
         Supplier<T> onAny,
         Function<KeepPrimitiveTypePattern, T> onPrimitive,
         Function<KeepArrayTypePattern, T> onArray,
-        Function<KeepQualifiedClassNamePattern, T> onClass) {
+        Function<KeepQualifiedClassNamePattern, T> onClass,
+        Function<KeepInstanceOfPattern, T> onInstanceOf) {
       return onClass.apply(type);
     }
 
@@ -208,7 +218,8 @@
         Supplier<T> onAny,
         Function<KeepPrimitiveTypePattern, T> onPrimitive,
         Function<KeepArrayTypePattern, T> onArray,
-        Function<KeepQualifiedClassNamePattern, T> onClass) {
+        Function<KeepQualifiedClassNamePattern, T> onClass,
+        Function<KeepInstanceOfPattern, T> onInstanceOf) {
       return onArray.apply(type);
     }
 
@@ -234,4 +245,22 @@
       return type.toString();
     }
   }
+
+  private static class KeepInstanceOf extends KeepTypePattern {
+    private final KeepInstanceOfPattern instanceOf;
+
+    private KeepInstanceOf(KeepInstanceOfPattern instanceOf) {
+      this.instanceOf = instanceOf;
+    }
+
+    @Override
+    public <T> T apply(
+        Supplier<T> onAny,
+        Function<KeepPrimitiveTypePattern, T> onPrimitive,
+        Function<KeepArrayTypePattern, T> onArray,
+        Function<KeepQualifiedClassNamePattern, T> onClass,
+        Function<KeepInstanceOfPattern, T> onInstanceOf) {
+      return onInstanceOf.apply(instanceOf);
+    }
+  }
 }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java
index 12d2caa..38a768f 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java
@@ -221,7 +221,13 @@
         printer::appendTripleStar,
         primitivePattern -> printPrimitiveType(printer, primitivePattern),
         arrayTypePattern -> printArrayType(printer, arrayTypePattern),
-        classTypePattern -> printClassName(classTypePattern, printer));
+        classTypePattern -> printClassName(classTypePattern, printer),
+        instanceOfPattern -> printInstanceOf(instanceOfPattern, printer));
+  }
+
+  private static RulePrinter printInstanceOf(
+      KeepInstanceOfPattern instanceOfPattern, RulePrinter printer) {
+    throw new Unimplemented();
   }
 
   private static RulePrinter printPrimitiveType(
diff --git a/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcher.java b/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcher.java
index dea79c9..052c9ad 100644
--- a/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcher.java
@@ -408,10 +408,11 @@
               },
               fieldPattern ->
                   holder.forEachProgramFieldMatching(
-                      f -> predicates.matchesField(f, fieldPattern), continueWithMember),
+                      f -> predicates.matchesField(f, fieldPattern, appInfo), continueWithMember),
               methodPattern ->
                   holder.forEachProgramMethodMatching(
-                      m -> predicates.matchesMethod(m, methodPattern), continueWithMember));
+                      m -> predicates.matchesMethod(m, methodPattern, appInfo),
+                      continueWithMember));
       if (didContinue.isFalse()) {
         // No match for the member pattern existed, continue with next class.
         continueWithNoClassClearingMembers(
diff --git a/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcherPredicates.java b/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcherPredicates.java
index 0982747..dfe3f51 100644
--- a/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcherPredicates.java
+++ b/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcherPredicates.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -84,7 +85,16 @@
   }
 
   private boolean matchesInstanceOfPattern(
-      DexProgramClass clazz, KeepInstanceOfPattern pattern, AppInfoWithClassHierarchy appInfo) {
+      DexType type, KeepInstanceOfPattern pattern, AppInfoWithClassHierarchy appInfo) {
+    DexClass dexClass = appInfo.definitionFor(type);
+    if (dexClass != null) {
+      return matchesInstanceOfPattern(dexClass.asProgramClass(), pattern, appInfo);
+    }
+    return false;
+  }
+
+  private boolean matchesInstanceOfPattern(
+      DexClass clazz, KeepInstanceOfPattern pattern, AppInfoWithClassHierarchy appInfo) {
     if (pattern.isAny()) {
       return true;
     }
@@ -115,23 +125,25 @@
         && matchesGeneralMemberAccess(member.getAccessFlags(), pattern.getAccessPattern());
   }
 
-  public boolean matchesMethod(DexEncodedMethod method, KeepMethodPattern pattern) {
+  public boolean matchesMethod(
+      DexEncodedMethod method, KeepMethodPattern pattern, AppInfoWithClassHierarchy appInfo) {
     if (pattern.isAnyMethod()) {
       return true;
     }
     return matchesString(method.getName(), pattern.getNamePattern().asStringPattern())
-        && matchesReturnType(method.getReturnType(), pattern.getReturnTypePattern())
-        && matchesParameters(method.getParameters(), pattern.getParametersPattern())
+        && matchesReturnType(method.getReturnType(), pattern.getReturnTypePattern(), appInfo)
+        && matchesParameters(method.getParameters(), pattern.getParametersPattern(), appInfo)
         && matchesAnnotatedBy(method.annotations(), pattern.getAnnotatedByPattern())
         && matchesMethodAccess(method.getAccessFlags(), pattern.getAccessPattern());
   }
 
-  public boolean matchesField(DexEncodedField field, KeepFieldPattern pattern) {
+  public boolean matchesField(
+      DexEncodedField field, KeepFieldPattern pattern, AppInfoWithClassHierarchy appInfo) {
     if (pattern.isAnyField()) {
       return true;
     }
     return matchesString(field.getName(), pattern.getNamePattern().asStringPattern())
-        && matchesType(field.getType(), pattern.getTypePattern().asType())
+        && matchesType(field.getType(), pattern.getTypePattern().asType(), appInfo)
         && matchesAnnotatedBy(field.annotations(), pattern.getAnnotatedByPattern())
         && matchesFieldAccess(field.getAccessFlags(), pattern.getAccessPattern());
   }
@@ -211,7 +223,10 @@
     return false;
   }
 
-  public boolean matchesParameters(DexTypeList parameters, KeepMethodParametersPattern pattern) {
+  public boolean matchesParameters(
+      DexTypeList parameters,
+      KeepMethodParametersPattern pattern,
+      AppInfoWithClassHierarchy appInfo) {
     if (pattern.isAny()) {
       return true;
     }
@@ -221,7 +236,7 @@
     }
     int size = parameters.size();
     for (int i = 0; i < size; i++) {
-      if (!matchesType(parameters.get(i), patternList.get(i))) {
+      if (!matchesType(parameters.get(i), patternList.get(i), appInfo)) {
         return false;
       }
     }
@@ -263,29 +278,34 @@
   }
 
   public boolean matchesReturnType(
-      DexType returnType, KeepMethodReturnTypePattern returnTypePattern) {
+      DexType returnType,
+      KeepMethodReturnTypePattern returnTypePattern,
+      AppInfoWithClassHierarchy appInfo) {
     if (returnTypePattern.isAny()) {
       return true;
     }
     if (returnTypePattern.isVoid()) {
       return returnType.isVoidType();
     }
-    return matchesType(returnType, returnTypePattern.asType());
+    return matchesType(returnType, returnTypePattern.asType(), appInfo);
   }
 
-  public boolean matchesType(DexType type, KeepTypePattern pattern) {
+  public boolean matchesType(
+      DexType type, KeepTypePattern pattern, AppInfoWithClassHierarchy appInfo) {
     return pattern.apply(
         () -> true,
         p -> matchesPrimitiveType(type, p),
-        p -> matchesArrayType(type, p),
-        p -> matchesClassType(type, p));
+        p -> matchesArrayType(type, p, appInfo),
+        p -> matchesClassType(type, p),
+        p -> matchesInstanceOfPattern(type, p, appInfo));
   }
 
   public boolean matchesClassType(DexType type, KeepQualifiedClassNamePattern pattern) {
     return type.isClassType() && matchesClassName(type, pattern);
   }
 
-  public boolean matchesArrayType(DexType type, KeepArrayTypePattern pattern) {
+  public boolean matchesArrayType(
+      DexType type, KeepArrayTypePattern pattern, AppInfoWithClassHierarchy appInfo) {
     if (!type.isArrayType()) {
       return false;
     }
@@ -296,7 +316,9 @@
       return false;
     }
     return matchesType(
-        type.toArrayElementAfterDimension(pattern.getDimensions(), factory), pattern.getBaseType());
+        type.toArrayElementAfterDimension(pattern.getDimensions(), factory),
+        pattern.getBaseType(),
+        appInfo);
   }
 
   public boolean matchesPrimitiveType(DexType type, KeepPrimitiveTypePattern pattern) {
diff --git a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
index b6be4dc..e92c4d8 100644
--- a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
+++ b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
@@ -581,7 +581,8 @@
           .addMember(
               new GroupMember("classNamePattern")
                   .setDocTitle("Classes matching the class-name pattern.")
-                  .defaultValue(CLASS_NAME_PATTERN, DEFAULT_INVALID_CLASS_NAME_PATTERN));
+                  .defaultValue(CLASS_NAME_PATTERN, DEFAULT_INVALID_CLASS_NAME_PATTERN))
+          .addMember(instanceOfPattern());
       // TODO(b/248408342): Add more injections on type pattern variants.
       // /** Exact type name as a string to match any array with that type as member. */
       // String arrayOf() default "";