4

How do I configure proguard/R8 to keep all the fields and methods of a class if the class contains an annotated field?

I have a class

public class MyDocument {
  @DocumentId
  private String foo;

  private String bar;

  public getFoo() { .. }
  public getBar() { .. }
}

I was able to make it not remove foo with

-keepclassmembers class * {
  @a.b.c.DocumentId <fields>;
  @a.b.c.DocumentId <methods>;
}

But it still removes getBar()

Is there any way to say "don't remove any methods or fields in a class if one of the fields in the same class contains the annotation?

The usecase here is that they are DTO classes used by firestore's toObject
I don't want to do the matching based on the name of the class since this rule will apply to many classes.

Hilikus
  • 9,954
  • 14
  • 65
  • 118
  • If I'm correct you don't want your model class to get obfuscate by R8 rules then you can use a line something line this `-keep class com.example.example.models.** { *; }` you can place all your model class into a single package and when proguard applies then this package is excluded for obfuscation. – CodeRED Innovations Jul 29 '20 at 22:30

2 Answers2

1

You should be able to use a conditional rule like this:

-if class * {
  @a.b.c.DocumentId *** *;
}
-keep class <1> {
  *;
}

Which says exactly that if a class has a field with the annotation, then keep all members of that class.

aruh
  • 396
  • 6
  • 10
sgjesse
  • 3,793
  • 14
  • 17
  • Depending on how you ensure that the field annotated with `a.b.x.DocumentId` is in the program this might not work as expected, See [issue 162921738](https://issuetracker.google.com/162921738). – sgjesse Aug 05 '20 at 12:30
  • To me it looks like you also need an additional `-keep` / `-keepclasseswithmembers` to keep the class with the field annotated with `@DocumentId` in the first place (e.g. `-keep class * { @DocumentId ; }`), otherwise the `-if` seems to have no effect (?), see also [related investigation with Gson](https://github.com/google/gson/issues/2401). – Marcono1234 May 29 '23 at 12:54
  • True, the rule will keep all members if at least one annotated field is live. That field could be live from direct use in the program or through a keep rule if the field is only accessed through reflection. This depends on the actual use in the application. Also please take a look at https://r8.googlesource.com/r8/+/refs/heads/main/compatibility-faq.md, and if you find that is lacking information please file a bug on https://issuetracker.google.com/issues/new?component=326788. GSON also provide some shrinking info: https://github.com/google/gson/blob/master/Troubleshooting.md#type-token-raw. – sgjesse May 30 '23 at 09:43
0

I just read about -keepclasseswithmembers, which would equate to an inferred @Keep based on the contents of the class:

-keepclasseswithmembers class * { 
  @a.b.c.DocumentId <fields>;
}

A more strict alternative that really boils down to the -keepclassmembers would be to annotate the DTOs with something r8 can detect (for example, an annotation with retention=runtime) and then just target that annotation in the rule:

-keepclassmembers @a.b.c.Dto class *
karllindmark
  • 6,031
  • 1
  • 26
  • 41
  • Your first solution doesn't work. i had tried that. that solution is exactly what i mentioned in my question that worked only to preserve the field that's annotated, not its "sibling" fields. The second solution would probably work, but my goal is to not pollute the code with things that are meant for proguard only – Hilikus Jul 30 '20 at 13:14
  • Wait, you already tried `keepclasseswithmembers`? Note the `withmembers` part - I didn't see it mentioned in your post. – karllindmark Jul 30 '20 at 13:41
  • oh sorry. my bad. what i mentiond was not *with* members, but i had tried that and it didn't work either: getBar still gets removed. what i understand is that that instruction is to preserve a class from being removed, not its content – Hilikus Jul 30 '20 at 20:58