Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ProGuard ignoring annotation default values

The Issue

I'm having a problem with ProGuard 4.11 (An application that can optimize, shrink and obfuscate Java code), using the proguard-maven-plugin (github.com/wvengen/proguard-maven-plugin) (Although that shouldn't matter that much, since the error is occurring at runtime and the maven plugin just calls the binary with some arguments as far as I know).

I have an annotation class which is stuctured like this (nothing special):

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD)
public @interface SqlValueCache {
  String value();

  SqlValueCache.Type type() default Type.OBJECT_UPDATE; //Type is just an ordinary enum
}

I also have some fields annotated with that annotation, but I'm skipping the type() parameter because I want to use the default value:

 @SqlValueCache("nickname")
 protected SqlValueHolder<String> nick;

Now I want to process that annotation at runtime:

SqlValueCache annotation = field.getAnnotation(SqlValueCache.class); //Skipped the validation
annotation.value(); //Works fine, because specified
annotation.type(); //java.lang.annotation.IncompleteAnnotationException, *not* using the default

As stated in the comment above, I get an IncompleteAnnotationException, stating that my annotation declaration is missing the type() value. But that value should be implied by the default Type.OBJECT_UPDATE! So now I'm wondering, why is that happening?

Assumptions

I assume that the default thing is stored in some kind of attribute that I need to specify in -keepattributes, but I haven't been able to figure out if this is true or which one it is.

Reproduction

It does work just as intended when not using ProGuard.

I have also made sure that the problem is in fact the missing implied value - The code runs as intended when using ProGuard, but explicitly specifying the type(), like so:

 @SqlValueCache(value = "nickname", type = Type.OBJECT_UPDATE)
 protected SqlValueHolder<String> nick;

I am using this method as a temporary workaround, but this isn't the prettiest solution in my opinion. Also, as stated above, I still want to know why this error is happening.

Thanks in advance for reading and investigating my question! :)

Appendix/Meta

Yes, I did search the web for solutions and did also use the StackOverflow searchbox. Using the title of this question and various other search queries, I could only find questions complaining about no annotations being kept at all or asking to keep subclasses of classes annotated, etc. I also searched for the exception, but the only useful result was the JavaDoc (docs.oracle.com/javase/7/docs/api/java/lang/annotation/IncompleteAnnotationException.html) of the exception I am encountering.

I'm sorry about the links, but I apparently need 10 reputation to post more than two, although I really like linking stuff :/

Stacktrace

I have attached the Stacktrace I got in console (class names won't be useful at all in my opinion - I can attach them if really necessary; My classes are also loaded by a custom class loader, but I doubt that that makes any difference):

java.lang.ExceptionInInitializerError
at <class with annotated field>
(...)
Caused by: java.lang.annotation.IncompleteAnnotationException: io.github.xxyy.common.sql.builder.annotation.SqlValueCache missing element type
at sun.reflect.annotation.AnnotationInvocationHandler.invoke(AnnotationInvocationHandler.java:72) ~[?:1.7.0_51]
at com.sun.proxy.$Proxy18.type(Unknown Source)
at <line accessing type() of SqlValueCache>
(...)

ProGuard config

Here's my ProGuard config, should that be of any help (That's the part I find relevant to this question - Full file here: pastebin.com/u6wt00cj):

-dontskipnonpubliclibraryclassmembers
-target 1.7
-dontshrink
-dontoptimize
-repackageclasses io.github.xxyy.obf.towerdefense.client
-keepattributes SourceFile,LineNumberTable,*Annotations*,LocalVariable*Table

-keep,allowobfuscation class ** {
    <fields>;
    <methods>;
}

-keep,allowshrinking class **.annotation** {
    <fields>;
    <methods>;
}

# Also keep - Enumerations. Keep the special static methods that are required in
# enumeration classes.
-keepclassmembers enum  ** {
  public static **[] values();
  public static ** valueOf(java.lang.String);
}
like image 246
Philipp Nowak Avatar asked Oct 20 '22 10:10

Philipp Nowak


1 Answers

You also need to keep the AnnotationDefault attribute:

-keepattributes AnnotationDefault

You can get the same effect by changing *Annotations* to *Annotation* in your current -keepattributes option, so the wildcards match AnnotationDefault.

like image 135
Eric Lafortune Avatar answered Oct 23 '22 10:10

Eric Lafortune