Can anyone explain the Proguard configuration setting: -keep @interface *
Asked Answered
R

1

7

This is the config setting in question: -keep @interface *

This setting is referenced in this post: https://mcmap.net/q/1628547/-annotations-having-no-effect-in-proguard

And here in the Proguard manual: https://www.guardsquare.com/en/proguard/manual/troubleshooting#notkept

But the manual (or the post) doesn't explain how or why this config setting works. I would think that this config value would keep any annotations that were defined in your application. I emphasize the word defined here because I want to compare it to where an annotation is used. So if I defined an annotation in my code like public @interface MyAnnotation {} then I would expect this config setting to preserve it. However, if I were to use my annotation in another class like public class MyClass { @MyAnnotation public void run(){} } then I would not expect this config setting to preserve it there. I would think that to preserve the annotation where it's used, you would need a setting like -keep class * { @interface <methods>; }. However, my understanding is clearly wrong, because the -keep @interface * config setting does preserve the annotations where they're used.

Can anyone explain why the -keep @interface * config setting affects annotations where they're used inside other classes? Additionally, the Proguard manual never explains how to use @interface keyword at all. Is that like a wildcard for any annotation? Or does it refer to the definition of the annotation like in my example public @interface MyAnnotation {}? Or is it something else entirely?

Recurrence answered 22/4, 2018 at 3:33 Comment(0)
S
1

When every interface is compiled, even with proguard, it should create a valid class file that can be used in the java virtual machine. Java has specific naming rules for connecting interfaces and classes in packages with the files.

So for example even if interface VeryImportantInterface of package com.mycompany.mypackage is obfuscated and as a result you get: interface a in package b.c.d java expects to find interface a in package b.c.d in a file named a.class

Similar rules apply for inner classes and interfaces.

So if you do obfuscate the definition of a class or interface, it will have to be mentioned everywhere with its obfuscated name.Otherwise if for example class MyClass.class is obfuscated to a.class and another class, e.g. MyClassReference still references this class as MyClass then a ClassNotFoundException will be thrown when MyClassReference attempts to use MyClass for the first time.

Regarding -keep @interface * As specified in ProGuard RefCard

'keep' prevents classes and class members from being removed or renamed.

ProGuard RefCard also mentions that wildcard * can be used

  1. * can be used for any number of characters (but not the package separator)
  2. the class name * refers to any class, irrespective of its package.
  3. * matches any field or method

So -keep @interface * according to the definitions above applies to public @interface MyAnnotation {} since its name match the wildcard *. Since according to the definition MyAnnotation is not to be removed, it is not removed from anywhere.

On the contrary -keep class * { @interface <methods>; } is more specific and would keep only the usages of the annotation.

Another thing to consider is that keep is followed by a Class specification. And based on the Class specification

The @ specifications can be used to restrict classes and class members to the ones that are annotated with the specified annotation types. An annotationtype is specified just like a classname.

During the shrinking process pro guard will delete all the methods and members that are directly or indirectly used plus the ones specified by -keep. However it does not explicitly mention what it does with annotations. Pro-Guard mentions regarding shrinking the following:

By default, shrinking is applied; all classes and class members are removed, except for the ones listed by the various -keep options, and the ones on which they depend, directly or indirectly. Shrinking Options So one case could be that pro-guard somehow detects that the annotation is used and prevents it from beeing removed during shrinking. Another case would be that the annotations are considered metadatum of the methods and not beeing considered members. It is really not obvious or well documented in proguard documentation.

You can use -whyareyoukeeping @interface * and -verbose to get a feedback from Prog-Guard on why it is keeping the related interfaces.

Update: Since the result of the above configuration directives was: ... is kept by a directive in the configuration., it can only be concluded that the -keep @interface * actually . Hence it is does not provide any additional clarification. Without any specific explanation on the Pro-Guard documentation I think the following 2 possible explanations are possible: 1. The proguard shrinking step considers that the annotation is used in some part of the code, thus needed and is not reduced. 2. The class_specification @interface * matches both annotated methods and annotation declarations.

Snapdragon answered 9/5, 2018 at 14:48 Comment(8)
Thanks for the response, but I feel like you've only confirmed my question. First of all, as I mentioned, I'm only shrinking, not obfuscating. As you say, the -keep @interface * should really only apply to where annotations are defined like your example public @interface MyAnnotation {} -- but the whole point of my question is that this isn't what's actually happening. This -keep @interface * command is keeping annotations where they're used (please reread my original post for more context). I'm trying to understand why.Recurrence
I will rephrase to make the answer more clear the point where this answer covers your question. Thanks for pointing out that you are shrinking in your comment. I just noticed the shrink tag in your question. I had only read the body, where this is not mentioned.Snapdragon
PS: You are right, the answer does not clearly answer your question, and the proguard documentation, really does not provide enough detail. Can you use -whyareyoukeeping @interface * ,-whyareyoukeeping @* and -verbose to get a feedback from Prog-Guard on why it is keeping the related interfaces. You can add it to your question and we can try to see what is going. It is really very interesting.Snapdragon
The -whyareyoukeeping @* option doesn't work, it just throws an error. I tried running with the -whyareyoukeeping @interface * config and the result was: ... is kept by a directive in the configuration. Not very helpful unfortunately :/Recurrence
Thank you for trying it! Indeed it is not very helpful. Are there any specific references of this annotation ? For example using reflection to search for the annotation?Snapdragon
Well the annotation is used directly on some methods/classes like public class MyClass { @MyAnnotation public void run(){} }. Of course all the annotation-based processing is done using reflection. Does that answer your question?Recurrence
Yes, I am trying to figure out if for example Proguard might think that @MyAnnotation is used and required in the methods signature. So then ProGuard does not shrink them.Snapdragon
If I remove the -keep @interface * from my config, then the annotations are stripped from those methods/classes where they're used, even though the methods/classes themselves remain. Adding the -keep @interface * config changes the behavior so that it now keeps the annotations on the methods/classes where they're used. So it seems clear to me that the -keep @interface * config is making the difference here, but I don't understand why, hence why I posted this question.Recurrence

© 2022 - 2024 — McMap. All rights reserved.