2010-03-03 3 views
7

Qualcuno ha mai provato a combinare l'uso di google guice con l'offuscamento (in particolare proguard)? La versione offuscata del mio codice non funziona con google guice in quanto guice si lamenta dei parametri di tipo mancanti. Questa informazione sembra essere cancellata dalla fase di trasformazione che proguard fa, anche quando le classi rilevanti sono escluse dall'offuscamento.L'iniezione con google guice non funziona più dopo l'offuscamento con proguard

L'analisi dello stack si presenta così:

com.google.inject.CreationException: Guice creation errors: 

1) Cannot inject a Provider that has no type parameter 
    while locating com.google.inject.Provider 
    for parameter 0 at de.repower.lvs.client.admin.user.administration.AdminUserCommonPanel.setPasswordPanelProvider(SourceFile:499) 
    at de.repower.lvs.client.admin.user.administration.AdminUserCommonPanel.setPasswordPanelProvider(SourceFile:499) 
    while locating de.repower.lvs.client.admin.user.administration.AdminUserCommonPanel 
    for parameter 0 at de.repower.lvs.client.admin.user.administration.b.k.setParentPanel(SourceFile:65) 
    at de.repower.lvs.client.admin.user.administration.b.k.setParentPanel(SourceFile:65) 
    at de.repower.lvs.client.admin.user.administration.o.a(SourceFile:38) 

2) Cannot inject a Provider that has no type parameter 
    while locating com.google.inject.Provider 
    for parameter 0 at de.repower.lvs.client.admin.user.administration.AdminUserCommonPanel.setWindTurbineAccessGroupProvider(SourceFile:509) 
    at de.repower.lvs.client.admin.user.administration.AdminUserCommonPanel.setWindTurbineAccessGroupProvider(SourceFile:509) 
    while locating de.repower.lvs.client.admin.user.administration.AdminUserCommonPanel 
    for parameter 0 at de.repower.lvs.client.admin.user.administration.b.k.setParentPanel(SourceFile:65) 
    at de.repower.lvs.client.admin.user.administration.b.k.setParentPanel(SourceFile:65) 
    at de.repower.lvs.client.admin.user.administration.o.a(SourceFile:38) 

2 errors 
    at com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:354) 
    at com.google.inject.InjectorBuilder.initializeStatically(InjectorBuilder.java:152) 
    at com.google.inject.InjectorBuilder.build(InjectorBuilder.java:105) 
    at com.google.inject.Guice.createInjector(Guice.java:92) 
    at com.google.inject.Guice.createInjector(Guice.java:69) 
    at com.google.inject.Guice.createInjector(Guice.java:59) 

Ho cercato di creare un piccolo esempio (senza utilizzare Guice) che sembra riprodurre il problema:

package de.repower.common; 

import java.lang.reflect.Method; 
import java.lang.reflect.ParameterizedType; 
import java.lang.reflect.Type; 

class SomeClass<S> { 
} 

public class ParameterizedTypeTest { 

    public void someMethod(SomeClass<Integer> param) { 
     System.out.println("value: " + param); 
     System.setProperty("my.dummmy.property", "hallo"); 
    } 

    private static void checkParameterizedMethod(ParameterizedTypeTest testObject) { 
     System.out.println("checking parameterized method ..."); 
     Method[] methods = testObject.getClass().getMethods(); 
     for (Method method : methods) { 
      if (method.getName().equals("someMethod")) { 
       System.out.println("Found method " + method.getName()); 
       Type[] types = method.getGenericParameterTypes(); 
       Type parameterType = types[0]; 
       if (parameterType instanceof ParameterizedType) { 
        Type parameterizedType = ((ParameterizedType) parameterType).getActualTypeArguments()[0]; 
        System.out.println("Parameter: " + parameterizedType); 
        System.out.println("Class: " + ((Class) parameterizedType).getName()); 
       } else { 
        System.out.println("Failed: type ist not instance of ParameterizedType"); 
       } 
      } 
     } 
    } 

