[KeepAnno] Introduce patterns on types
The TypePattern is the general matcher for types. It is basically a
disjoint union of the possible ways on which to match types.
Follow-up CLs will extend this with more specific patterns for classes,
packages, class names, method names, field names, etc.
Bug: b/248408342
Change-Id: Ifa933e8bd792384f45f7552410756d586e790ee6
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
index c3c58c2..579d46f 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
@@ -298,11 +298,56 @@
* <p>If none, and other properties define this item as a method, the default matches any return
* type.
*
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnTypeConstant
+ * <li>methodReturnTypePattern
+ * </ul>
+ *
* @return The qualified type name of the method return type.
*/
String methodReturnType() default "";
/**
+ * Define the method return-type pattern by a class constant.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any return
+ * type.
+ *
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnType
+ * <li>methodReturnTypePattern
+ * </ul>
+ *
+ * @return A class constant denoting the type of the method return type.
+ */
+ Class<?> methodReturnTypeConstant() default Object.class;
+
+ /**
+ * Define the method return-type pattern by a type pattern.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any return
+ * type.
+ *
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnType
+ * <li>methodReturnTypeConstant
+ * </ul>
+ *
+ * @return The pattern of the method return type.
+ */
+ TypePattern methodReturnTypePattern() default @TypePattern(name = "");
+
+ /**
* Define the method parameters pattern by a list of fully qualified types.
*
* <p>Mutually exclusive with all field properties.
@@ -310,9 +355,25 @@
* <p>If none, and other properties define this item as a method, the default matches any
* parameters.
*
+ * <p>Mutually exclusive with the property `methodParameterTypePatterns` also defining parameters.
+ *
* @return The list of qualified type names of the method parameters.
*/
- String[] methodParameters() default {"<default>"};
+ String[] methodParameters() default {""};
+
+ /**
+ * Define the method parameters pattern by a list of patterns on types.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any
+ * parameters.
+ *
+ * <p>Mutually exclusive with the property `methodParameters` also defining parameters.
+ *
+ * @return The list of type patterns for the method parameters.
+ */
+ TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
/**
* Define the field-access pattern by matching on access flags.
@@ -345,7 +406,50 @@
*
* <p>If none, and other properties define this item as a field, the default matches any type.
*
- * @return The qualified type name of the field type.
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldTypeConstant
+ * <li>fieldTypePattern
+ * </ul>
+ *
+ * @return The qualified type name for the field type.
*/
String fieldType() default "";
+
+ /**
+ * Define the field-type pattern by a class constant.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any type.
+ *
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldType
+ * <li>fieldTypePattern
+ * </ul>
+ *
+ * @return The class constant for the field type.
+ */
+ Class<?> fieldTypeConstant() default Object.class;
+
+ /**
+ * Define the field-type pattern by a pattern on types.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any type.
+ *
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldType
+ * <li>fieldTypeConstant
+ * </ul>
+ *
+ * @return The type pattern for the field type.
+ */
+ TypePattern fieldTypePattern() default @TypePattern(name = "");
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
index e539cc4..470266b 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
@@ -270,11 +270,56 @@
* <p>If none, and other properties define this item as a method, the default matches any return
* type.
*
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnTypeConstant
+ * <li>methodReturnTypePattern
+ * </ul>
+ *
* @return The qualified type name of the method return type.
*/
String methodReturnType() default "";
/**
+ * Define the method return-type pattern by a class constant.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any return
+ * type.
+ *
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnType
+ * <li>methodReturnTypePattern
+ * </ul>
+ *
+ * @return A class constant denoting the type of the method return type.
+ */
+ Class<?> methodReturnTypeConstant() default Object.class;
+
+ /**
+ * Define the method return-type pattern by a type pattern.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any return
+ * type.
+ *
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnType
+ * <li>methodReturnTypeConstant
+ * </ul>
+ *
+ * @return The pattern of the method return type.
+ */
+ TypePattern methodReturnTypePattern() default @TypePattern(name = "");
+
+ /**
* Define the method parameters pattern by a list of fully qualified types.
*
* <p>Mutually exclusive with all field properties.
@@ -282,9 +327,25 @@
* <p>If none, and other properties define this item as a method, the default matches any
* parameters.
*
+ * <p>Mutually exclusive with the property `methodParameterTypePatterns` also defining parameters.
+ *
* @return The list of qualified type names of the method parameters.
*/
- String[] methodParameters() default {"<default>"};
+ String[] methodParameters() default {""};
+
+ /**
+ * Define the method parameters pattern by a list of patterns on types.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any
+ * parameters.
+ *
+ * <p>Mutually exclusive with the property `methodParameters` also defining parameters.
+ *
+ * @return The list of type patterns for the method parameters.
+ */
+ TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
/**
* Define the field-access pattern by matching on access flags.
@@ -317,7 +378,50 @@
*
* <p>If none, and other properties define this item as a field, the default matches any type.
*
- * @return The qualified type name of the field type.
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldTypeConstant
+ * <li>fieldTypePattern
+ * </ul>
+ *
+ * @return The qualified type name for the field type.
*/
String fieldType() default "";
+
+ /**
+ * Define the field-type pattern by a class constant.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any type.
+ *
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldType
+ * <li>fieldTypePattern
+ * </ul>
+ *
+ * @return The class constant for the field type.
+ */
+ Class<?> fieldTypeConstant() default Object.class;
+
+ /**
+ * Define the field-type pattern by a pattern on types.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any type.
+ *
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldType
+ * <li>fieldTypeConstant
+ * </ul>
+ *
+ * @return The type pattern for the field type.
+ */
+ TypePattern fieldTypePattern() default @TypePattern(name = "");
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java
index cd3559c..5ece578 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java
@@ -97,11 +97,56 @@
* <p>If none, and other properties define this item as a method, the default matches any return
* type.
*
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnTypeConstant
+ * <li>methodReturnTypePattern
+ * </ul>
+ *
* @return The qualified type name of the method return type.
*/
String methodReturnType() default "";
/**
+ * Define the method return-type pattern by a class constant.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any return
+ * type.
+ *
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnType
+ * <li>methodReturnTypePattern
+ * </ul>
+ *
+ * @return A class constant denoting the type of the method return type.
+ */
+ Class<?> methodReturnTypeConstant() default Object.class;
+
+ /**
+ * Define the method return-type pattern by a type pattern.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any return
+ * type.
+ *
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnType
+ * <li>methodReturnTypeConstant
+ * </ul>
+ *
+ * @return The pattern of the method return type.
+ */
+ TypePattern methodReturnTypePattern() default @TypePattern(name = "");
+
+ /**
* Define the method parameters pattern by a list of fully qualified types.
*
* <p>Mutually exclusive with all field properties.
@@ -109,9 +154,25 @@
* <p>If none, and other properties define this item as a method, the default matches any
* parameters.
*
+ * <p>Mutually exclusive with the property `methodParameterTypePatterns` also defining parameters.
+ *
* @return The list of qualified type names of the method parameters.
*/
- String[] methodParameters() default {"<default>"};
+ String[] methodParameters() default {""};
+
+ /**
+ * Define the method parameters pattern by a list of patterns on types.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any
+ * parameters.
+ *
+ * <p>Mutually exclusive with the property `methodParameters` also defining parameters.
+ *
+ * @return The list of type patterns for the method parameters.
+ */
+ TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
/**
* Define the field-access pattern by matching on access flags.
@@ -144,7 +205,50 @@
*
* <p>If none, and other properties define this item as a field, the default matches any type.
*
- * @return The qualified type name of the field type.
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldTypeConstant
+ * <li>fieldTypePattern
+ * </ul>
+ *
+ * @return The qualified type name for the field type.
*/
String fieldType() default "";
+
+ /**
+ * Define the field-type pattern by a class constant.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any type.
+ *
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldType
+ * <li>fieldTypePattern
+ * </ul>
+ *
+ * @return The class constant for the field type.
+ */
+ Class<?> fieldTypeConstant() default Object.class;
+
+ /**
+ * Define the field-type pattern by a pattern on types.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any type.
+ *
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldType
+ * <li>fieldTypeConstant
+ * </ul>
+ *
+ * @return The type pattern for the field type.
+ */
+ TypePattern fieldTypePattern() default @TypePattern(name = "");
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
index f080454..7bbc2f0 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
@@ -367,11 +367,56 @@
* <p>If none, and other properties define this item as a method, the default matches any return
* type.
*
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnTypeConstant
+ * <li>methodReturnTypePattern
+ * </ul>
+ *
* @return The qualified type name of the method return type.
*/
String methodReturnType() default "";
/**
+ * Define the method return-type pattern by a class constant.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any return
+ * type.
+ *
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnType
+ * <li>methodReturnTypePattern
+ * </ul>
+ *
+ * @return A class constant denoting the type of the method return type.
+ */
+ Class<?> methodReturnTypeConstant() default Object.class;
+
+ /**
+ * Define the method return-type pattern by a type pattern.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any return
+ * type.
+ *
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnType
+ * <li>methodReturnTypeConstant
+ * </ul>
+ *
+ * @return The pattern of the method return type.
+ */
+ TypePattern methodReturnTypePattern() default @TypePattern(name = "");
+
+ /**
* Define the method parameters pattern by a list of fully qualified types.
*
* <p>Mutually exclusive with all field properties.
@@ -379,9 +424,25 @@
* <p>If none, and other properties define this item as a method, the default matches any
* parameters.
*
+ * <p>Mutually exclusive with the property `methodParameterTypePatterns` also defining parameters.
+ *
* @return The list of qualified type names of the method parameters.
*/
- String[] methodParameters() default {"<default>"};
+ String[] methodParameters() default {""};
+
+ /**
+ * Define the method parameters pattern by a list of patterns on types.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any
+ * parameters.
+ *
+ * <p>Mutually exclusive with the property `methodParameters` also defining parameters.
+ *
+ * @return The list of type patterns for the method parameters.
+ */
+ TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
/**
* Define the field-access pattern by matching on access flags.
@@ -414,7 +475,50 @@
*
* <p>If none, and other properties define this item as a field, the default matches any type.
*
- * @return The qualified type name of the field type.
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldTypeConstant
+ * <li>fieldTypePattern
+ * </ul>
+ *
+ * @return The qualified type name for the field type.
*/
String fieldType() default "";
+
+ /**
+ * Define the field-type pattern by a class constant.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any type.
+ *
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldType
+ * <li>fieldTypePattern
+ * </ul>
+ *
+ * @return The class constant for the field type.
+ */
+ Class<?> fieldTypeConstant() default Object.class;
+
+ /**
+ * Define the field-type pattern by a pattern on types.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any type.
+ *
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldType
+ * <li>fieldTypeConstant
+ * </ul>
+ *
+ * @return The type pattern for the field type.
+ */
+ TypePattern fieldTypePattern() default @TypePattern(name = "");
}
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
new file mode 100644
index 0000000..1d7e6c6
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/TypePattern.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2023, 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.
+
+// ***********************************************************************************
+// GENERATED FILE. DO NOT EDIT! See KeepItemAnnotationGenerator.java.
+// ***********************************************************************************
+
+package com.android.tools.r8.keepanno.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A pattern structure for matching types.
+ *
+ * <p>If no properties are set, the default pattern matches any type.
+ *
+ * <p>All properties on this annotation are mutually exclusive.
+ */
+@Target(ElementType.ANNOTATION_TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface TypePattern {
+
+ /**
+ * Exact type name as a string.
+ *
+ * <p>For example, {@code "long"} or {@code "java.lang.String"}.
+ *
+ * <p>Mutually exclusive with the property `constant` also defining type-pattern.
+ */
+ String name() default "";
+
+ /**
+ * Exact type from a class constant.
+ *
+ * <p>For example, {@code String.class}.
+ *
+ * <p>Mutually exclusive with the property `name` also defining type-pattern.
+ */
+ Class<?> constant() default Object.class;
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java
index fa41767..149e51c 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java
@@ -140,11 +140,56 @@
* <p>If none, and other properties define this item as a method, the default matches any return
* type.
*
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnTypeConstant
+ * <li>methodReturnTypePattern
+ * </ul>
+ *
* @return The qualified type name of the method return type.
*/
String methodReturnType() default "";
/**
+ * Define the method return-type pattern by a class constant.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any return
+ * type.
+ *
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnType
+ * <li>methodReturnTypePattern
+ * </ul>
+ *
+ * @return A class constant denoting the type of the method return type.
+ */
+ Class<?> methodReturnTypeConstant() default Object.class;
+
+ /**
+ * Define the method return-type pattern by a type pattern.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any return
+ * type.
+ *
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnType
+ * <li>methodReturnTypeConstant
+ * </ul>
+ *
+ * @return The pattern of the method return type.
+ */
+ TypePattern methodReturnTypePattern() default @TypePattern(name = "");
+
+ /**
* Define the method parameters pattern by a list of fully qualified types.
*
* <p>Mutually exclusive with all field properties.
@@ -152,9 +197,25 @@
* <p>If none, and other properties define this item as a method, the default matches any
* parameters.
*
+ * <p>Mutually exclusive with the property `methodParameterTypePatterns` also defining parameters.
+ *
* @return The list of qualified type names of the method parameters.
*/
- String[] methodParameters() default {"<default>"};
+ String[] methodParameters() default {""};
+
+ /**
+ * Define the method parameters pattern by a list of patterns on types.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any
+ * parameters.
+ *
+ * <p>Mutually exclusive with the property `methodParameters` also defining parameters.
+ *
+ * @return The list of type patterns for the method parameters.
+ */
+ TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
/**
* Define the field-access pattern by matching on access flags.
@@ -187,7 +248,50 @@
*
* <p>If none, and other properties define this item as a field, the default matches any type.
*
- * @return The qualified type name of the field type.
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldTypeConstant
+ * <li>fieldTypePattern
+ * </ul>
+ *
+ * @return The qualified type name for the field type.
*/
String fieldType() default "";
+
+ /**
+ * Define the field-type pattern by a class constant.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any type.
+ *
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldType
+ * <li>fieldTypePattern
+ * </ul>
+ *
+ * @return The class constant for the field type.
+ */
+ Class<?> fieldTypeConstant() default Object.class;
+
+ /**
+ * Define the field-type pattern by a pattern on types.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any type.
+ *
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldType
+ * <li>fieldTypeConstant
+ * </ul>
+ *
+ * @return The type pattern for the field type.
+ */
+ TypePattern fieldTypePattern() default @TypePattern(name = "");
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java
index 83425ad..24073af 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java
@@ -140,11 +140,56 @@
* <p>If none, and other properties define this item as a method, the default matches any return
* type.
*
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnTypeConstant
+ * <li>methodReturnTypePattern
+ * </ul>
+ *
* @return The qualified type name of the method return type.
*/
String methodReturnType() default "";
/**
+ * Define the method return-type pattern by a class constant.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any return
+ * type.
+ *
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnType
+ * <li>methodReturnTypePattern
+ * </ul>
+ *
+ * @return A class constant denoting the type of the method return type.
+ */
+ Class<?> methodReturnTypeConstant() default Object.class;
+
+ /**
+ * Define the method return-type pattern by a type pattern.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any return
+ * type.
+ *
+ * <p>Mutually exclusive with the following other properties defining return-type:
+ *
+ * <ul>
+ * <li>methodReturnType
+ * <li>methodReturnTypeConstant
+ * </ul>
+ *
+ * @return The pattern of the method return type.
+ */
+ TypePattern methodReturnTypePattern() default @TypePattern(name = "");
+
+ /**
* Define the method parameters pattern by a list of fully qualified types.
*
* <p>Mutually exclusive with all field properties.
@@ -152,9 +197,25 @@
* <p>If none, and other properties define this item as a method, the default matches any
* parameters.
*
+ * <p>Mutually exclusive with the property `methodParameterTypePatterns` also defining parameters.
+ *
* @return The list of qualified type names of the method parameters.
*/
- String[] methodParameters() default {"<default>"};
+ String[] methodParameters() default {""};
+
+ /**
+ * Define the method parameters pattern by a list of patterns on types.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any
+ * parameters.
+ *
+ * <p>Mutually exclusive with the property `methodParameters` also defining parameters.
+ *
+ * @return The list of type patterns for the method parameters.
+ */
+ TypePattern[] methodParameterTypePatterns() default {@TypePattern(name = "")};
/**
* Define the field-access pattern by matching on access flags.
@@ -187,7 +248,50 @@
*
* <p>If none, and other properties define this item as a field, the default matches any type.
*
- * @return The qualified type name of the field type.
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldTypeConstant
+ * <li>fieldTypePattern
+ * </ul>
+ *
+ * @return The qualified type name for the field type.
*/
String fieldType() default "";
+
+ /**
+ * Define the field-type pattern by a class constant.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any type.
+ *
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldType
+ * <li>fieldTypePattern
+ * </ul>
+ *
+ * @return The class constant for the field type.
+ */
+ Class<?> fieldTypeConstant() default Object.class;
+
+ /**
+ * Define the field-type pattern by a pattern on types.
+ *
+ * <p>Mutually exclusive with all method properties.
+ *
+ * <p>If none, and other properties define this item as a field, the default matches any type.
+ *
+ * <p>Mutually exclusive with the following other properties defining field-type:
+ *
+ * <ul>
+ * <li>fieldType
+ * <li>fieldTypeConstant
+ * </ul>
+ *
+ * @return The type pattern for the field type.
+ */
+ TypePattern fieldTypePattern() default @TypePattern(name = "");
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
index 98223c7..35beddf 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.keepanno.ast.AnnotationConstants.MethodAccess;
import com.android.tools.r8.keepanno.ast.AnnotationConstants.Option;
import com.android.tools.r8.keepanno.ast.AnnotationConstants.Target;
+import com.android.tools.r8.keepanno.ast.AnnotationConstants.TypePattern;
import com.android.tools.r8.keepanno.ast.AnnotationConstants.UsedByReflection;
import com.android.tools.r8.keepanno.ast.AnnotationConstants.UsesReflection;
import com.android.tools.r8.keepanno.ast.KeepBindingReference;
@@ -756,7 +757,7 @@
},
bindingsHelper);
}
- AnnotationVisitor visitor = optionsDeclaration.tryParseArray(name, v -> {});
+ AnnotationVisitor visitor = optionsDeclaration.tryParseArray(name);
if (visitor != null) {
return visitor;
}
@@ -879,7 +880,7 @@
},
bindingsHelper);
}
- AnnotationVisitor visitor = optionsDeclaration.tryParseArray(name, v -> {});
+ AnnotationVisitor visitor = optionsDeclaration.tryParseArray(name);
if (visitor != null) {
return visitor;
}
@@ -1186,15 +1187,48 @@
abstract static class Declaration<T> {
abstract String kind();
- abstract boolean isDefault();
+ boolean isDefault() {
+ for (Declaration<?> declaration : declarations()) {
+ if (!declaration.isDefault()) {
+ return false;
+ }
+ }
+ return true;
+ }
+ ;
abstract T getValue();
+ List<Declaration<?>> declarations() {
+ return Collections.emptyList();
+ }
+
boolean tryParse(String name, Object value) {
+ for (Declaration<?> declaration : declarations()) {
+ if (declaration.tryParse(name, value)) {
+ return true;
+ }
+ }
return false;
}
- AnnotationVisitor tryParseArray(String name, Consumer<T> onValue) {
+ AnnotationVisitor tryParseArray(String name) {
+ for (Declaration<?> declaration : declarations()) {
+ AnnotationVisitor visitor = declaration.tryParseArray(name);
+ if (visitor != null) {
+ return visitor;
+ }
+ }
+ return null;
+ }
+
+ AnnotationVisitor tryParseAnnotation(String name, String descriptor) {
+ for (Declaration<?> declaration : declarations()) {
+ AnnotationVisitor visitor = declaration.tryParseAnnotation(name, descriptor);
+ if (visitor != null) {
+ return visitor;
+ }
+ }
return null;
}
}
@@ -1212,6 +1246,10 @@
return null;
}
+ AnnotationVisitor parseAnnotation(String name, String descriptor, Consumer<T> setValue) {
+ return null;
+ }
+
@Override
boolean isDefault() {
return !hasDeclaration();
@@ -1252,8 +1290,22 @@
}
@Override
- AnnotationVisitor tryParseArray(String name, Consumer<T> setValue) {
- AnnotationVisitor visitor = parseArray(name, setValue.andThen(v -> declarationValue = v));
+ final AnnotationVisitor tryParseArray(String name) {
+ AnnotationVisitor visitor = parseArray(name, v -> declarationValue = v);
+ if (visitor != null) {
+ if (hasDeclaration()) {
+ error(name);
+ }
+ declarationName = name;
+ declarationVisitor = visitor;
+ return visitor;
+ }
+ return null;
+ }
+
+ @Override
+ final AnnotationVisitor tryParseAnnotation(String name, String descriptor) {
+ AnnotationVisitor visitor = parseAnnotation(name, descriptor, v -> declarationValue = v);
if (visitor != null) {
if (hasDeclaration()) {
error(name);
@@ -1423,13 +1475,130 @@
}
}
+ private static class MethodReturnTypeDeclaration
+ extends SingleDeclaration<KeepMethodReturnTypePattern> {
+
+ private final Supplier<String> annotationName;
+
+ private MethodReturnTypeDeclaration(Supplier<String> annotationName) {
+ this.annotationName = annotationName;
+ }
+
+ @Override
+ String kind() {
+ return "return type";
+ }
+
+ @Override
+ KeepMethodReturnTypePattern getDefaultValue() {
+ return KeepMethodReturnTypePattern.any();
+ }
+
+ @Override
+ KeepMethodReturnTypePattern parse(String name, Object value) {
+ if (name.equals(Item.methodReturnType) && value instanceof String) {
+ return KeepEdgeReaderUtils.methodReturnTypeFromTypeName((String) value);
+ }
+ if (name.equals(Item.methodReturnTypeConstant) && value instanceof Type) {
+ Type type = (Type) value;
+ return KeepEdgeReaderUtils.methodReturnTypeFromTypeDescriptor(type.getDescriptor());
+ }
+ return null;
+ }
+
+ @Override
+ AnnotationVisitor parseAnnotation(
+ String name, String descriptor, Consumer<KeepMethodReturnTypePattern> setValue) {
+ if (name.equals(Item.methodReturnTypePattern) && descriptor.equals(TypePattern.DESCRIPTOR)) {
+ return new TypePatternVisitor(
+ annotationName, t -> setValue.accept(KeepMethodReturnTypePattern.fromType(t)));
+ }
+ return super.parseAnnotation(name, descriptor, setValue);
+ }
+ }
+
+ private static class MethodParametersDeclaration
+ extends SingleDeclaration<KeepMethodParametersPattern> {
+
+ private final Supplier<String> annotationName;
+ private KeepMethodParametersPattern pattern = null;
+
+ public MethodParametersDeclaration(Supplier<String> annotationName) {
+ this.annotationName = annotationName;
+ }
+
+ private void setPattern(
+ KeepMethodParametersPattern pattern, Consumer<KeepMethodParametersPattern> setValue) {
+ assert setValue != null;
+ if (this.pattern != null) {
+ throw new KeepEdgeException("Cannot declare multiple patterns for the parameter list");
+ }
+ setValue.accept(pattern);
+ this.pattern = pattern;
+ }
+
+ @Override
+ String kind() {
+ return "parameters";
+ }
+
+ @Override
+ KeepMethodParametersPattern getDefaultValue() {
+ return KeepMethodParametersPattern.any();
+ }
+
+ @Override
+ KeepMethodParametersPattern parse(String name, Object value) {
+ return null;
+ }
+
+ @Override
+ AnnotationVisitor parseArray(String name, Consumer<KeepMethodParametersPattern> setValue) {
+ if (name.equals(Item.methodParameters)) {
+ return new StringArrayVisitor(
+ annotationName,
+ params -> {
+ KeepMethodParametersPattern.Builder builder = KeepMethodParametersPattern.builder();
+ for (String param : params) {
+ builder.addParameterTypePattern(KeepEdgeReaderUtils.typePatternFromString(param));
+ }
+ setPattern(builder.build(), setValue);
+ });
+ }
+ if (name.equals(Item.methodParameterTypePatterns)) {
+ return new TypePatternsArrayVisitor(
+ annotationName,
+ params -> {
+ KeepMethodParametersPattern.Builder builder = KeepMethodParametersPattern.builder();
+ for (KeepTypePattern param : params) {
+ builder.addParameterTypePattern(param);
+ }
+ setPattern(builder.build(), setValue);
+ });
+ }
+ return super.parseArray(name, setValue);
+ }
+ }
+
private static class MethodDeclaration extends Declaration<KeepMethodPattern> {
private final Supplier<String> annotationName;
private KeepMethodAccessPattern.Builder accessBuilder = null;
private KeepMethodPattern.Builder builder = null;
+ private final MethodReturnTypeDeclaration returnTypeDeclaration;
+ private final MethodParametersDeclaration parametersDeclaration;
+
+ private final List<Declaration<?>> declarations;
private MethodDeclaration(Supplier<String> annotationName) {
this.annotationName = annotationName;
+ returnTypeDeclaration = new MethodReturnTypeDeclaration(annotationName);
+ parametersDeclaration = new MethodParametersDeclaration(annotationName);
+ declarations = ImmutableList.of(returnTypeDeclaration, parametersDeclaration);
+ }
+
+ @Override
+ List<Declaration<?>> declarations() {
+ return declarations;
}
private KeepMethodPattern.Builder getBuilder() {
@@ -1446,7 +1615,7 @@
@Override
boolean isDefault() {
- return accessBuilder == null && builder == null;
+ return accessBuilder == null && builder == null && super.isDefault();
}
@Override
@@ -1454,6 +1623,12 @@
if (accessBuilder != null) {
getBuilder().setAccessPattern(accessBuilder.build());
}
+ if (!returnTypeDeclaration.isDefault()) {
+ getBuilder().setReturnTypePattern(returnTypeDeclaration.getValue());
+ }
+ if (!parametersDeclaration.isDefault()) {
+ getBuilder().setParametersPattern(parametersDeclaration.getValue());
+ }
return builder != null ? builder.build() : null;
}
@@ -1463,43 +1638,77 @@
getBuilder().setNamePattern(KeepMethodNamePattern.exact((String) value));
return true;
}
- if (name.equals(Item.methodReturnType) && value instanceof String) {
- getBuilder()
- .setReturnTypePattern(KeepEdgeReaderUtils.methodReturnTypeFromString((String) value));
- return true;
- }
- return false;
+ return super.tryParse(name, value);
}
@Override
- AnnotationVisitor tryParseArray(String name, Consumer<KeepMethodPattern> ignored) {
+ AnnotationVisitor tryParseArray(String name) {
if (name.equals(Item.methodAccess)) {
accessBuilder = KeepMethodAccessPattern.builder();
return new MethodAccessVisitor(annotationName, accessBuilder);
}
- if (name.equals(Item.methodParameters)) {
- return new StringArrayVisitor(
- annotationName,
- params -> {
- KeepMethodParametersPattern.Builder builder = KeepMethodParametersPattern.builder();
- for (String param : params) {
- builder.addParameterTypePattern(KeepEdgeReaderUtils.typePatternFromString(param));
- }
- KeepMethodParametersPattern result = builder.build();
- getBuilder().setParametersPattern(result);
- });
+ return super.tryParseArray(name);
+ }
+ }
+
+ private static class FieldTypeDeclaration extends SingleDeclaration<KeepFieldTypePattern> {
+
+ private final Supplier<String> annotationName;
+
+ private FieldTypeDeclaration(Supplier<String> annotationName) {
+ this.annotationName = annotationName;
+ }
+
+ @Override
+ String kind() {
+ return "field type";
+ }
+
+ @Override
+ KeepFieldTypePattern getDefaultValue() {
+ return KeepFieldTypePattern.any();
+ }
+
+ @Override
+ KeepFieldTypePattern parse(String name, Object value) {
+ if (name.equals(Item.fieldType) && value instanceof String) {
+ return KeepFieldTypePattern.fromType(
+ KeepEdgeReaderUtils.typePatternFromString((String) value));
+ }
+ if (name.equals(Item.fieldTypeConstant) && value instanceof Type) {
+ String descriptor = ((Type) value).getDescriptor();
+ return KeepFieldTypePattern.fromType(KeepTypePattern.fromDescriptor(descriptor));
}
return null;
}
+
+ @Override
+ AnnotationVisitor parseAnnotation(
+ String name, String descriptor, Consumer<KeepFieldTypePattern> setValue) {
+ if (name.equals(Item.fieldTypePattern) && descriptor.equals(TypePattern.DESCRIPTOR)) {
+ return new TypePatternVisitor(
+ annotationName, t -> setValue.accept(KeepFieldTypePattern.fromType(t)));
+ }
+ return super.parseAnnotation(name, descriptor, setValue);
+ }
}
private static class FieldDeclaration extends Declaration<KeepFieldPattern> {
private final Supplier<String> annotationName;
+ private final FieldTypeDeclaration typeDeclaration;
private KeepFieldAccessPattern.Builder accessBuilder = null;
private KeepFieldPattern.Builder builder = null;
+ private final List<Declaration<?>> declarations;
public FieldDeclaration(Supplier<String> annotationName) {
this.annotationName = annotationName;
+ typeDeclaration = new FieldTypeDeclaration(annotationName);
+ declarations = Collections.singletonList(typeDeclaration);
+ }
+
+ @Override
+ List<Declaration<?>> declarations() {
+ return declarations;
}
private KeepFieldPattern.Builder getBuilder() {
@@ -1524,6 +1733,9 @@
if (accessBuilder != null) {
getBuilder().setAccessPattern(accessBuilder.build());
}
+ if (!typeDeclaration.isDefault()) {
+ getBuilder().setTypePattern(typeDeclaration.getValue());
+ }
return builder != null ? builder.build() : null;
}
@@ -1533,23 +1745,16 @@
getBuilder().setNamePattern(KeepFieldNamePattern.exact((String) value));
return true;
}
- if (name.equals(Item.fieldType) && value instanceof String) {
- getBuilder()
- .setTypePattern(
- KeepFieldTypePattern.fromType(
- KeepEdgeReaderUtils.typePatternFromString((String) value)));
- return true;
- }
- return false;
+ return super.tryParse(name, value);
}
@Override
- AnnotationVisitor tryParseArray(String name, Consumer<KeepFieldPattern> onValue) {
+ AnnotationVisitor tryParseArray(String name) {
if (name.equals(Item.fieldAccess)) {
accessBuilder = KeepFieldAccessPattern.builder();
return new FieldAccessVisitor(annotationName, accessBuilder);
}
- return super.tryParseArray(name, onValue);
+ return super.tryParseArray(name);
}
}
@@ -1558,11 +1763,18 @@
private KeepMemberAccessPattern.Builder accessBuilder = null;
private final MethodDeclaration methodDeclaration;
private final FieldDeclaration fieldDeclaration;
+ private final List<Declaration<?>> declarations;
MemberDeclaration(Supplier<String> annotationName) {
this.annotationName = annotationName;
methodDeclaration = new MethodDeclaration(annotationName);
fieldDeclaration = new FieldDeclaration(annotationName);
+ declarations = ImmutableList.of(methodDeclaration, fieldDeclaration);
+ }
+
+ @Override
+ List<Declaration<?>> declarations() {
+ return declarations;
}
@Override
@@ -1599,21 +1811,12 @@
}
@Override
- boolean tryParse(String name, Object value) {
- return methodDeclaration.tryParse(name, value) || fieldDeclaration.tryParse(name, value);
- }
-
- @Override
- AnnotationVisitor tryParseArray(String name, Consumer<KeepMemberPattern> ignored) {
+ AnnotationVisitor tryParseArray(String name) {
if (name.equals(Item.memberAccess)) {
accessBuilder = KeepMemberAccessPattern.memberBuilder();
return new MemberAccessVisitor(annotationName, accessBuilder);
}
- AnnotationVisitor visitor = methodDeclaration.tryParseArray(name, v -> {});
- if (visitor != null) {
- return visitor;
- }
- return fieldDeclaration.tryParseArray(name, v -> {});
+ return super.tryParseArray(name);
}
}
@@ -1751,8 +1954,21 @@
}
@Override
+ public AnnotationVisitor visitAnnotation(String name, String descriptor) {
+ AnnotationVisitor visitor = classDeclaration.tryParseAnnotation(name, descriptor);
+ if (visitor != null) {
+ return visitor;
+ }
+ visitor = memberDeclaration.tryParseAnnotation(name, descriptor);
+ if (visitor != null) {
+ return visitor;
+ }
+ return super.visitAnnotation(name, descriptor);
+ }
+
+ @Override
public AnnotationVisitor visitArray(String name) {
- AnnotationVisitor visitor = memberDeclaration.tryParseArray(name, v -> {});
+ AnnotationVisitor visitor = memberDeclaration.tryParseArray(name);
if (visitor != null) {
return visitor;
}
@@ -1923,6 +2139,80 @@
}
}
+ private static class TypePatternVisitor extends AnnotationVisitorBase {
+ private final Supplier<String> annotationName;
+ private final Consumer<KeepTypePattern> consumer;
+ private KeepTypePattern result = null;
+
+ private TypePatternVisitor(
+ Supplier<String> annotationName, Consumer<KeepTypePattern> consumer) {
+ this.annotationName = annotationName;
+ this.consumer = consumer;
+ }
+
+ @Override
+ public String getAnnotationName() {
+ return annotationName.get();
+ }
+
+ private void setResult(KeepTypePattern result) {
+ if (this.result != null) {
+ throw new KeepEdgeException("Invalid type annotation defining multiple properties.");
+ }
+ this.result = result;
+ }
+
+ @Override
+ public void visit(String name, Object value) {
+ if (TypePattern.name.equals(name) && value instanceof String) {
+ setResult(KeepEdgeReaderUtils.typePatternFromString((String) value));
+ return;
+ }
+ if (TypePattern.constant.equals(name) && value instanceof Type) {
+ Type type = (Type) value;
+ setResult(KeepTypePattern.fromDescriptor(type.getDescriptor()));
+ return;
+ }
+ super.visit(name, value);
+ }
+
+ @Override
+ public void visitEnd() {
+ consumer.accept(result != null ? result : KeepTypePattern.any());
+ }
+ }
+
+ private static class TypePatternsArrayVisitor extends AnnotationVisitorBase {
+ private final Supplier<String> annotationName;
+ private final Consumer<List<KeepTypePattern>> fn;
+ private final List<KeepTypePattern> patterns = new ArrayList<>();
+
+ public TypePatternsArrayVisitor(
+ Supplier<String> annotationName, Consumer<List<KeepTypePattern>> fn) {
+ this.annotationName = annotationName;
+ this.fn = fn;
+ }
+
+ @Override
+ public String getAnnotationName() {
+ return annotationName.get();
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String unusedName, String descriptor) {
+ if (TypePattern.DESCRIPTOR.equals(descriptor)) {
+ return new TypePatternVisitor(annotationName, patterns::add);
+ }
+ return null;
+ }
+
+ @Override
+ public void visitEnd() {
+ super.visitEnd();
+ fn.accept(patterns);
+ }
+ }
+
private static class OptionsDeclaration extends SingleDeclaration<KeepOptions> {
private final String annotationName;
@@ -1996,7 +2286,7 @@
@Override
public AnnotationVisitor visitArray(String name) {
- AnnotationVisitor visitor = optionsDeclaration.tryParseArray(name, v -> {});
+ AnnotationVisitor visitor = optionsDeclaration.tryParseArray(name);
if (visitor != null) {
return visitor;
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java
index 7b0cf28..d88b010 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java
@@ -68,10 +68,18 @@
}
}
- public static KeepMethodReturnTypePattern methodReturnTypeFromString(String returnType) {
+ public static KeepMethodReturnTypePattern methodReturnTypeFromTypeName(String returnType) {
if ("void".equals(returnType)) {
return KeepMethodReturnTypePattern.voidType();
}
return KeepMethodReturnTypePattern.fromType(typePatternFromString(returnType));
}
+
+ public static KeepMethodReturnTypePattern methodReturnTypeFromTypeDescriptor(
+ String returnTypeDesc) {
+ if ("V".equals(returnTypeDesc)) {
+ return KeepMethodReturnTypePattern.voidType();
+ }
+ return KeepMethodReturnTypePattern.fromType(KeepTypePattern.fromDescriptor(returnTypeDesc));
+ }
}
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 6ae281b..1ab1561 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
@@ -87,10 +87,15 @@
public static final String methodAccess = "methodAccess";
public static final String methodName = "methodName";
public static final String methodReturnType = "methodReturnType";
+ public static final String methodReturnTypeConstant = "methodReturnTypeConstant";
+ public static final String methodReturnTypePattern = "methodReturnTypePattern";
public static final String methodParameters = "methodParameters";
+ public static final String methodParameterTypePatterns = "methodParameterTypePatterns";
public static final String fieldAccess = "fieldAccess";
public static final String fieldName = "fieldName";
public static final String fieldType = "fieldType";
+ public static final String fieldTypeConstant = "fieldTypeConstant";
+ public static final String fieldTypePattern = "fieldTypePattern";
}
public static final class Binding {
@@ -191,4 +196,12 @@
public static final String VOLATILE = "VOLATILE";
public static final String TRANSIENT = "TRANSIENT";
}
+
+ public static final class TypePattern {
+ public static final String SIMPLE_NAME = "TypePattern";
+ public static final String DESCRIPTOR =
+ "Lcom/android/tools/r8/keepanno/annotations/TypePattern;";
+ public static final String name = "name";
+ public static final String constant = "constant";
+ }
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodParametersPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodParametersPattern.java
index aec5fbf..2a7061d 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodParametersPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodParametersPattern.java
@@ -72,12 +72,11 @@
}
@Override
- @SuppressWarnings("EqualsGetClass")
public boolean equals(Object o) {
if (this == o) {
return true;
}
- if (o == null || getClass() != o.getClass()) {
+ if (!(o instanceof Some)) {
return false;
}
diff --git a/src/test/java/com/android/tools/r8/keepanno/FieldPatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/FieldPatternsTest.java
new file mode 100644
index 0000000..4f26412
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/FieldPatternsTest.java
@@ -0,0 +1,114 @@
+// Copyright (c) 2023, 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.keepanno;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.keepanno.annotations.KeepConstraint;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.TypePattern;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.lang.reflect.Field;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class FieldPatternsTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("Hello, world", "42", A.class.toString());
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+ }
+
+ public FieldPatternsTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(getInputClasses())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testWithRuleExtraction() throws Exception {
+ testForR8(parameters.getBackend())
+ .enableExperimentalKeepAnnotations()
+ .addProgramClasses(getInputClasses())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkOutput);
+ }
+
+ public List<Class<?>> getInputClasses() {
+ return ImmutableList.of(TestClass.class, A.class);
+ }
+
+ private void checkOutput(CodeInspector inspector) {
+ assertThat(inspector.clazz(A.class), isPresent());
+ assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("fieldA"), isPresent());
+ assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("fieldB"), isAbsent());
+ assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("fieldC"), isPresent());
+ assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("fieldD"), isPresent());
+ }
+
+ static class A {
+
+ public String fieldA = "Hello, world";
+ public Integer fieldB = 42; // Not used or kept.
+ public int fieldC = 42;
+ public Object fieldD = A.class;
+
+ @UsesReflection({
+ @KeepTarget(classConstant = A.class),
+ @KeepTarget(
+ classConstant = A.class,
+ fieldTypePattern = @TypePattern(name = "java.lang.String"),
+ constraints = {KeepConstraint.LOOKUP, KeepConstraint.FIELD_GET}),
+ @KeepTarget(
+ classConstant = A.class,
+ fieldTypeConstant = Object.class,
+ constraints = {KeepConstraint.LOOKUP, KeepConstraint.FIELD_GET}),
+ @KeepTarget(
+ classConstant = A.class,
+ fieldTypePattern = @TypePattern(constant = int.class),
+ constraints = {KeepConstraint.LOOKUP, KeepConstraint.FIELD_GET})
+ })
+ public void foo() throws Exception {
+ for (Field field : getClass().getDeclaredFields()) {
+ if (field.getType().equals(String.class)
+ || field.getType().equals(Object.class)
+ || field.getType().equals(int.class)) {
+ System.out.println(field.get(this));
+ }
+ }
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) throws Exception {
+ new A().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/MethodPatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/MethodPatternsTest.java
new file mode 100644
index 0000000..86f2f9c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/MethodPatternsTest.java
@@ -0,0 +1,156 @@
+// Copyright (c) 2023, 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.keepanno;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.TypePattern;
+import com.android.tools.r8.keepanno.annotations.UsedByReflection;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.lang.reflect.Method;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MethodPatternsTest extends TestBase {
+
+ static final String EXPECTED =
+ StringUtils.lines("Hello 42", "Hello 12", "int", "long", "class java.lang.Integer");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+ }
+
+ public MethodPatternsTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(getInputClasses())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .enableExperimentalKeepAnnotations()
+ .addProgramClasses(getInputClasses())
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkOutput);
+ }
+
+ public List<Class<?>> getInputClasses() {
+ return ImmutableList.of(TestClass.class, A.class, B.class);
+ }
+
+ private void checkOutput(CodeInspector inspector) {
+ assertThat(inspector.clazz(B.class), isPresentAndRenamed());
+ assertThat(inspector.clazz(B.class).method("void", "bar"), isAbsent());
+ assertThat(inspector.clazz(B.class).method("void", "bar", "int"), isPresent());
+ assertThat(inspector.clazz(B.class).method("void", "bar", "int", "int"), isPresent());
+ assertThat(
+ inspector.clazz(B.class).method("void", "bar", "java.lang.Object", "java.lang.Object"),
+ isAbsent());
+ assertThat(
+ inspector.clazz(B.class).method("int", "bar", "int", "long", "java.lang.Integer"),
+ isPresent());
+ assertThat(
+ inspector.clazz(B.class).method("int", "bar", "int", "long", "java.lang.Integer", "int"),
+ isAbsent());
+ }
+
+ static class A {
+
+ @UsesReflection({
+ @KeepTarget(
+ classConstant = B.class,
+ methodName = "bar",
+ methodReturnTypePattern = @TypePattern(name = "void"),
+ methodParameterTypePatterns = {@TypePattern(constant = int.class)}),
+ @KeepTarget(
+ classConstant = B.class,
+ methodName = "bar",
+ methodReturnTypeConstant = void.class,
+ methodParameterTypePatterns = {
+ @TypePattern(constant = int.class),
+ @TypePattern(name = "int")
+ }),
+ @KeepTarget(
+ classConstant = B.class,
+ methodName = "bar",
+ methodReturnTypeConstant = int.class,
+ methodParameterTypePatterns = {@TypePattern, @TypePattern, @TypePattern}),
+ })
+ public void foo() throws Exception {
+ // Invoke the first and second method.
+ B.class.getDeclaredMethod("bar", int.class).invoke(null, 42);
+ B.class.getDeclaredMethod("bar", int.class, int.class).invoke(null, 1, 2);
+ // Print args of third method.
+ for (Method method : B.class.getDeclaredMethods()) {
+ if (method.getReturnType().equals(int.class) && method.getParameterCount() == 3) {
+ for (Class<?> type : method.getParameterTypes()) {
+ System.out.println(type);
+ }
+ }
+ }
+ }
+ }
+
+ static class B {
+ public static void bar() {
+ throw new RuntimeException("UNUSED");
+ }
+
+ public static void bar(int value) {
+ System.out.println("Hello " + value);
+ }
+
+ public static void bar(int value1, int value2) {
+ System.out.println("Hello " + value1 + value2);
+ }
+
+ public static void bar(Object value1, Object value2) {
+ throw new RuntimeException("UNUSED");
+ }
+
+ public static int bar(int value1, long value2, Integer value3) {
+ System.out.println("Hello " + value1 + value2 + value3);
+ return value1 + (int) value2 + value3;
+ }
+
+ public static int bar(int value1, long value2, Integer value3, int value4) {
+ throw new RuntimeException("UNUSED");
+ }
+ }
+
+ static class TestClass {
+
+ @UsedByReflection(kind = KeepItemKind.CLASS_AND_METHODS)
+ public static void main(String[] args) throws Exception {
+ new A().foo();
+ }
+ }
+}
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 3e334a3..bd3d82f 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
@@ -20,6 +20,7 @@
import com.android.tools.r8.keepanno.annotations.KeepTarget;
import com.android.tools.r8.keepanno.annotations.MemberAccessFlags;
import com.android.tools.r8.keepanno.annotations.MethodAccessFlags;
+import com.android.tools.r8.keepanno.annotations.TypePattern;
import com.android.tools.r8.keepanno.annotations.UsedByNative;
import com.android.tools.r8.keepanno.annotations.UsedByReflection;
import com.android.tools.r8.keepanno.annotations.UsesReflection;
@@ -42,7 +43,6 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.function.Consumer;
-import org.jetbrains.annotations.NotNull;
public class KeepItemAnnotationGenerator {
@@ -334,6 +334,38 @@
.defaultEmptyArray("KeepTarget"));
}
+ private Group typePatternGroup() {
+ return new Group("type-pattern")
+ .addMember(
+ new GroupMember("name")
+ .setDocTitle("Exact type name as a string.")
+ .addParagraph("For example, {@code \"long\"} or {@code \"java.lang.String\"}.")
+ .defaultEmptyString())
+ .addMember(
+ new GroupMember("constant")
+ .setDocTitle("Exact type from a class constant.")
+ .addParagraph("For example, {@code String.class}.")
+ .defaultObjectClass());
+ // 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 "";
+ //
+ // /** Exact type as a class constant to match any array with that type as member. */
+ // Class<?> arrayOfConstant() default TypePattern.class;
+ //
+ // /** If true, the pattern matches any primitive type. Such as, boolean, int, etc. */
+ // boolean anyPrimitive() default false;
+ //
+ // /** If true, the pattern matches any array type. */
+ // boolean anyArray() default false;
+ //
+ // /** If true, the pattern matches any class type. */
+ // boolean anyClass() default false;
+ //
+ // /** If true, the pattern matches any reference type, namely: arrays or classes. */
+ // boolean anyReference() default false;
+ }
+
private Group getKindGroup() {
return new Group(KIND_GROUP).addMember(getKindMember());
}
@@ -396,7 +428,6 @@
return StringUtils.join(", ", values, v -> docLink(v), BraceType.TUBORG);
}
- @NotNull
private static GroupMember constraints() {
return new GroupMember("constraints")
.setDocTitle("Define the usage constraints of the target.")
@@ -627,7 +658,22 @@
.addParagraph(getMutuallyExclusiveForMethodProperties())
.addParagraph(getMethodDefaultDoc("any return type"))
.setDocReturn("The qualified type name of the method return type.")
- .defaultEmptyString());
+ .defaultEmptyString())
+ .addMember(
+ new GroupMember("methodReturnTypeConstant")
+ .setDocTitle("Define the method return-type pattern by a class constant.")
+ .addParagraph(getMutuallyExclusiveForMethodProperties())
+ .addParagraph(getMethodDefaultDoc("any return type"))
+ .setDocReturn("A class constant denoting the type of the method return type.")
+ .defaultObjectClass())
+ .addMember(
+ new GroupMember("methodReturnTypePattern")
+ .setDocTitle("Define the method return-type pattern by a type pattern.")
+ .addParagraph(getMutuallyExclusiveForMethodProperties())
+ .addParagraph(getMethodDefaultDoc("any return type"))
+ .setDocReturn("The pattern of the method return type.")
+ .defaultType("TypePattern")
+ .defaultValue("@TypePattern(name = \"\")"));
}
private Group createMethodParametersGroup() {
@@ -640,7 +686,16 @@
.addParagraph(getMethodDefaultDoc("any parameters"))
.setDocReturn("The list of qualified type names of the method parameters.")
.defaultType("String[]")
- .defaultValue("{\"<default>\"}"));
+ .defaultValue("{\"\"}"))
+ .addMember(
+ new GroupMember("methodParameterTypePatterns")
+ .setDocTitle(
+ "Define the method parameters pattern by a list of patterns on types.")
+ .addParagraph(getMutuallyExclusiveForMethodProperties())
+ .addParagraph(getMethodDefaultDoc("any parameters"))
+ .setDocReturn("The list of type patterns for the method parameters.")
+ .defaultType("TypePattern[]")
+ .defaultValue("{@TypePattern(name = \"\")}"));
}
private Group createFieldAccessGroup() {
@@ -672,8 +727,23 @@
.setDocTitle("Define the field-type pattern by a fully qualified type.")
.addParagraph(getMutuallyExclusiveForFieldProperties())
.addParagraph(getFieldDefaultDoc("any type"))
- .setDocReturn("The qualified type name of the field type.")
- .defaultEmptyString());
+ .setDocReturn("The qualified type name for the field type.")
+ .defaultEmptyString())
+ .addMember(
+ new GroupMember("fieldTypeConstant")
+ .setDocTitle("Define the field-type pattern by a class constant.")
+ .addParagraph(getMutuallyExclusiveForFieldProperties())
+ .addParagraph(getFieldDefaultDoc("any type"))
+ .setDocReturn("The class constant for the field type.")
+ .defaultObjectClass())
+ .addMember(
+ new GroupMember("fieldTypePattern")
+ .setDocTitle("Define the field-type pattern by a pattern on types.")
+ .addParagraph(getMutuallyExclusiveForFieldProperties())
+ .addParagraph(getFieldDefaultDoc("any type"))
+ .setDocReturn("The type pattern for the field type.")
+ .defaultType("TypePattern")
+ .defaultValue("@TypePattern(name = \"\")"));
}
private void generateClassAndMemberPropertiesWithClassAndMemberBinding() {
@@ -733,6 +803,24 @@
createFieldTypeGroup().generate(this);
}
+ private void generateTypePattern() {
+ printCopyRight(2023);
+ printPackage("annotations");
+ printImports(ANNOTATION_IMPORTS);
+ DocPrinter.printer()
+ .setDocTitle("A pattern structure for matching types.")
+ .addParagraph("If no properties are set, the default pattern matches any type.")
+ .addParagraph("All properties on this annotation are mutually exclusive.")
+ .printDoc(this::println);
+ println("@Target(ElementType.ANNOTATION_TYPE)");
+ println("@Retention(RetentionPolicy.CLASS)");
+ println("public @interface TypePattern {");
+ println();
+ withIndent(() -> typePatternGroup().generate(this));
+ println();
+ println("}");
+ }
+
private void generateKeepBinding() {
printCopyRight(2022);
printPackage("annotations");
@@ -1061,6 +1149,8 @@
generateMemberAccessConstants();
generateMethodAccessConstants();
generateFieldAccessConstants();
+
+ generateTypePatternConstants();
});
println("}");
}
@@ -1330,6 +1420,17 @@
println();
}
+ private void generateTypePatternConstants() {
+ println("public static final class TypePattern {");
+ withIndent(
+ () -> {
+ generateAnnotationConstants(TypePattern.class);
+ typePatternGroup().generateConstants(this);
+ });
+ println("}");
+ println();
+ }
+
private static void writeFile(Path file, Consumer<Generator> fn) throws IOException {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(byteStream);
@@ -1351,6 +1452,7 @@
writeFile(astPkg.resolve("AnnotationConstants.java"), Generator::generateConstants);
Path annoPkg = Paths.get("src/keepanno/java/com/android/tools/r8/keepanno/annotations");
+ writeFile(annoPkg.resolve("TypePattern.java"), Generator::generateTypePattern);
writeFile(annoPkg.resolve("KeepBinding.java"), Generator::generateKeepBinding);
writeFile(annoPkg.resolve("KeepTarget.java"), Generator::generateKeepTarget);
writeFile(annoPkg.resolve("KeepCondition.java"), Generator::generateKeepCondition);