Skip to content

Commit ea69eb3

Browse files
authored
Added bundled proguard rules (#2092)
* added delivery of ProGuard/R8 rules along with the core artifact. * the documentation about the rules has been made more readable * added an example of rules for named companions for R8 full mode * added R8-only rules to support full mode * rules have been introduced suppressing some warnings about serialization classes Resolves #1121 Resolves #1899 Resolves #1900 Resolves #2050
1 parent ccf9c2c commit ea69eb3

File tree

4 files changed

+107
-70
lines changed

4 files changed

+107
-70
lines changed

README.md

Lines changed: 42 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -178,96 +178,68 @@ dependencies {
178178
179179
### Android
180180

181-
The library works on Android, but, if you're using ProGuard,
182-
you need to add rules to your `proguard-rules.pro` configuration to cover all classes that are serialized at runtime.
181+
By default, proguard rules are supplied with the library.
182+
[These rules](rules/common.pro) keep serializers for _all_ serializable classes that are retained after shrinking,
183+
so you don't need additional setup.
183184

184-
The following configuration keeps serializers for _all_ serializable classes that are retained after shrinking.
185-
Uncomment and modify the last section in case you're serializing classes with named companion objects.
185+
**However, these rules do not affect serializable classes if they have named companion objects.**
186+
187+
If you want to serialize classes with named companion objects, you need to add and edit rules below to your `proguard-rules.pro` configuration.
188+
189+
Note that the rules for R8 differ depending on the [compatibility mode](https://r8.googlesource.com/r8/+/refs/heads/master/compatibility-faq.md) used.
190+
191+
<details>
192+
<summary>Example of named companion rules for ProGuard and R8 compatibility mode</summary>
186193

187194
```proguard
188-
# Keep `Companion` object fields of serializable classes.
189-
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
190-
-if @kotlinx.serialization.Serializable class **
191-
-keepclassmembers class <1> {
192-
static <1>$Companion Companion;
193-
}
195+
# Serializer for classes with named companion objects are retrieved using `getDeclaredClasses`.
196+
# If you have any, replace classes with those containing named companion objects.
197+
-keepattributes InnerClasses # Needed for `getDeclaredClasses`.
194198
195-
# Keep `serializer()` on companion objects (both default and named) of serializable classes.
196-
-if @kotlinx.serialization.Serializable class ** {
199+
-if @kotlinx.serialization.Serializable class
200+
com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions.
201+
com.example.myapplication.HasNamedCompanion2
202+
{
197203
static **$* *;
198204
}
199-
-keepclassmembers class <2>$<3> {
200-
kotlinx.serialization.KSerializer serializer(...);
205+
-keepnames class <1>$$serializer { # -keepnames suffices; class is kept when serializer() is kept.
206+
static <1>$$serializer INSTANCE;
201207
}
202-
203-
# Keep `INSTANCE.serializer()` of serializable objects.
204-
-if @kotlinx.serialization.Serializable class ** {
205-
public static ** INSTANCE;
206-
}
207-
-keepclassmembers class <1> {
208-
public static <1> INSTANCE;
209-
kotlinx.serialization.KSerializer serializer(...);
210-
}
211-
212-
# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
213-
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
214-
215-
# Serializer for classes with named companion objects are retrieved using `getDeclaredClasses`.
216-
# If you have any, uncomment and replace classes with those containing named companion objects.
217-
#-keepattributes InnerClasses # Needed for `getDeclaredClasses`.
218-
#-if @kotlinx.serialization.Serializable class
219-
#com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions.
220-
#com.example.myapplication.HasNamedCompanion2
221-
#{
222-
# static **$* *;
223-
#}
224-
#-keepnames class <1>$$serializer { # -keepnames suffices; class is kept when serializer() is kept.
225-
# static <1>$$serializer INSTANCE;
226-
#}
227208
```
209+
</details>
228210

229-
In case you want to exclude serializable classes that are used, but never serialized at runtime,
230-
you will need to write custom rules with narrower [class specifications](https://www.guardsquare.com/manual/configuration/usage).
231211

232212
<details>
233-
<summary>Example of custom rules</summary>
234-
235-
```proguard
236-
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
213+
<summary>Example of named companion rules for R8 full mode</summary>
237214

238-
# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer
239-
-keepclassmembers class kotlinx.serialization.json.** {
240-
*** Companion;
241-
}
242-
-keepclasseswithmembers class kotlinx.serialization.json.** {
243-
kotlinx.serialization.KSerializer serializer(...);
244-
}
245-
246-
# Application rules
215+
```proguard
216+
# Serializer for classes with named companion objects are retrieved using `getDeclaredClasses`.
217+
# If you have any, replace classes with those containing named companion objects.
218+
-keepattributes InnerClasses # Needed for `getDeclaredClasses`.
247219
248-
# Change here com.yourcompany.yourpackage
249-
-keepclassmembers @kotlinx.serialization.Serializable class com.yourcompany.yourpackage.** {
250-
# lookup for plugin generated serializable classes
251-
*** Companion;
252-
# lookup for serializable objects
253-
*** INSTANCE;
254-
kotlinx.serialization.KSerializer serializer(...);
220+
-if @kotlinx.serialization.Serializable class
221+
com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions.
222+
com.example.myapplication.HasNamedCompanion2
223+
{
224+
static **$* *;
255225
}
256-
# lookup for plugin generated serializable classes
257-
-if @kotlinx.serialization.Serializable class com.yourcompany.yourpackage.**
258-
-keepclassmembers class com.yourcompany.yourpackage.<1>$Companion {
259-
kotlinx.serialization.KSerializer serializer(...);
226+
-keepnames class <1>$$serializer { # -keepnames suffices; class is kept when serializer() is kept.
227+
static <1>$$serializer INSTANCE;
260228
}
261229
262-
# Serialization supports named companions but for such classes it is necessary to add an additional rule.
263-
# This rule keeps serializer and serializable class from obfuscation. Therefore, it is recommended not to use wildcards in it, but to write rules for each such class.
264-
-keepattributes InnerClasses # Needed for `getDeclaredClasses`.
265-
-keep class com.yourcompany.yourpackage.SerializableClassWithNamedCompanion$$serializer {
266-
*** INSTANCE;
230+
# Keep both serializer and serializable classes to save the attribute InnerClasses
231+
-keepclasseswithmembers, allowshrinking, allowobfuscation, allowaccessmodification class
232+
com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions.
233+
com.example.myapplication.HasNamedCompanion2
234+
{
235+
*;
267236
}
268237
```
269238
</details>
270239

240+
In case you want to exclude serializable classes that are used, but never serialized at runtime,
241+
you will need to write custom rules with narrower [class specifications](https://www.guardsquare.com/manual/configuration/usage).
242+
271243
### Multiplatform (Common, JS, Native)
272244

273245
Most of the modules are also available for Kotlin/JS and Kotlin/Native.

core/build.gradle

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,26 @@ kotlin {
3535
to reject runtime if runtime's Require-Kotlin-Version is greater then the current compiler.
3636
*/
3737
tasks.withType(Jar).named(kotlin.jvm().artifactsTaskName) {
38+
39+
// adding the ProGuard rules to the jar
40+
from(rootProject.file("rules/common.pro")) {
41+
rename { "kotlinx-serialization-common.pro" }
42+
into("META-INF/proguard")
43+
}
44+
from(rootProject.file("rules/common.pro")) {
45+
rename { "kotlinx-serialization-common.pro" }
46+
into("META-INF/com.android.tools/proguard")
47+
}
48+
from(rootProject.file("rules/common.pro")) {
49+
rename { "kotlinx-serialization-common.pro" }
50+
into("META-INF/com.android.tools/r8")
51+
}
52+
from(rootProject.file("rules/r8.pro")) {
53+
rename { "kotlinx-serialization-r8.pro" }
54+
into("META-INF/com.android.tools/r8")
55+
}
56+
57+
3858
manifest {
3959
attributes(
4060
"Implementation-Version": version,

rules/common.pro

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Keep `Companion` object fields of serializable classes.
2+
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
3+
-if @kotlinx.serialization.Serializable class **
4+
-keepclassmembers class <1> {
5+
static <1>$Companion Companion;
6+
}
7+
8+
# Keep `serializer()` on companion objects (both default and named) of serializable classes.
9+
-if @kotlinx.serialization.Serializable class ** {
10+
static **$* *;
11+
}
12+
-keepclassmembers class <2>$<3> {
13+
kotlinx.serialization.KSerializer serializer(...);
14+
}
15+
16+
# Keep `INSTANCE.serializer()` of serializable objects.
17+
-if @kotlinx.serialization.Serializable class ** {
18+
public static ** INSTANCE;
19+
}
20+
-keepclassmembers class <1> {
21+
public static <1> INSTANCE;
22+
kotlinx.serialization.KSerializer serializer(...);
23+
}
24+
25+
# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
26+
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
27+
28+
# Don't print notes about potential mistakes or omissions in the configuration for kotlinx-serialization classes
29+
# See also https://github.com/Kotlin/kotlinx.serialization/issues/1900
30+
-dontnote kotlinx.serialization.**
31+
32+
# Serialization core uses `Class.forName("java.lang.ClassValue")` for caching in JVM-only, so it is an expected situation that this class is not in Android
33+
-dontwarn java.lang.ClassValue

rules/r8.pro

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Rule to save runtime annotations on serializable class.
2+
# If the R8 full mode is used, annotations are removed from classes-files.
3+
#
4+
# For the annotation serializer, it is necessary to read the `Serializable` annotation inside the serializer<T>() function - if it is present,
5+
# then `SealedClassSerializer` is used, if absent, then `PolymorphicSerializer'.
6+
#
7+
# When using R8 full mode, all interfaces will be serialized using `PolymorphicSerializer`.
8+
#
9+
# see https://github.com/Kotlin/kotlinx.serialization/issues/2050
10+
11+
-if @kotlinx.serialization.Serializable class **
12+
-keep, allowshrinking, allowoptimization, allowobfuscation, allowaccessmodification class <1>

0 commit comments

Comments
 (0)