2

I'm using proguard to shrink a shaded jar for use as a command line tool. The shaded jar works fine but I'm getting an exception when running the jar created by proguard. The app uses Guice injection and I added configuration that I found on SO, mostly in this answer.

This is the Exception:

Exception in thread "main" java.lang.reflect.MalformedParameterizedTypeException
        at sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl.validateConstructorArguments(ParameterizedTypeImpl.java:60)
        at sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl.<init>(ParameterizedTypeImpl.java:53)
        at sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl.make(ParameterizedTypeImpl.java:95)
        at sun.reflect.generics.factory.CoreReflectionFactory.makeParameterizedType(CoreReflectionFactory.java:105)
        at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:140)
        at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
        at sun.reflect.generics.repository.ClassRepository.getSuperclass(ClassRepository.java:84)
        at java.lang.Class.getGenericSuperclass(Class.java:696)
        at com.google.inject.internal.MoreTypes.getGenericSupertype(MoreTypes.java:273)
        at com.google.inject.TypeLiteral.getSupertype(TypeLiteral.java:257)
        at com.google.inject.spi.InjectionPoint.hierarchyFor(InjectionPoint.java:755)
        at com.google.inject.spi.InjectionPoint.getInjectionPoints(InjectionPoint.java:635)
        at com.google.inject.spi.InjectionPoint.forInstanceMethodsAndFields(InjectionPoint.java:356)
        at com.google.inject.internal.ConstructorBindingImpl.getInternalDependencies(ConstructorBindingImpl.java:151)
        at com.google.inject.internal.InjectorImpl.getInternalDependencies(InjectorImpl.java:585)
        at com.google.inject.internal.InjectorImpl.cleanup(InjectorImpl.java:543)
        at com.google.inject.internal.InjectorImpl.initializeJitBinding(InjectorImpl.java:529)
        at com.google.inject.internal.InjectorImpl.createJustInTimeBinding(InjectorImpl.java:847)
        at com.google.inject.internal.InjectorImpl.createJustInTimeBindingRecursive(InjectorImpl.java:772)
        at com.google.inject.internal.InjectorImpl.getJustInTimeBinding(InjectorImpl.java:256)
        at com.google.inject.internal.InjectorImpl.getBindingOrThrow(InjectorImpl.java:205)
        at com.google.inject.internal.InjectorImpl.getInternalFactory(InjectorImpl.java:853)
        at com.google.inject.internal.FactoryProxy.notify(FactoryProxy.java:46)
        at com.google.inject.internal.ProcessedBindingData.runCreationListeners(ProcessedBindingData.java:50)
        at com.google.inject.internal.InternalInjectorCreator.initializeStatically(InternalInjectorCreator.java:133)
        at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:106)
        at com.google.inject.Guice.createInjector(Guice.java:95)
        at com.google.inject.Guice.createInjector(Guice.java:72)
        at com.google.inject.Guice.createInjector(Guice.java:62)
        at com.acme.ui.cli.Main.main(Main.java:44)

And this the configuration that's producing it:

-injars acme-cli-0.2.1-SNAPSHOT.jar
-outjars target/acme-cli-0.2.1-SNAPSHOT.jar

-libraryjars /Library/Java/JavaVirtualMachines/jdk1.7.0_25.jdk/Contents/Home/jre/lib/jce.jar
-libraryjars /Library/Java/JavaVirtualMachines/jdk1.7.0_25.jdk/Contents/Home/jre/lib/jsse.jar
-libraryjars /Library/Java/JavaVirtualMachines/jdk1.7.0_25.jdk/Contents/Home/jre/lib/rt.jar

-dontobfuscate
-dontpreverify
-dontoptimize
-printmapping mapping.map
-ignorewarnings
-keepattributes *Annotation*,Signature  


# Keep - Applications. Keep all application classes, along with their 'main' methods.
-keep public class com.acme.ui.cli.Main {
    public static void main(java.lang.String[]);
}


# Keep Guice-related classes
-keep public class javax.inject.**
-keep class com.google.inject**
-keep class * extends com.google.inject.AbstractModule

 # keeps all fields and Constructors annotated with @Inject and @AssistedInject
-keepclasseswithmembers class * {
    @com.google.inject.Inject <fields>;
    @com.google.inject.Inject <init>(...);
}

-keepclasseswithmembers class * {
    @com.google.inject.assistedinject.AssistedInject <fields>;
    @com.google.inject.assistedinject.AssistedInject <init>(...);
}

When I add this the problem goes away:

-keep class * {
   <init>(...);
}

But it keeps many more classes and doubles the size of the jar. I'm using proguard 4.10 and tried 4.9 too. These are some of the option I've experimented with that seemed most relevant:

    -keepattributes *
    -keepparameternames
    -keep class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl {
        <init>(...);
    }

Since it's seems related to Guice and keeping all constructors solves the problem, I've also experimented with the various ways you can configure the keep options without results. But I must say that I don't understand them fully.

I'm not sure if it's related to the problem, but what looks odd is that the only constructor in the class ParameterizedTypeImpl has a signature like this: private ParameterizedTypeImpl(java.lang.Class<?> aClass, java.lang.reflect.Type[] types, java.lang.reflect.Type type), but the signature in the stacktrace looks like it's accepting ParameterizedTypeImpl itself: ParameterizedTypeImpl.<init>(ParameterizedTypeImpl.java:53).

Now I'm stuck; Any suggestions that could help me further are very appreciated.

Community
  • 1
  • 1
Rolf W.
  • 1,379
  • 1
  • 15
  • 25
  • Have you been able to solve this issue? – Eloff Jan 01 '14 at 20:46
  • I also posted the [same question on the proguard forum](https://sourceforge.net/p/proguard/discussion/182456/thread/107e0e6b/) where I got a reply. There are some suggestions you might be able to use but the main culprit appears to be a bug. We didn't get to posting an example yet for the maintainer of proguard to investigate, might take another two weeks for us to do so. – Rolf W. Jan 04 '14 at 07:17
  • I'll post our findings here if useful. If you find out anything useful before we do, would you mind sharing it too? – Rolf W. Jan 04 '14 at 07:29

1 Answers1

1

This is a known bug that was fixed in Proguard 5.0. See https://sourceforge.net/p/proguard/bugs/518/

Gili
  • 86,244
  • 97
  • 390
  • 689
  • Kudos for isolating the problem. We didn't have the faintest clue after many hours. Now we tried the workaround; Even though we use logback too, in our case it still gives the same exception with no apparent difference. No doubt we need to include one or more other interfaces in the workaround because it's a large project with many dependencies; Did you find the culprit by process of elimination or is there another thing we could try? – Rolf W. May 06 '14 at 16:47
  • It works if we keep all interfaces by the way. So we know our problem is caused by the same issue. – Rolf W. May 06 '14 at 17:36
  • @RolfW. Yes, I kept on removing Proguard rules and the classes being processed until I ended up with a minimal testcase necessary to end up with this exception. It's took a lot of work, but thankfully it was worth the effort in the end. I pushed harder than normal because I knew Eric has a good track record of fixing bugs fast :) – Gili May 08 '14 at 02:39
  • We did something similar but didn't go all the way. Most tedious and time-consuming is having to re-run the process each time, isn't it? Thanks a lot for pushing through and letting me know! I'm very happy this problem is solved all of a sudden. – Rolf W. May 08 '14 at 17:38