    public static void main(String[] args) { 
     System.out.println("Starting ..."); 
     try { 
      ParameterizedTypeTest someInstance = new ParameterizedTypeTest(); 
      checkParameterizedMethod(someInstance); 
     } catch (SecurityException e) { 
      e.printStackTrace(); 
     } 

    } 

} 

Se si esegue questo codice unsbfuscated , l'output è il seguente:

Starting ... 
checking parameterized method ... 
Found method someMethod 
Parameter: class java.lang.Integer 
Class: java.lang.Integer 

Ma eseguendo la versione offuscata con pro i rendimenti di guardia:

Starting ... 
checking parameterized method ... 
Found method someMethod 
Failed: type ist not instance of ParameterizedType 

Queste sono le opzioni che ho usato per l'offuscamento:

-injars classes_eclipse\methodTest.jar 
-outjars classes_eclipse\methodTestObfuscated.jar 

-libraryjars 'C:\Program Files\Java\jre6\lib\rt.jar' 

-dontskipnonpubliclibraryclasses 
-dontskipnonpubliclibraryclassmembers 
-dontshrink 
-printusage classes_eclipse\shrink.txt 
-dontoptimize 
-dontpreverify 
-verbose 


-keep class **.ParameterizedTypeTest.class { 
    <fields>; 
    <methods>; 
} 

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

# Keep - Applications. Keep all application classes, along with their 'main' 
# methods. 
-keepclasseswithmembers public class * { 
    public static void main(java.lang.String[]); 
} 

# 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); 
} 

# Also keep - Database drivers. Keep all implementations of java.sql.Driver. 
-keep class * extends java.sql.Driver 

# Also keep - Swing UI L&F. Keep all extensions of javax.swing.plaf.ComponentUI, 
# along with the special 'createUI' method. 
-keep class * extends javax.swing.plaf.ComponentUI { 
    public static javax.swing.plaf.ComponentUI createUI(javax.swing.JComponent); 
} 

# Keep names - Native method names. Keep all native class/method names. 
-keepclasseswithmembers,allowshrinking class * { 
    native <methods>; 
} 

# Keep names - _class method names. Keep all .class method names. This may be 
# useful for libraries that will be obfuscated again with different obfuscators. 
-keepclassmembers,allowshrinking class * { 
    java.lang.Class class$(java.lang.String); 
    java.lang.Class class$(java.lang.String,boolean); 
} 

Qualcuno ha un'idea di come risolvere questo (a parte la soluzione ovvia per mettere i file in questione in un separato barattolo e non lo offuscare)?

Con i migliori saluti,
Stefan

risposta

8

