blob: da018b3479d4f01ea2f6f32d41b6d0cdf2f96b00 [file] [log] [blame] [view]
Ian Zerny8f0adab2023-11-23 15:12:13 +01001# Guide to Keep Annotations
2
3## Disclaimer
4
5The annotation library described here is in development and considered to be in
6its prototype phase. As such it is not yet feature complete, but we are actively
7working on supporting all of the use cases we know of. Once the design exits the
8prototype phase, it is intended to move to an R8 independent library as part of
9androidx. All feedback: criticism, comments and suggestions are very welcome!
10
11[File new feature requests and
12bugs](https://issuetracker.google.com/issues/new?component=326788) in the
13[R8 component](https://issuetracker.google.com/issues?q=status:open%20componentid:326788).
14
15
Ian Zerny4e219c12023-11-27 13:27:55 +010016[[[TOC]]]
Ian Zerny8f0adab2023-11-23 15:12:13 +010017
Ian Zerny4e219c12023-11-27 13:27:55 +010018
19## [Introduction](introduction)
Ian Zerny8f0adab2023-11-23 15:12:13 +010020
21When using a Java/Kotlin shrinker such as R8 or Proguard, developers must inform
22the shrinker about parts of the program that are used either externally from the
23program itself or internally via reflection and therefore must be kept.
24
25Traditionally these aspects would be kept by writing keep rules in a
26configuration file and passing that to the shrinker.
27
28The keep annotations described in this document represent an alternative method
29using Java annotations. The motivation for using these annotations is foremost
30to place the description of what to keep closer to the program point using
31reflective behavior. Doing so more directly connects the reflective code with
Ian Zerny84427962023-12-06 10:57:30 +010032the keep specification and makes it easier to maintain as the code develops.
33Often the keep annotations are only in effect if the annotated method is used,
34allowing more precise shrinking. In addition, the annotations are defined
35independent from keep rules and have a hopefully more clear and direct meaning.
Ian Zerny8f0adab2023-11-23 15:12:13 +010036
37
Ian Zerny4e219c12023-11-27 13:27:55 +010038## [Build configuration](build-configuration)
Ian Zerny8f0adab2023-11-23 15:12:13 +010039
40To use the keep annotations your build must include the library of
41annotations. It is currently built as part of each R8 build and if used with R8,
42you should use the matching version. You can find all archived builds at:
43
44```
45https://storage.googleapis.com/r8-releases/raw/<version>/keepanno-annotations.jar
46```
47
48Thus you may obtain version `8.2.34` by running:
49
50```
51wget https://storage.googleapis.com/r8-releases/raw/8.2.34/keepanno-annotations.jar
52```
53
54You will then need to set the system property
55`com.android.tools.r8.enableKeepAnnotations` to instruct R8 to make use of the
56annotations when shrinking:
57
58```
59java -Dcom.android.tools.r8.enableKeepAnnotations=1 \
60 -cp r8.jar com.android.tools.r8.R8 \
61 # ... the rest of your R8 compilation command here ...
62```
63
Ian Zerny84427962023-12-06 10:57:30 +010064
Ian Zerny4e219c12023-11-27 13:27:55 +010065## [Annotating code using reflection](using-reflection)
Ian Zerny8f0adab2023-11-23 15:12:13 +010066
67The keep annotation library defines a family of annotations depending on your
68use case. You should generally prefer `@UsesReflection` where applicable.
Ian Zerny84427962023-12-06 10:57:30 +010069Common uses of reflection are to lookup fields and methods on classes. Examples
70of such use cases are detailed below.
71
72
73### [Invoking methods](using-reflection-methods)
Ian Zerny8f0adab2023-11-23 15:12:13 +010074
75[[[INCLUDE DOC:UsesReflectionOnVirtualMethod]]]
76
77[[[INCLUDE CODE:UsesReflectionOnVirtualMethod]]]
78
79
Ian Zerny84427962023-12-06 10:57:30 +010080### [Accessing fields](using-reflection-fields)
81
82[[[INCLUDE DOC:UsesReflectionFieldPrinter]]]
83
84[[[INCLUDE CODE:UsesReflectionFieldPrinter]]]
85
Ian Zernye9981bd2024-01-18 13:26:54 +010086### [Accessing annotations](using-reflection-annotations)
87
88[[[INCLUDE DOC:UsesReflectionOnAnnotations]]]
89
90[[[INCLUDE CODE:UsesReflectionOnAnnotations]]]
91
92If the annotations that need to be kept are not runtime
93visible annotations, then you must specify that by including the `RetentionPolicy.CLASS` value in the
94`@AnnotationPattern#retention` property.
95An annotation is runtime visible if its definition is explicitly annotated with
96`Retention(RetentionPolicy.RUNTIME)`.
97
98
Ian Zerny84427962023-12-06 10:57:30 +010099
Ian Zerny4e219c12023-11-27 13:27:55 +0100100## [Annotating code used by reflection (or via JNI)](used-by-reflection)
Ian Zerny8f0adab2023-11-23 15:12:13 +0100101
Ian Zernyd8bc17b2024-01-04 09:48:47 +0100102Sometimes reflecting code cannot be annotated. For example, the reflection can
103be done in native code or in a library outside your control. In such cases you
104can annotate the code that is being used by reflection with either
105`@UsedByReflection` or `@UsedByNative`. These two annotations are equivalent.
106Use the one that best matches why the annotation is needed.
107
108Let's consider some code with reflection outside our control.
109[[[INCLUDE DOC:UsedByReflectionFieldPrinterOnFields]]]
110
111[[[INCLUDE CODE:UsedByReflectionFieldPrinterOnFields]]]
112
113[[[INCLUDE DOC:UsedByReflectionFieldPrinterOnClass]]]
114
115[[[INCLUDE CODE:UsedByReflectionFieldPrinterOnClass]]]
116
117[[[INCLUDE DOC:UsedByReflectionFieldPrinterConditional]]]
118
119[[[INCLUDE CODE:UsedByReflectionFieldPrinterConditional]]]
Ian Zernye692bce2023-11-27 13:51:43 +0100120
Ian Zerny8f0adab2023-11-23 15:12:13 +0100121
Ian Zerny4e219c12023-11-27 13:27:55 +0100122## [Annotating APIs](apis)
Ian Zerny8f0adab2023-11-23 15:12:13 +0100123
Ian Zernyb7199f22024-07-03 15:07:59 +0200124If your code is being shrunk before release as a library, then you need to keep
125the API surface. For that you should use the `@KeepForApi` annotation.
Ian Zerny5f8936a2024-01-04 11:43:47 +0100126
127[[[INCLUDE DOC:ApiClass]]]
128
129[[[INCLUDE CODE:ApiClass]]]
130
131[[[INCLUDE DOC:ApiClassMemberAccess]]]
132
133[[[INCLUDE CODE:ApiClassMemberAccess]]]
134
135[[[INCLUDE DOC:ApiMember]]]
136
137[[[INCLUDE CODE:ApiMember]]]
Ian Zernye692bce2023-11-27 13:51:43 +0100138
Ian Zerny8f0adab2023-11-23 15:12:13 +0100139
Ian Zernyb7199f22024-07-03 15:07:59 +0200140## [Constraints](constraints)
141
142When an item is kept (e.g., items matched by `@KeepTarget` or annotated by
143`@UsedByReflection` or `@KeepForApi`) you can additionally specify constraints
144about what properties of that item must be kept. Typical constraints are to keep
145the items *name* or its ability to be reflectively *looked up*. You may also be
146interested in keeping the generic signature of an item or annotations associated
147with it.
148
149### [Defaults](constraints-defaults)
150
151By default the constraints are to retain the item's name, its ability to be
152looked-up as well as its normal usage. Its normal usage is:
153
154- to be instantiated, for class items;
155- to be invoked, for method items; and
156- to be get and/or set, for field items.
157
158[[[INCLUDE DOC:UsesReflectionFieldPrinterWithConstraints]]]
159
160[[[INCLUDE CODE:UsesReflectionFieldPrinterWithConstraints]]]
161
162
163### [Generic signatures](constraints-signatures)
164
165The generic signature information of an item is not kept by default, and
166requires adding constraints to the targeted items.
167
168[[[INCLUDE DOC:GenericSignaturePrinter]]]
169
170[[[INCLUDE CODE:GenericSignaturePrinter]]]
171
172
Ian Zerny4e219c12023-11-27 13:27:55 +0100173## [Migrating rules to annotations](migrating-rules)
Ian Zerny8f0adab2023-11-23 15:12:13 +0100174
Ian Zernye692bce2023-11-27 13:51:43 +0100175There is no automatic migration of keep rules. Keep annotations often invert the
176direction and rules have no indication of where the reflection is taking
177place or why. Thus, migrating existing keep rules requires user involvement.
178Keep rules also have a tendency to be very general, matching a large
179number of classes and members. Often the rules are much too broad and are
180keeping more than needed which will have a negative impact on the shrinkers
181ability to reduce size.
182
183First step in converting a rule is to determine the purpose of the rule. Is it
184API surface or is it reflection? Note that a very general rule may be covering
185several use cases and even a mix of both reflection and API usage.
186
187When migrating it is preferable to use `@UsesReflection` instead of
188`@UsedByReflection`. For very general rules it might not be easy or worth it to
189migrate without completely reevaluating the rule. If one still wants to replace
190it by annotations, the general `@KeepEdge` can be used to define a context
191independent keep annotation.
192
193[[[INCLUDE DOC:KeepMainMethods]]]
194
195[[[INCLUDE CODE:KeepMainMethods]]]
196
Ian Zerny8f0adab2023-11-23 15:12:13 +0100197
Ian Zerny4e219c12023-11-27 13:27:55 +0100198## [My use case is not covered!](other-uses)
Ian Zerny8f0adab2023-11-23 15:12:13 +0100199
Ian Zernye692bce2023-11-27 13:51:43 +0100200The annotation library is in active development and not all use cases are
201described here or supported. Reach out to the R8 team by
202[filing a new issue in our tracker](https://issuetracker.google.com/issues/new?component=326788).
203Describe your use case and we will look at how best to support it.
204
Ian Zerny8f0adab2023-11-23 15:12:13 +0100205
Ian Zerny4e219c12023-11-27 13:27:55 +0100206## [Troubleshooting](troubleshooting)
Ian Zernye692bce2023-11-27 13:51:43 +0100207
208If an annotation is not working as expected it may be helpful to inspect the
209rules that have been extracted for the annotation. This can be done by
210inspecting the configuration output of the shrinker. For R8 you can use the
211command line argument `--pg-conf-output <path>` to emit the full configuration
212used by R8.