2010-09-17 17 views
2

en réflexion, le champ privé peut être accessible via getDeclaredField() et setAccessible (true). Comment accéder au champ privé d'une classe externe via Objectweb ASM bytecode API? Je mis pour obtenir le domaine privé de quelque chose comme, parObtenir java.lang.IllegalAccessError lors de l'accès au champ privé d'une classe externe via ASM Java Bytecode

Field current = sourceObject.getDeclaredField(privateFieldName); 
Field.setAccessible(true); 
Type sourceType = Type.getType(sourceObject.getClass()); 
mv.visitFieldInsn(Opcodes.GETFIELD, 
        sourceType.getInternalName(), 
        privateFieldname, 
        Type.getDescriptor(current.getType())); 

Lorsque le code octet est exécuté et pour obtenir le champ privé, j'ai toujours eu une erreur « java.lang.IllegalAccessError »

Tous indice? Merci un paquet,

+0

Bonjour, Toute pensée? Merci, –

+0

Oui. Vois ma réponse. – aioobe

Répondre

2

Vous ne pouvez pas le faire comme ça. Le setAccessible(true) affecte uniquement la référence de champ en cours dans l'exécution actuelle de votre programme (c'est-à il n'affectera pas l'exécution du programme modifié résultant).

Pour accéder à un champ privé lors de l'exécution de votre programme modifié, vous devez essentiellement intégrer les étapes de réflexion correspondantes dans le programme.

Pour accéder à un champ privé YourClass.thePrivatefield d'un objet stocké dans la variable locale varId vous faites quelque chose comme

// Get hold of the field-reference 
mv.visitLdcInsn(Type.getType("LYourClass;")); 
mv.visitLdcInsn("thePrivateField"); 
mv.visitMethodInsn(INVOKEVIRTUAL, 
        "java/lang/Class", 
        "getDeclaredField", 
        "(Ljava/lang/String;)Ljava/lang/reflect/Field;"); 

// Duplicate the reference 
mv.visitInsn(DUP); 

// Call setAccessible(true) using the first reference. 
mv.visitInsn(ICONST_1); 
mv.visitMethodInsn(INVOKEVIRTUAL, 
        "java/lang/reflect/Field", 
        "setAccessible", 
        "(Z)V"); 

// Call get(yourObject) using the second reference to the field. 
mv.visitInsn(ALOAD, varId); 
mv.visitMethodInsn(INVOKEVIRTUAL, 
        "java/lang/reflect/Field", 
        "get", 
        "(Ljava/lang/Object;)Ljava/lang/Object;"); 

Si le champ que vous essayez de rendre accessible fait partie de la base de COBE que votre En réécrivant, vous pouvez évidemment rendre ce champ public en utilisant ACC_PUBLIC au lieu de ACC_PRIVATE.

+0

Génial. Je vais essayer. En passant, est-ce Java ASM ou BCEL? Merci –

+1

ASM ............. – aioobe

0

Le problème réel est que vous ne pouvez pas accéder légalement à ces variables. En effet, la JVM a défini ses règles d'accès avant que Java n'ait des classes internes. Ainsi, javac crée des accesseurs synthétiques pour les champs auxquels elle ne peut pas accéder légalement dans la JVM, mais en Java. Par exemple,

class Sample { 
    private int i = 0; 
    class Inner { 
     int foo = i; 
    } 
} 

Ensuite, nous pouvons utiliser javap décompiler les classes générées.

[email protected]:/tmp$ javap -private Sample 
Compiled from "Sample.java" 
class Sample extends java.lang.Object{ 
    private int i; 
    Sample(); 
    static int access$000(Sample); 
} 

[email protected]:/tmp$ javap -c Sample.Inner 
Compiled from "Sample.java" 
class Sample$Inner extends java.lang.Object{ 
int foo; 

final Sample this$0; 

Sample$Inner(Sample); 
    Code: 
    0: aload_0 
    1: aload_1 
    2: putfield #1; //Field this$0:LSample; 
    5: aload_0 
    6: invokespecial #2; //Method java/lang/Object."<init>":()V 
    9: aload_0 
    10: aload_0 
    11: getfield #1; //Field this$0:LSample; 
    14: invokestatic #3; //Method Sample.access$000:(LSample;)I 
    17: putfield #4; //Field foo:I 
    20: return 

} 

Notez la méthode access$000(Sample) qui a obtenu généré en Sample et utilisé à partir Sample.Inner. Malheureusement, vos options sont soit

  1. rendre le champ accessible
  2. Utiliser la réflexion
  3. Générer accesseurs synthétiques