[KeepAnno] Update user guide
This adds a dedicated section on constraints and removes the use of
constraints from the other sections. The constraints cover the defaults,
and signatures (annotations are already covered).
Change-Id: I8e0904cee29a74cdf6a8f81efb77a6de7f93aa31
diff --git a/doc/keepanno-guide.md b/doc/keepanno-guide.md
index c924bdcf..253a756 100644
--- a/doc/keepanno-guide.md
+++ b/doc/keepanno-guide.md
@@ -26,6 +26,9 @@
- [Accessing annotations](#using-reflection-annotations)
- [Annotating code used by reflection (or via JNI)](#used-by-reflection)
- [Annotating APIs](#apis)
+- [Constraints](#constraints)
+ - [Defaults](#constraints-defaults)
+ - [Generic signatures](#constraints-signatures)
- [Migrating rules to annotations](#migrating-rules)
- [My use case is not covered!](#other-uses)
- [Troubleshooting](#troubleshooting)
@@ -104,12 +107,11 @@
```
public class MyHiddenMethodCaller {
- @UsesReflection({
- @KeepTarget(
- instanceOfClassConstant = BaseClass.class,
- methodName = "hiddenMethod",
- methodParameters = {})
- })
+ @UsesReflection(
+ @KeepTarget(
+ instanceOfClassConstant = BaseClass.class,
+ methodName = "hiddenMethod",
+ methodParameters = {}))
public void callHiddenMethod(BaseClass base) throws Exception {
base.getClass().getDeclaredMethod("hiddenMethod").invoke(base);
}
@@ -128,21 +130,16 @@
of the object.
The [@KeepTarget](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepTarget.html) describes these field targets. Since the printing only cares about preserving
-the fields, the [@KeepTarget.kind](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepTarget.html#kind()) is set to [KeepItemKind.ONLY_FIELDS](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepItemKind.html#ONLY_FIELDS). Also, since printing
-the field names and values only requires looking up the field, printing its name and getting
-its value the [@KeepTarget.constraints](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepTarget.html#constraints()) are set to just [KeepConstraint.LOOKUP](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepConstraint.html#LOOKUP),
-[KeepConstraint.NAME](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepConstraint.html#NAME) and [KeepConstraint.FIELD_GET](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepConstraint.html#FIELD_GET).
+the fields, the [@KeepTarget.kind](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepTarget.html#kind()) is set to [KeepItemKind.ONLY_FIELDS](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepItemKind.html#ONLY_FIELDS).
```
-public class MyFieldValuePrinter {
+static class MyFieldValuePrinter {
- @UsesReflection({
- @KeepTarget(
- instanceOfClassConstant = PrintableFieldInterface.class,
- kind = KeepItemKind.ONLY_FIELDS,
- constraints = {KeepConstraint.LOOKUP, KeepConstraint.NAME, KeepConstraint.FIELD_GET})
- })
+ @UsesReflection(
+ @KeepTarget(
+ instanceOfClassConstant = PrintableFieldInterface.class,
+ kind = KeepItemKind.ONLY_FIELDS))
public void printFieldValues(PrintableFieldInterface objectWithFields) throws Exception {
for (Field field : objectWithFields.getClass().getDeclaredFields()) {
System.out.println(field.getName() + " = " + field.get(objectWithFields));
@@ -241,9 +238,6 @@
field-printing utility of the library. Since the library is reflectively accessing each field
we annotate them with the [@UsedByReflection](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/UsedByReflection.html) annotation.
-We could additionally add the [@UsedByReflection.constraints](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/UsedByReflection.html#constraints()) property as we did previously.
-We elide it here for brevity.
-
```
public class MyClassWithFields implements PrintableFieldInterface {
@@ -263,15 +257,10 @@
used reflectively. In particular, the "field printer" example we are considering here does not
make reflective assumptions about the holder class, so we should not constrain it.
-To be more precise let's add the [@UsedByReflection.constraints](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/UsedByReflection.html#constraints()) property now. This specifies
-that the fields are looked up, their names are used/assumed and their values are read.
-
```
-@UsedByReflection(
- kind = KeepItemKind.ONLY_FIELDS,
- constraints = {KeepConstraint.LOOKUP, KeepConstraint.NAME, KeepConstraint.FIELD_GET})
-public class MyClassWithFields implements PrintableFieldInterface {
+@UsedByReflection(kind = KeepItemKind.ONLY_FIELDS) public class MyClassWithFields
+ implements PrintableFieldInterface {
final int intField = 42;
String stringField = "Hello!";
}
@@ -296,9 +285,8 @@
classConstant = FieldValuePrinterLibrary.class,
methodName = "printFieldValues")
},
- kind = KeepItemKind.ONLY_FIELDS,
- constraints = {KeepConstraint.LOOKUP, KeepConstraint.NAME, KeepConstraint.FIELD_GET})
-public class MyClassWithFields implements PrintableFieldInterface {
+ kind = KeepItemKind.ONLY_FIELDS) public class MyClassWithFields
+ implements PrintableFieldInterface {
final int intField = 42;
String stringField = "Hello!";
}
@@ -308,9 +296,8 @@
## Annotating APIs<a name="apis"></a>
-If your code is being shrunk before release as a library, or if you have an API
-surface that is used via dynamic loading at runtime, then you need to keep the
-API surface. For that you should use the [@KeepForApi](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepForApi.html) annotation.
+If your code is being shrunk before release as a library, then you need to keep
+the API surface. For that you should use the [@KeepForApi](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepForApi.html) annotation.
When annotating a class the default for [@KeepForApi](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepForApi.html) is to keep the class as well as all of its
public and protected members:
@@ -372,6 +359,92 @@
+## Constraints<a name="constraints"></a>
+
+When an item is kept (e.g., items matched by [@KeepTarget](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepTarget.html) or annotated by
+[@UsedByReflection](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/UsedByReflection.html) or [@KeepForApi](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepForApi.html)) you can additionally specify constraints
+about what properties of that item must be kept. Typical constraints are to keep
+the items *name* or its ability to be reflectively *looked up*. You may also be
+interested in keeping the generic signature of an item or annotations associated
+with it.
+
+### Defaults<a name="constraints-defaults"></a>
+
+By default the constraints are to retain the item's name, its ability to be
+looked-up as well as its normal usage. Its normal usage is:
+
+- to be instantiated, for class items;
+- to be invoked, for method items; and
+- to be get and/or set, for field items.
+
+Let us revisit the example reflectively accessing the fields on a class.
+
+Notice that printing the field names and values only requires looking up the field, printing
+its name and getting its value. It does not require setting a new value on the field.
+We can thus use a more restrictive set of constraints
+by setting the [@KeepTarget.constraints](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepTarget.html#constraints()) property to just [KeepConstraint.LOOKUP](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepConstraint.html#LOOKUP),
+[KeepConstraint.NAME](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepConstraint.html#NAME) and [KeepConstraint.FIELD_GET](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepConstraint.html#FIELD_GET).
+
+
+```
+static class MyFieldValuePrinter {
+
+ @UsesReflection(
+ @KeepTarget(
+ instanceOfClassConstant = PrintableFieldInterface.class,
+ kind = KeepItemKind.ONLY_FIELDS,
+ constraints = {KeepConstraint.LOOKUP, KeepConstraint.NAME, KeepConstraint.FIELD_GET}))
+ public void printFieldValues(PrintableFieldInterface objectWithFields) throws Exception {
+ for (Field field : objectWithFields.getClass().getDeclaredFields()) {
+ System.out.println(field.getName() + " = " + field.get(objectWithFields));
+ }
+ }
+}
+```
+
+
+
+### Generic signatures<a name="constraints-signatures"></a>
+
+The generic signature information of an item is not kept by default, and
+requires adding constraints to the targeted items.
+
+Imagine we had code that is making use of the template parameters for implementations of a
+generic interface. The code below assumes direct implementations of the `WrappedValue` interface
+and simply prints the type parameter used.
+
+Since we are reflecting on the class structure of implementations of `WrappedValue` we need to
+keep it and any instance of it.
+
+We must also preserve the generic signatures of these classes. We add the
+[KeepConstraint.GENERIC_SIGNATURE](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepConstraint.html#GENERIC_SIGNATURE) constraint by using the [@KeepTarget.constraintAdditions](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepTarget.html#constraintAdditions())
+property. This ensures that the default constraints are still in place in addition to the
+constraint on generic signatures.
+
+
+```
+public class GenericSignaturePrinter {
+
+ interface WrappedValue<T> {
+ T getValue();
+ }
+
+ @UsesReflection(
+ @KeepTarget(
+ instanceOfClassConstant = WrappedValue.class,
+ constraintAdditions = KeepConstraint.GENERIC_SIGNATURE))
+ public static void printSignature(WrappedValue<?> obj) {
+ Class<? extends WrappedValue> clazz = obj.getClass();
+ for (Type iface : clazz.getGenericInterfaces()) {
+ String typeName = iface.getTypeName();
+ String param = typeName.substring(typeName.lastIndexOf('<') + 1, typeName.lastIndexOf('>'));
+ System.out.println(clazz.getName() + " uses type " + param);
+ }
+ }
+```
+
+
+
## Migrating rules to annotations<a name="migrating-rules"></a>
There is no automatic migration of keep rules. Keep annotations often invert the
diff --git a/doc/keepanno-guide.template.md b/doc/keepanno-guide.template.md
index 54d910c..da018b3 100644
--- a/doc/keepanno-guide.template.md
+++ b/doc/keepanno-guide.template.md
@@ -121,9 +121,8 @@
## [Annotating APIs](apis)
-If your code is being shrunk before release as a library, or if you have an API
-surface that is used via dynamic loading at runtime, then you need to keep the
-API surface. For that you should use the `@KeepForApi` annotation.
+If your code is being shrunk before release as a library, then you need to keep
+the API surface. For that you should use the `@KeepForApi` annotation.
[[[INCLUDE DOC:ApiClass]]]
@@ -138,6 +137,39 @@
[[[INCLUDE CODE:ApiMember]]]
+## [Constraints](constraints)
+
+When an item is kept (e.g., items matched by `@KeepTarget` or annotated by
+`@UsedByReflection` or `@KeepForApi`) you can additionally specify constraints
+about what properties of that item must be kept. Typical constraints are to keep
+the items *name* or its ability to be reflectively *looked up*. You may also be
+interested in keeping the generic signature of an item or annotations associated
+with it.
+
+### [Defaults](constraints-defaults)
+
+By default the constraints are to retain the item's name, its ability to be
+looked-up as well as its normal usage. Its normal usage is:
+
+- to be instantiated, for class items;
+- to be invoked, for method items; and
+- to be get and/or set, for field items.
+
+[[[INCLUDE DOC:UsesReflectionFieldPrinterWithConstraints]]]
+
+[[[INCLUDE CODE:UsesReflectionFieldPrinterWithConstraints]]]
+
+
+### [Generic signatures](constraints-signatures)
+
+The generic signature information of an item is not kept by default, and
+requires adding constraints to the targeted items.
+
+[[[INCLUDE DOC:GenericSignaturePrinter]]]
+
+[[[INCLUDE CODE:GenericSignaturePrinter]]]
+
+
## [Migrating rules to annotations](migrating-rules)
There is no automatic migration of keep rules. Keep annotations often invert the
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java
index 516cccd..cf7edc4 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java
@@ -70,6 +70,11 @@
public abstract KeepAnnoTestBuilder addProgramFiles(List<Path> programFiles) throws IOException;
+ public final KeepAnnoTestBuilder addProgramClasses(Class<?>... programClasses)
+ throws IOException {
+ return addProgramClasses(Arrays.asList(programClasses));
+ }
+
public abstract KeepAnnoTestBuilder addProgramClasses(List<Class<?>> programClasses)
throws IOException;
diff --git a/src/test/java/com/android/tools/r8/keepanno/doctests/GenericSignaturePrinter.java b/src/test/java/com/android/tools/r8/keepanno/doctests/GenericSignaturePrinter.java
new file mode 100644
index 0000000..c81a27f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/doctests/GenericSignaturePrinter.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2024, 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.doctests;
+
+import com.android.tools.r8.keepanno.annotations.KeepConstraint;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.UsedByReflection;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
+import java.lang.reflect.Type;
+
+/* INCLUDE DOC: GenericSignaturePrinter
+
+Imagine we had code that is making use of the template parameters for implementations of a
+generic interface. The code below assumes direct implementations of the `WrappedValue` interface
+and simply prints the type parameter used.
+
+Since we are reflecting on the class structure of implementations of `WrappedValue` we need to
+keep it and any instance of it.
+
+We must also preserve the generic signatures of these classes. We add the
+`@KeepConstraint#GENERIC_SIGNATURE` constraint by using the `@KeepTarget#constraintAdditions`
+property. This ensures that the default constraints are still in place in addition to the
+constraint on generic signatures.
+
+INCLUDE END */
+
+// INCLUDE CODE: GenericSignaturePrinter
+public class GenericSignaturePrinter {
+
+ interface WrappedValue<T> {
+ T getValue();
+ }
+
+ @UsesReflection(
+ @KeepTarget(
+ instanceOfClassConstant = WrappedValue.class,
+ constraintAdditions = KeepConstraint.GENERIC_SIGNATURE))
+ public static void printSignature(WrappedValue<?> obj) {
+ Class<? extends WrappedValue> clazz = obj.getClass();
+ for (Type iface : clazz.getGenericInterfaces()) {
+ String typeName = iface.getTypeName();
+ String param = typeName.substring(typeName.lastIndexOf('<') + 1, typeName.lastIndexOf('>'));
+ System.out.println(clazz.getName() + " uses type " + param);
+ }
+ }
+
+ // INCLUDE END
+
+ static class MyBool implements WrappedValue<Boolean> {
+
+ private boolean value;
+
+ public MyBool(boolean value) {
+ this.value = value;
+ }
+
+ @Override
+ public Boolean getValue() {
+ return value;
+ }
+ }
+
+ static class MyString implements WrappedValue<String> {
+ private String value;
+
+ public MyString(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String getValue() {
+ return value;
+ }
+ }
+
+ static class TestClass {
+
+ @UsedByReflection(kind = KeepItemKind.CLASS_AND_METHODS)
+ public static void main(String[] args) throws Exception {
+ GenericSignaturePrinter.printSignature(new MyString("foo"));
+ GenericSignaturePrinter.printSignature(new MyBool(true));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/doctests/SignatureConstraintsDocumentationTest.java b/src/test/java/com/android/tools/r8/keepanno/doctests/SignatureConstraintsDocumentationTest.java
new file mode 100644
index 0000000..40853dd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/doctests/SignatureConstraintsDocumentationTest.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2024, 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.doctests;
+
+import com.android.tools.r8.keepanno.KeepAnnoParameters;
+import com.android.tools.r8.keepanno.KeepAnnoTestBase;
+import com.android.tools.r8.keepanno.doctests.GenericSignaturePrinter.TestClass;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+
+@RunWith(Parameterized.class)
+public class SignatureConstraintsDocumentationTest extends KeepAnnoTestBase {
+
+ static final String EXPECTED =
+ StringUtils.lines(
+ "com.android.tools.r8.keepanno.doctests.SignaturePrinter$MyString uses type"
+ + " java.lang.String",
+ "com.android.tools.r8.keepanno.doctests.SignaturePrinter$MyBool uses type"
+ + " java.lang.Boolean");
+
+ @Parameter public KeepAnnoParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static List<KeepAnnoParameters> data() {
+ return createParameters(
+ getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForKeepAnno(parameters)
+ .addProgramClasses(GenericSignaturePrinter.class)
+ .addInnerClasses(GenericSignaturePrinter.class)
+ .run(TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/doctests/UsesReflectionDocumentationTest.java b/src/test/java/com/android/tools/r8/keepanno/doctests/UsesReflectionDocumentationTest.java
index 98a2f0d..95a8d5b 100644
--- a/src/test/java/com/android/tools/r8/keepanno/doctests/UsesReflectionDocumentationTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/doctests/UsesReflectionDocumentationTest.java
@@ -34,6 +34,7 @@
EXPECTED_FIELD_EXAMPLE,
EXPECTED_FIELD_EXAMPLE,
EXPECTED_FIELD_EXAMPLE,
+ EXPECTED_FIELD_EXAMPLE,
EXPECTED_FIELD_EXAMPLE);
private final TestParameters parameters;
@@ -70,7 +71,12 @@
public List<Class<?>> getExampleClasses() {
return ImmutableList.of(
- Example1.class, Example2.class, Example3.class, Example4.class, Example5.class);
+ Example1.class,
+ Example2.class,
+ Example2WithConstraints.class,
+ Example3.class,
+ Example4.class,
+ Example5.class);
}
static class Example1 {
@@ -105,12 +111,11 @@
// INCLUDE CODE: UsesReflectionOnVirtualMethod
public class MyHiddenMethodCaller {
- @UsesReflection({
- @KeepTarget(
- instanceOfClassConstant = BaseClass.class,
- methodName = "hiddenMethod",
- methodParameters = {})
- })
+ @UsesReflection(
+ @KeepTarget(
+ instanceOfClassConstant = BaseClass.class,
+ methodName = "hiddenMethod",
+ methodParameters = {}))
public void callHiddenMethod(BaseClass base) throws Exception {
base.getClass().getDeclaredMethod("hiddenMethod").invoke(base);
}
@@ -142,22 +147,59 @@
of the object.
The `@KeepTarget` describes these field targets. Since the printing only cares about preserving
- the fields, the `@KeepTarget#kind` is set to `@KeepItemKind#ONLY_FIELDS`. Also, since printing
- the field names and values only requires looking up the field, printing its name and getting
- its value the `@KeepTarget#constraints` are set to just `@KeepConstraint#LOOKUP`,
+ the fields, the `@KeepTarget#kind` is set to `@KeepItemKind#ONLY_FIELDS`.
+ INCLUDE END */
+
+ public
+ // INCLUDE CODE: UsesReflectionFieldPrinter
+ static class MyFieldValuePrinter {
+
+ @UsesReflection(
+ @KeepTarget(
+ instanceOfClassConstant = PrintableFieldInterface.class,
+ kind = KeepItemKind.ONLY_FIELDS))
+ public void printFieldValues(PrintableFieldInterface objectWithFields) throws Exception {
+ for (Field field : objectWithFields.getClass().getDeclaredFields()) {
+ System.out.println(field.getName() + " = " + field.get(objectWithFields));
+ }
+ }
+ }
+
+ // INCLUDE END
+
+ static void run() throws Exception {
+ new MyFieldValuePrinter().printFieldValues(new ClassWithFields());
+ }
+ }
+
+ static class Example2WithConstraints {
+
+ interface PrintableFieldInterface {}
+
+ static class ClassWithFields implements PrintableFieldInterface {
+ final int intField = 42;
+ String stringField = "Hello!";
+ }
+
+ /* INCLUDE DOC: UsesReflectionFieldPrinterWithConstraints
+ Let us revisit the example reflectively accessing the fields on a class.
+
+ Notice that printing the field names and values only requires looking up the field, printing
+ its name and getting its value. It does not require setting a new value on the field.
+ We can thus use a more restrictive set of constraints
+ by setting the `@KeepTarget#constraints` property to just `@KeepConstraint#LOOKUP`,
`@KeepConstraint#NAME` and `@KeepConstraint#FIELD_GET`.
INCLUDE END */
- static
- // INCLUDE CODE: UsesReflectionFieldPrinter
- public class MyFieldValuePrinter {
+ public
+ // INCLUDE CODE: UsesReflectionFieldPrinterWithConstraints
+ static class MyFieldValuePrinter {
- @UsesReflection({
- @KeepTarget(
- instanceOfClassConstant = PrintableFieldInterface.class,
- kind = KeepItemKind.ONLY_FIELDS,
- constraints = {KeepConstraint.LOOKUP, KeepConstraint.NAME, KeepConstraint.FIELD_GET})
- })
+ @UsesReflection(
+ @KeepTarget(
+ instanceOfClassConstant = PrintableFieldInterface.class,
+ kind = KeepItemKind.ONLY_FIELDS,
+ constraints = {KeepConstraint.LOOKUP, KeepConstraint.NAME, KeepConstraint.FIELD_GET}))
public void printFieldValues(PrintableFieldInterface objectWithFields) throws Exception {
for (Field field : objectWithFields.getClass().getDeclaredFields()) {
System.out.println(field.getName() + " = " + field.get(objectWithFields));
@@ -182,9 +224,6 @@
In this example, the `MyClassWithFields` is a class you are passing to the
field-printing utility of the library. Since the library is reflectively accessing each field
we annotate them with the `@UsedByReflection` annotation.
-
- We could additionally add the `@UsedByReflection#constraints` property as we did previously.
- We elide it here for brevity.
INCLUDE END */
static
@@ -220,17 +259,12 @@
similar to the `@KeepTarget`. The `@UsedByReflection#kind` specifies that only the fields are
used reflectively. In particular, the "field printer" example we are considering here does not
make reflective assumptions about the holder class, so we should not constrain it.
-
- To be more precise let's add the `@UsedByReflection#constraints` property now. This specifies
- that the fields are looked up, their names are used/assumed and their values are read.
INCLUDE END */
static
// INCLUDE CODE: UsedByReflectionFieldPrinterOnClass
- @UsedByReflection(
- kind = KeepItemKind.ONLY_FIELDS,
- constraints = {KeepConstraint.LOOKUP, KeepConstraint.NAME, KeepConstraint.FIELD_GET})
- public class MyClassWithFields implements PrintableFieldInterface {
+ @UsedByReflection(kind = KeepItemKind.ONLY_FIELDS) public class MyClassWithFields
+ implements PrintableFieldInterface {
final int intField = 42;
String stringField = "Hello!";
}
@@ -275,9 +309,8 @@
classConstant = FieldValuePrinterLibrary.class,
methodName = "printFieldValues")
},
- kind = KeepItemKind.ONLY_FIELDS,
- constraints = {KeepConstraint.LOOKUP, KeepConstraint.NAME, KeepConstraint.FIELD_GET})
- public class MyClassWithFields implements PrintableFieldInterface {
+ kind = KeepItemKind.ONLY_FIELDS) public class MyClassWithFields
+ implements PrintableFieldInterface {
final int intField = 42;
String stringField = "Hello!";
}
@@ -303,6 +336,7 @@
public static void main(String[] args) throws Exception {
Example1.run();
Example2.run();
+ Example2WithConstraints.run();
Example3.run();
Example4.run();
Example5.run();
diff --git a/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java b/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java
index 5a45f17..9b6d615 100644
--- a/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java
+++ b/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java
@@ -30,6 +30,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.keepanno.doctests.ForApiDocumentationTest;
+import com.android.tools.r8.keepanno.doctests.GenericSignaturePrinter;
import com.android.tools.r8.keepanno.doctests.MainMethodsDocumentationTest;
import com.android.tools.r8.keepanno.doctests.UsesReflectionAnnotationsDocumentationTest;
import com.android.tools.r8.keepanno.doctests.UsesReflectionDocumentationTest;
@@ -112,7 +113,8 @@
UsesReflectionDocumentationTest.class,
UsesReflectionAnnotationsDocumentationTest.class,
ForApiDocumentationTest.class,
- MainMethodsDocumentationTest.class);
+ MainMethodsDocumentationTest.class,
+ GenericSignaturePrinter.class);
}
private static String getPrefix(ClassReference annoType) {
diff --git a/src/test/testbase/java/com/android/tools/r8/ExternalR8TestBuilder.java b/src/test/testbase/java/com/android/tools/r8/ExternalR8TestBuilder.java
index 247cb6a..d1a45c1 100644
--- a/src/test/testbase/java/com/android/tools/r8/ExternalR8TestBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/ExternalR8TestBuilder.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.utils.ListUtils;
import com.google.common.base.Charsets;
import java.io.IOException;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
@@ -245,7 +246,11 @@
if (FileUtils.isJarFile(file)) {
programJars.add(file);
} else {
- throw new Unimplemented("No support for adding paths directly");
+ try {
+ addProgramClassFileData(Files.readAllBytes(file));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
}
return self();