blob: 54d910c12a66aaeb306c3747a5d13ff28d0498dd [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 Zerny5f8936a2024-01-04 11:43:47 +0100124If your code is being shrunk before release as a library, or if you have an API
125surface that is used via dynamic loading at runtime, then you need to keep the
126API surface. For that you should use the `@KeepForApi` annotation.
127
128[[[INCLUDE DOC:ApiClass]]]
129
130[[[INCLUDE CODE:ApiClass]]]
131
132[[[INCLUDE DOC:ApiClassMemberAccess]]]
133
134[[[INCLUDE CODE:ApiClassMemberAccess]]]
135
136[[[INCLUDE DOC:ApiMember]]]
137
138[[[INCLUDE CODE:ApiMember]]]
Ian Zernye692bce2023-11-27 13:51:43 +0100139
Ian Zerny8f0adab2023-11-23 15:12:13 +0100140
Ian Zerny4e219c12023-11-27 13:27:55 +0100141## [Migrating rules to annotations](migrating-rules)
Ian Zerny8f0adab2023-11-23 15:12:13 +0100142
Ian Zernye692bce2023-11-27 13:51:43 +0100143There is no automatic migration of keep rules. Keep annotations often invert the
144direction and rules have no indication of where the reflection is taking
145place or why. Thus, migrating existing keep rules requires user involvement.
146Keep rules also have a tendency to be very general, matching a large
147number of classes and members. Often the rules are much too broad and are
148keeping more than needed which will have a negative impact on the shrinkers
149ability to reduce size.
150
151First step in converting a rule is to determine the purpose of the rule. Is it
152API surface or is it reflection? Note that a very general rule may be covering
153several use cases and even a mix of both reflection and API usage.
154
155When migrating it is preferable to use `@UsesReflection` instead of
156`@UsedByReflection`. For very general rules it might not be easy or worth it to
157migrate without completely reevaluating the rule. If one still wants to replace
158it by annotations, the general `@KeepEdge` can be used to define a context
159independent keep annotation.
160
161[[[INCLUDE DOC:KeepMainMethods]]]
162
163[[[INCLUDE CODE:KeepMainMethods]]]
164
Ian Zerny8f0adab2023-11-23 15:12:13 +0100165
Ian Zerny4e219c12023-11-27 13:27:55 +0100166## [My use case is not covered!](other-uses)
Ian Zerny8f0adab2023-11-23 15:12:13 +0100167
Ian Zernye692bce2023-11-27 13:51:43 +0100168The annotation library is in active development and not all use cases are
169described here or supported. Reach out to the R8 team by
170[filing a new issue in our tracker](https://issuetracker.google.com/issues/new?component=326788).
171Describe your use case and we will look at how best to support it.
172
Ian Zerny8f0adab2023-11-23 15:12:13 +0100173
Ian Zerny4e219c12023-11-27 13:27:55 +0100174## [Troubleshooting](troubleshooting)
Ian Zernye692bce2023-11-27 13:51:43 +0100175
176If an annotation is not working as expected it may be helpful to inspect the
177rules that have been extracted for the annotation. This can be done by
178inspecting the configuration output of the shrinker. For R8 you can use the
179command line argument `--pg-conf-output <path>` to emit the full configuration
180used by R8.