Guardsquare / proguard

ProGuard, Java optimizer and obfuscator
https://www.guardsquare.com/en/products/proguard
GNU General Public License v2.0
2.81k stars 405 forks source link

Clarify meaning of "present" for `-if` option #344

Open Marcono1234 opened 1 year ago

Marcono1234 commented 1 year ago

The documentation for the -if option currently says:

Specifies classes and class members that must be present to activate the subsequent keep option

Could you please clarify what "present" here means? I assume it means "present after the shrinking phase" (in that case please adjust the documentation). Because for example in the context of R8 (and I assume the same applies to ProGuard) I saw usages like -if class *, and without this "after shrinking", that option -if class * would seem like a no-op / be always true.

As side note: The Dagger and Butterknife examples referenced from the -if documentation and various other places don't seem to exist anymore.

mrjameshamilton commented 1 year ago

Hi @Marcono1234 ,

These kind of rules are normally used where some library/tool generates classes based on the name of other classes e.g. some Foo_TypeAdapter class is generated for a class named Foo; then you can write a keep rule like the following which captures the prefix using a wildcard to keep the original class but not the _Adapter class:

-if class **_Adapter
-keep class <1>

See this example on the ProGuard Playground: https://playground.proguard.com/p/YLS4pH

-if class * on it's own seems like a mistake.

Marcono1234 commented 1 year ago

Thanks for the answer! Is there a reason then why the documentation has "present" formatted as "present"? I assumed this meant something special.

-if class * on it's own seems like a mistake.

Maybe that is some (undocumented) special behavior for R8 then, see also https://github.com/google/gson/pull/2397#issuecomment-1575236086.

EricLafortune commented 1 year ago

It's a good question. To be precise, -if refers to classes before each shrinking/optimization/obfuscation step. Initially, this is your original code, but with -optimizationpasses 2 (or more), this can also be the code after a prior shrinking/optimization step. Technically, -if could be applied during the shrinking step, but it would then have to be applied recursively. Right now, it's more conservatively iterative for the subsequent passes.

-if class * could be useful if the corresponding -keep option refers back to the class wildcard *. For some framework that relies on reflection, the following could be necessary:

-if   class *
-keep class <1>_Foo

Such a construct may be suboptimal, if the kept classes aren't really necessary (as always).

Marcono1234 commented 1 year ago

Thanks for the clarification!

To be precise, -if refers to classes before each shrinking/optimization/obfuscation step.

If I understand it correctly then, for ProGuard an -if class * would always keep the class, since it is also executed before the first shrinking step. I assume for R8 it might be different then because if I recall correctly it did make a difference there whether -if class * was used or not. So maybe they are performing some shrinking or optimization already before the first evaluation of the -if. -if class * has also been recommended a few times by R8 developers, for example in https://issuetracker.google.com/issues/150189783#comment11.

Such a construct may be suboptimal, if the kept classes aren't really necessary (as always).

It appears to me that even though it is suboptimal, for R8 it is at least better than just using -keep which would always keep the class, so I assume that is why the R8 developers recommended it.

Would it be possible to adjust the ProGuard documentation to replace or adjust the confusingly formatted "present" (as mentioned above)? Maybe it would also be useful to mention in the documentation what you said about -if being evaluated after every shrinking / optimization step, in case that has any potentially relevant effects for ProGuard.

EricLafortune commented 1 year ago

My musings about the technical details may have added to the confusion -- the bottom line is that ProGuard may still remove both the -if class and the subsequent -keep class if they are not explicitly kept in your further configuration, and not used in your app (the former class in a first pass, the latter class in a second pass because the former class is gone). Only if the former class is kept or used, you can be sure the latter class remains too.

The automatically converted highlight present should be a more apt present or just present.

The initial rule for GSON in the R8 tracker seems correct to me. The correction suggested later probably works, but the -if class * makes no sense there -- the rule should work without it (with <1> just specified as *).