Dopo aver usato Proguard per una buona quantità di tempo, ecco come ho deciso di risolvere i problemi in materia di riflessione (e Guice è solo un caso d'uso di esso).

Reflection può essere utilizzato con Proguard purché NO nome classe o metodi sia inserito come Stringhe.

Vale a dire che questo codice è valido e funzionerà dopo ProGuard offuscamento

Class someClass = Class.forName(SomeClass.class.getName()); 

mentre questo codice non funzionerà

Class someClass = Class.forName("SomeClass"); 

Inoltre, Proguard si ridurrà metodi non richiamato e costruttore. Di conseguenza, il metodo Class.newInstance non funzionerà. Sfortunatamente, i soliti legami di Guice funzionano con questo metodo.

Ciò ha alcune conseguenze sul codice Guice.

  • Tutte le iniezioni devono essere prodotte utilizzando i metodi annotati da @Provides, poiché ProGuard ridurrà le classi a causa del fatto che i loro costruttori non vengono chiamati esplicitamente.
  • Proguard non deve ridurre il codice delle classi di moduli.
  • ProGuard non deve ridurre le annotazioni, quale e ovunque esse siano (questo può essere configurato, ma non riesco a ricordare dove).
+1

Wow, questa è una pessima notizia che devi usare i metodi @Provides solo per impedire a ProGuard di rimuovere le dipendenze. Questo richiede uno strumento per aiutare. Presumo che funzionerebbe anche (quasi ugualmente fastidiosamente) attaccare un gruppo di linee di mantenimento nella configurazione di proguard? –

+0

Forse perché aggiungere l'annotazione -keep renderà i refactoring meno efficaci, poiché in jar di uscita dovremo assicurarci che i nomi delle classi siano validi. A mio parere, il funzionamento su un livello Java puro rende il nostro codice più conforme al refactoring (se esiste una cosa simile) – Riduidel

7

L'attributo "Firma" è necessario per poter accedere a tipi generici durante la compilazione in JDK 5.0 e versioni successive.

Usa -keepattributes Firma per risolvere l'errore con ParameterizedType

+0

Non ha risolto il problema dell'esempio precedente. Devo ancora provarlo sul mio problema originale con google guice. – sme

+1

Questo lo ha risolto per me su Android con la build no-aop, stavo avendo lo stesso problema dell'OP. Ho anche il seguente in proguard config. -keepclassmembers class * { @ com.google.inject.Inject (...); } – abombss

+0

Questo ha funzionato anche per me, con lo stesso problema del richiedente la domanda !! – Peterdk

0

seguito il codice funziona per me, avendo avuto lo stesso problema.

-keepattributes Signature era la soluzione.

-optimizationpasses 5 
-dontusemixedcaseclassnames 
-dontskipnonpubliclibraryclasses 
-dontpreverify 
#-dontobfuscate 
-repackageclasses '' 
-keepattributes *Annotation* 
-keepattributes Signature 
-verbose 
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 

-keep public class * extends android.app.Activity 
-keep public class * extends android.app.Application 
-keep public class * extends android.app.Service 
-keep public class * extends android.content.BroadcastReceiver 
-keep public class * extends android.content.ContentProvider 
-keep public class * extends android.app.backup.BackupAgentHelper 
-keep public class * extends android.preference.Preference 
-keep public class com.android.vending.licensing.ILicensingService 

-keepclasseswithmembernames class * { 
    native <methods>; 
} 

-keepclasseswithmembernames class * { 
    public <init>(android.content.Context, android.util.AttributeSet); 
} 

-keepclasseswithmembernames class * { 
    public <init>(android.content.Context, android.util.AttributeSet, int); 
} 

-keepclassmembers enum * { 
    public static **[] values(); 
    public static ** valueOf(java.lang.String); 
} 
-keepattributes Signature 
-keep class * implements android.os.Parcelable { 
    public static final android.os.Parcelable$Creator *; 
} 
-keep class com.google.inject.Binder 
-keepclassmembers class * { 
    @com.google.inject.Inject <init>(...); 
} 
-keep public class * extends android.view.View { 
    public <init>(android.content.Context); 
    public <init>(android.content.Context, android.util.AttributeSet); 
    public <init>(android.content.Context, android.util.AttributeSet, int); 
    public void set*(...); 
} 

# I didn't need this one, maybe you need it. 
#-keep public class roboguice.** 

-keepclassmembers class **.R$* { 
    public static <fields>; 
} 
3

Con la versione corrente di Proguard (4.7) sono stato in grado di farlo funzionare aggiungendo il seguente: -

-keepattributes *Annotation*,Signature 
-keep class com.google.inject.Binder  
-keep public class com.google.inject.Inject 
# keeps all fields and Constructors with @Inject 
-keepclassmembers,allowobfuscation class * { 
    @com.google.inject.Inject <fields>; 
    @com.google.inject.Inject <init>(...); 
} 

Oltre a mantenere in modo esplicito qualsiasi classe che viene creato da Guice esempio

-keep class com.example.Service 
+0

Questa modifica non richiede esplicitamente aggiungendo le vostre classi @Inject (che, come già sottolineato in altre messaggi è soggetto a errori): '-keepclasseswithmembers, allowoptimization, classe allowobfuscation * { @ com.google.inject.Inject ; } - keepclassmembers, allowobfuscation, allowoptimization class * { @ com.google.inject.Provides ; } ' – enl8enmentnow