blob: 253a756b38aed3b35571f1faab9de5a0a36fbcd9 [file] [log] [blame] [view]
Ian Zerny8f0adab2023-11-23 15:12:13 +01001[comment]: <> (DO NOT EDIT - GENERATED FILE)
2[comment]: <> (Changes should be made in doc/keepanno-guide.template.md)
3
4# Guide to Keep Annotations
5
6## Disclaimer
7
8The annotation library described here is in development and considered to be in
9its prototype phase. As such it is not yet feature complete, but we are actively
10working on supporting all of the use cases we know of. Once the design exits the
11prototype phase, it is intended to move to an R8 independent library as part of
12androidx. All feedback: criticism, comments and suggestions are very welcome!
13
14[File new feature requests and
15bugs](https://issuetracker.google.com/issues/new?component=326788) in the
16[R8 component](https://issuetracker.google.com/issues?q=status:open%20componentid:326788).
17
18
Ian Zerny4e219c12023-11-27 13:27:55 +010019## Table of contents
Ian Zerny8f0adab2023-11-23 15:12:13 +010020
Ian Zerny4e219c12023-11-27 13:27:55 +010021- [Introduction](#introduction)
22- [Build configuration](#build-configuration)
23- [Annotating code using reflection](#using-reflection)
Ian Zerny84427962023-12-06 10:57:30 +010024 - [Invoking methods](#using-reflection-methods)
25 - [Accessing fields](#using-reflection-fields)
Ian Zernye9981bd2024-01-18 13:26:54 +010026 - [Accessing annotations](#using-reflection-annotations)
Ian Zerny4e219c12023-11-27 13:27:55 +010027- [Annotating code used by reflection (or via JNI)](#used-by-reflection)
28- [Annotating APIs](#apis)
Ian Zernyb7199f22024-07-03 15:07:59 +020029- [Constraints](#constraints)
30 - [Defaults](#constraints-defaults)
31 - [Generic signatures](#constraints-signatures)
Ian Zerny4e219c12023-11-27 13:27:55 +010032- [Migrating rules to annotations](#migrating-rules)
33- [My use case is not covered!](#other-uses)
34- [Troubleshooting](#troubleshooting)
35
36
37
Ian Zerny6ad10ce2024-01-04 14:40:27 +010038## Introduction<a name="introduction"></a>
Ian Zerny8f0adab2023-11-23 15:12:13 +010039
40When using a Java/Kotlin shrinker such as R8 or Proguard, developers must inform
41the shrinker about parts of the program that are used either externally from the
42program itself or internally via reflection and therefore must be kept.
43
44Traditionally these aspects would be kept by writing keep rules in a
45configuration file and passing that to the shrinker.
46
47The keep annotations described in this document represent an alternative method
48using Java annotations. The motivation for using these annotations is foremost
49to place the description of what to keep closer to the program point using
50reflective behavior. Doing so more directly connects the reflective code with
Ian Zerny84427962023-12-06 10:57:30 +010051the keep specification and makes it easier to maintain as the code develops.
52Often the keep annotations are only in effect if the annotated method is used,
53allowing more precise shrinking. In addition, the annotations are defined
54independent from keep rules and have a hopefully more clear and direct meaning.
Ian Zerny8f0adab2023-11-23 15:12:13 +010055
56
Ian Zerny6ad10ce2024-01-04 14:40:27 +010057## Build configuration<a name="build-configuration"></a>
Ian Zerny8f0adab2023-11-23 15:12:13 +010058
59To use the keep annotations your build must include the library of
60annotations. It is currently built as part of each R8 build and if used with R8,
61you should use the matching version. You can find all archived builds at:
62
63```
64https://storage.googleapis.com/r8-releases/raw/<version>/keepanno-annotations.jar
65```
66
67Thus you may obtain version `8.2.34` by running:
68
69```
70wget https://storage.googleapis.com/r8-releases/raw/8.2.34/keepanno-annotations.jar
71```
72
73You will then need to set the system property
74`com.android.tools.r8.enableKeepAnnotations` to instruct R8 to make use of the
75annotations when shrinking:
76
77```
78java -Dcom.android.tools.r8.enableKeepAnnotations=1 \
79 -cp r8.jar com.android.tools.r8.R8 \
80 # ... the rest of your R8 compilation command here ...
81```
82
Ian Zerny84427962023-12-06 10:57:30 +010083
Ian Zerny6ad10ce2024-01-04 14:40:27 +010084## Annotating code using reflection<a name="using-reflection"></a>
Ian Zerny8f0adab2023-11-23 15:12:13 +010085
86The keep annotation library defines a family of annotations depending on your
87use case. You should generally prefer [@UsesReflection](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/UsesReflection.html) where applicable.
Ian Zerny84427962023-12-06 10:57:30 +010088Common uses of reflection are to lookup fields and methods on classes. Examples
89of such use cases are detailed below.
90
91
Ian Zerny6ad10ce2024-01-04 14:40:27 +010092### Invoking methods<a name="using-reflection-methods"></a>
Ian Zerny8f0adab2023-11-23 15:12:13 +010093
94For example, if your program is reflectively invoking a method, you
95should annotate the method that is doing the reflection. The annotation must describe the
96assumptions the reflective code makes.
97
Ian Zerny84427962023-12-06 10:57:30 +010098In the following example, the method `callHiddenMethod` is looking up the method with the name
Ian Zerny8f0adab2023-11-23 15:12:13 +010099`hiddenMethod` on objects that are instances of `BaseClass`. It is then invoking the method with
100no other arguments than the receiver.
101
102The assumptions the code makes are that all methods with the name
103`hiddenMethod` and the empty list of parameters must remain valid for `getDeclaredMethod` if they
104are objects that are instances of the class `BaseClass` or subclasses thereof.
105
106
107```
Ian Zerny84427962023-12-06 10:57:30 +0100108public class MyHiddenMethodCaller {
Ian Zerny8f0adab2023-11-23 15:12:13 +0100109
Ian Zernyb7199f22024-07-03 15:07:59 +0200110 @UsesReflection(
111 @KeepTarget(
112 instanceOfClassConstant = BaseClass.class,
113 methodName = "hiddenMethod",
114 methodParameters = {}))
Ian Zerny84427962023-12-06 10:57:30 +0100115 public void callHiddenMethod(BaseClass base) throws Exception {
Ian Zerny8f0adab2023-11-23 15:12:13 +0100116 base.getClass().getDeclaredMethod("hiddenMethod").invoke(base);
117 }
118}
119```
120
121
122
Ian Zerny6ad10ce2024-01-04 14:40:27 +0100123### Accessing fields<a name="using-reflection-fields"></a>
Ian Zerny84427962023-12-06 10:57:30 +0100124
125For example, if your program is reflectively accessing the fields on a class, you should
126annotate the method that is doing the reflection.
127
128In the following example, the `printFieldValues` method takes in an object of
129type `PrintableFieldInterface` and then looks for all the fields declared on the class
130of the object.
131
132The [@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
Ian Zernyb7199f22024-07-03 15:07:59 +0200133the 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).
Ian Zerny84427962023-12-06 10:57:30 +0100134
135
136```
Ian Zernyb7199f22024-07-03 15:07:59 +0200137static class MyFieldValuePrinter {
Ian Zerny84427962023-12-06 10:57:30 +0100138
Ian Zernyb7199f22024-07-03 15:07:59 +0200139 @UsesReflection(
140 @KeepTarget(
141 instanceOfClassConstant = PrintableFieldInterface.class,
142 kind = KeepItemKind.ONLY_FIELDS))
Ian Zerny84427962023-12-06 10:57:30 +0100143 public void printFieldValues(PrintableFieldInterface objectWithFields) throws Exception {
144 for (Field field : objectWithFields.getClass().getDeclaredFields()) {
145 System.out.println(field.getName() + " = " + field.get(objectWithFields));
146 }
147 }
148}
149```
150
151
Ian Zernye9981bd2024-01-18 13:26:54 +0100152### Accessing annotations<a name="using-reflection-annotations"></a>
153
154If your program is reflectively inspecting annotations on classes, methods or fields, you
155will need to declare additional "annotation constraints" about what assumptions are made
156about the annotations.
157
158In the following example, we have defined an annotation that will record the printing name we
159would like to use for fields instead of printing the concrete field name. That may be useful
160so that the field can be renamed to follow coding conventions for example.
161
162We are only interested in matching objects that contain fields annotated by `MyNameAnnotation`,
163that is specified using [@KeepTarget.fieldAnnotatedByClassConstant](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepTarget.html#fieldAnnotatedByClassConstant()).
164
165At runtime we need to be able to find the annotation too, so we add a constraint on the
166annotation using [@KeepTarget.constrainAnnotations](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepTarget.html#constrainAnnotations()).
167
168Finally, for the sake of example, we don't actually care about the name of the fields
169themselves, so we explicitly declare the smaller set of constraints to be
170[KeepConstraint.LOOKUP](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepConstraint.html#LOOKUP) since we must find the fields via `Class.getDeclaredFields` as well as
171[KeepConstraint.VISIBILITY_RELAX](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepConstraint.html#VISIBILITY_RELAX) 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) in order to be able to get
172the actual field value without accessibility errors.
173
174The effect is that the default constraint [KeepConstraint.NAME](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepConstraint.html#NAME) is not specified which allows
175the shrinker to rename the fields at will.
176
177
178```
179public class MyAnnotationPrinter {
180
181 @Target(ElementType.FIELD)
182 @Retention(RetentionPolicy.RUNTIME)
183 public @interface MyNameAnnotation {
184 String value();
185 }
186
187 public static class MyClass {
188 @MyNameAnnotation("fieldOne")
189 public int mFieldOne = 1;
190
191 @MyNameAnnotation("fieldTwo")
192 public int mFieldTwo = 2;
193
194 public int mFieldThree = 3;
195 }
196
197 @UsesReflection(
198 @KeepTarget(
199 fieldAnnotatedByClassConstant = MyNameAnnotation.class,
200 constrainAnnotations = @AnnotationPattern(constant = MyNameAnnotation.class),
201 constraints = {
202 KeepConstraint.LOOKUP,
203 KeepConstraint.VISIBILITY_RELAX,
204 KeepConstraint.FIELD_GET
205 }))
206 public void printMyNameAnnotatedFields(Object obj) throws Exception {
207 for (Field field : obj.getClass().getDeclaredFields()) {
208 if (field.isAnnotationPresent(MyNameAnnotation.class)) {
209 System.out.println(
210 field.getAnnotation(MyNameAnnotation.class).value() + " = " + field.get(obj));
211 }
212 }
213 }
214}
215```
216
217
218If the annotations that need to be kept are not runtime
219visible annotations, then you must specify that by including the `RetentionPolicy.CLASS` value in the
220[@AnnotationPattern.retention](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/AnnotationPattern.html#retention()) property.
221An annotation is runtime visible if its definition is explicitly annotated with
222`Retention(RetentionPolicy.RUNTIME)`.
223
224
Ian Zerny84427962023-12-06 10:57:30 +0100225
Ian Zerny6ad10ce2024-01-04 14:40:27 +0100226## Annotating code used by reflection (or via JNI)<a name="used-by-reflection"></a>
Ian Zerny8f0adab2023-11-23 15:12:13 +0100227
Ian Zernyd8bc17b2024-01-04 09:48:47 +0100228Sometimes reflecting code cannot be annotated. For example, the reflection can
229be done in native code or in a library outside your control. In such cases you
230can annotate the code that is being used by reflection with either
231[@UsedByReflection](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/UsedByReflection.html) or [@UsedByNative](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/UsedByNative.html). These two annotations are equivalent.
232Use the one that best matches why the annotation is needed.
233
234Let's consider some code with reflection outside our control.
235For example, the same field printing as in the above example might be part of a library.
236
237In this example, the `MyClassWithFields` is a class you are passing to the
238field-printing utility of the library. Since the library is reflectively accessing each field
239we 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.
240
Ian Zernyd8bc17b2024-01-04 09:48:47 +0100241
242```
Ian Zerny5f8936a2024-01-04 11:43:47 +0100243public class MyClassWithFields implements PrintableFieldInterface {
Ian Zernyd8bc17b2024-01-04 09:48:47 +0100244 @UsedByReflection final int intField = 42;
245
246 @UsedByReflection String stringField = "Hello!";
247}
248
249public static void run() throws Exception {
250 new FieldValuePrinterLibrary().printFieldValues(new MyClassWithFields());
251}
252```
253
254
255Rather than annotate the individual fields we can annotate the holder and add a specification
256similar to the [@KeepTarget](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepTarget.html). The [@UsedByReflection.kind](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/UsedByReflection.html#kind()) specifies that only the fields are
257used reflectively. In particular, the "field printer" example we are considering here does not
258make reflective assumptions about the holder class, so we should not constrain it.
259
Ian Zernyd8bc17b2024-01-04 09:48:47 +0100260
261```
Ian Zernyb7199f22024-07-03 15:07:59 +0200262@UsedByReflection(kind = KeepItemKind.ONLY_FIELDS) public class MyClassWithFields
263 implements PrintableFieldInterface {
Ian Zernyd8bc17b2024-01-04 09:48:47 +0100264 final int intField = 42;
265 String stringField = "Hello!";
266}
267```
268
269
270Our use of [@UsedByReflection](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/UsedByReflection.html) is still not as flexible as the original [@UsesReflection](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/UsesReflection.html). In
271particular, if we change our code to no longer have any call to the library method
272`printFieldValues` the shrinker will still keep all of the fields on our annotated class.
273
274This is because the [@UsesReflection](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/UsesReflection.html) implicitly encodes as a precondition that the annotated
275method is actually used in the program. If not, the [@UsesReflection](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/UsesReflection.html) annotation is not
276"active".
277
278Luckily we can specify the same precondition using [@UsedByReflection.preconditions](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/UsedByReflection.html#preconditions()).
279
280
281```
282@UsedByReflection(
283 preconditions = {
284 @KeepCondition(
285 classConstant = FieldValuePrinterLibrary.class,
286 methodName = "printFieldValues")
287 },
Ian Zernyb7199f22024-07-03 15:07:59 +0200288 kind = KeepItemKind.ONLY_FIELDS) public class MyClassWithFields
289 implements PrintableFieldInterface {
Ian Zernyd8bc17b2024-01-04 09:48:47 +0100290 final int intField = 42;
291 String stringField = "Hello!";
292}
293```
294
Ian Zernye692bce2023-11-27 13:51:43 +0100295
Ian Zerny8f0adab2023-11-23 15:12:13 +0100296
Ian Zerny6ad10ce2024-01-04 14:40:27 +0100297## Annotating APIs<a name="apis"></a>
Ian Zerny8f0adab2023-11-23 15:12:13 +0100298
Ian Zernyb7199f22024-07-03 15:07:59 +0200299If your code is being shrunk before release as a library, then you need to keep
300the 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.
Ian Zerny5f8936a2024-01-04 11:43:47 +0100301
302When 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
303public and protected members:
304
305
306```
307@KeepForApi
308public class MyApi {
309 public void thisPublicMethodIsKept() {
310 /* ... */
311 }
312
313 protected void thisProtectedMethodIsKept() {
314 /* ... */
315 }
316
317 void thisPackagePrivateMethodIsNotKept() {
318 /* ... */
319 }
320
321 private void thisPrivateMethodIsNotKept() {
322 /* ... */
323 }
324}
325```
326
327
328The default can be changed using the [@KeepForApi.memberAccess](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepForApi.html#memberAccess()) property:
329
330
331```
332@KeepForApi(
333 memberAccess = {
334 MemberAccessFlags.PUBLIC,
335 MemberAccessFlags.PROTECTED,
336 MemberAccessFlags.PACKAGE_PRIVATE
337 })
338```
339
340
341The [@KeepForApi](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepForApi.html) annotation can also be placed directly on members and avoid keeping
342unannotated members. The holder class is implicitly kept. When annotating the members
343directly, the access does not matter as illustrated here by annotating a package private method:
344
345
346```
347public class MyOtherApi {
348
349 public void notKept() {
350 /* ... */
351 }
352
353 @KeepForApi
354 void isKept() {
355 /* ... */
356 }
357}
358```
359
Ian Zernye692bce2023-11-27 13:51:43 +0100360
Ian Zerny8f0adab2023-11-23 15:12:13 +0100361
Ian Zernyb7199f22024-07-03 15:07:59 +0200362## Constraints<a name="constraints"></a>
363
364When 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
365[@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
366about what properties of that item must be kept. Typical constraints are to keep
367the items *name* or its ability to be reflectively *looked up*. You may also be
368interested in keeping the generic signature of an item or annotations associated
369with it.
370
371### Defaults<a name="constraints-defaults"></a>
372
373By default the constraints are to retain the item's name, its ability to be
374looked-up as well as its normal usage. Its normal usage is:
375
376- to be instantiated, for class items;
377- to be invoked, for method items; and
378- to be get and/or set, for field items.
379
380Let us revisit the example reflectively accessing the fields on a class.
381
382Notice that printing the field names and values only requires looking up the field, printing
383its name and getting its value. It does not require setting a new value on the field.
384We can thus use a more restrictive set of constraints
385by 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),
386[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).
387
388
389```
390static class MyFieldValuePrinter {
391
392 @UsesReflection(
393 @KeepTarget(
394 instanceOfClassConstant = PrintableFieldInterface.class,
395 kind = KeepItemKind.ONLY_FIELDS,
396 constraints = {KeepConstraint.LOOKUP, KeepConstraint.NAME, KeepConstraint.FIELD_GET}))
397 public void printFieldValues(PrintableFieldInterface objectWithFields) throws Exception {
398 for (Field field : objectWithFields.getClass().getDeclaredFields()) {
399 System.out.println(field.getName() + " = " + field.get(objectWithFields));
400 }
401 }
402}
403```
404
405
406
407### Generic signatures<a name="constraints-signatures"></a>
408
409The generic signature information of an item is not kept by default, and
410requires adding constraints to the targeted items.
411
412Imagine we had code that is making use of the template parameters for implementations of a
413generic interface. The code below assumes direct implementations of the `WrappedValue` interface
414and simply prints the type parameter used.
415
416Since we are reflecting on the class structure of implementations of `WrappedValue` we need to
417keep it and any instance of it.
418
419We must also preserve the generic signatures of these classes. We add the
420[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())
421property. This ensures that the default constraints are still in place in addition to the
422constraint on generic signatures.
423
424
425```
426public class GenericSignaturePrinter {
427
428 interface WrappedValue<T> {
429 T getValue();
430 }
431
432 @UsesReflection(
433 @KeepTarget(
434 instanceOfClassConstant = WrappedValue.class,
435 constraintAdditions = KeepConstraint.GENERIC_SIGNATURE))
436 public static void printSignature(WrappedValue<?> obj) {
437 Class<? extends WrappedValue> clazz = obj.getClass();
438 for (Type iface : clazz.getGenericInterfaces()) {
439 String typeName = iface.getTypeName();
440 String param = typeName.substring(typeName.lastIndexOf('<') + 1, typeName.lastIndexOf('>'));
441 System.out.println(clazz.getName() + " uses type " + param);
442 }
443 }
444```
445
446
447
Ian Zerny6ad10ce2024-01-04 14:40:27 +0100448## Migrating rules to annotations<a name="migrating-rules"></a>
Ian Zerny8f0adab2023-11-23 15:12:13 +0100449
Ian Zernye692bce2023-11-27 13:51:43 +0100450There is no automatic migration of keep rules. Keep annotations often invert the
451direction and rules have no indication of where the reflection is taking
452place or why. Thus, migrating existing keep rules requires user involvement.
453Keep rules also have a tendency to be very general, matching a large
454number of classes and members. Often the rules are much too broad and are
455keeping more than needed which will have a negative impact on the shrinkers
456ability to reduce size.
457
458First step in converting a rule is to determine the purpose of the rule. Is it
459API surface or is it reflection? Note that a very general rule may be covering
460several use cases and even a mix of both reflection and API usage.
461
462When migrating it is preferable to use [@UsesReflection](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/UsesReflection.html) instead of
463[@UsedByReflection](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/UsedByReflection.html). For very general rules it might not be easy or worth it to
464migrate without completely reevaluating the rule. If one still wants to replace
465it by annotations, the general [@KeepEdge](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepEdge.html) can be used to define a context
466independent keep annotation.
467
468For example, to keep all main methods in the program one could use:
469
470
471```
472@KeepEdge(
473 consequences = {
474 @KeepTarget(
475 kind = KeepItemKind.CLASS_AND_MEMBERS,
476 methodName = "main",
477 methodReturnType = "void",
478 methodParameters = {"java.lang.String[]"},
479 methodAccess = {MethodAccessFlags.PUBLIC, MethodAccessFlags.STATIC})
480 })
481public class SomeClass {
482 // ...
483}
484```
485
486
Ian Zerny8f0adab2023-11-23 15:12:13 +0100487
Ian Zerny6ad10ce2024-01-04 14:40:27 +0100488## My use case is not covered!<a name="other-uses"></a>
Ian Zerny8f0adab2023-11-23 15:12:13 +0100489
Ian Zernye692bce2023-11-27 13:51:43 +0100490The annotation library is in active development and not all use cases are
491described here or supported. Reach out to the R8 team by
492[filing a new issue in our tracker](https://issuetracker.google.com/issues/new?component=326788).
493Describe your use case and we will look at how best to support it.
494
Ian Zerny8f0adab2023-11-23 15:12:13 +0100495
Ian Zerny6ad10ce2024-01-04 14:40:27 +0100496## Troubleshooting<a name="troubleshooting"></a>
Ian Zernye692bce2023-11-27 13:51:43 +0100497
498If an annotation is not working as expected it may be helpful to inspect the
499rules that have been extracted for the annotation. This can be done by
500inspecting the configuration output of the shrinker. For R8 you can use the
501command line argument `--pg-conf-output <path>` to emit the full configuration
502used by R8.