0

J'essaye d'employer ASM dans un javaagent to change the class that is being constructed (sun/misc/URLClassPath) à un autre (fommil/URLClassPath) qui hérite de lui et remplace toutes les méthodes. Je sais que la classe cible (java/net/URLClassLoader), que je suis retransforming, est la seule chose qui crée sun/misc/URLClassPath s et seulement dans son constructeur.comment instancier une classe différente par l'instrumentation/ASM

L'idée de base est quelque chose comme ceci:

@Override 
public MethodVisitor visitMethod(int access, 
           String name, 
           String desc, 
           String signature, 
           String[] exceptions) { 
    MethodVisitor visitor = super.visitMethod(access, name, desc, signature, exceptions); 

    return new MethodVisitor(Opcodes.ASM5, visitor) { 
     @Override 
     public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 
      if (opcode == Opcodes.INVOKESPECIAL && "sun/misc/URLClassPath".equals(owner) && "<init>".equals(name)) { 
       super.visitMethodInsn(opcode, "fommil/URLClassPath", name, desc, itf); 
      } else { 
       super.visitMethodInsn(opcode, owner, name, desc, itf); 
      } 
     } 
    }; 
} 

Je peux mettre un constructeur de println dans fommil/URLClassPath et le voir en cours de construction!

Cependant, aucune des méthodes de fommil.URLClassPath sont appelés. Seules les méthodes de la super classe sont appelées.

Même si je change le code ci-dessus afin que non seulement invokespecial/<init>, mais tous les appels à URLClassPath sont réécrites afin que leurs invokevirtual points ma classe, mes méthodes sont encore jamais appelés. J'ai même essayé de le faire pour toutes les classes internes de URLClassLoader.

Alors, pourquoi le invokevirtual ne trouve-t-il pas les méthodes de remplacement, même quand elles sont retransformées? Est-ce que ce que je fais - changer le type de la chose en cours de construction - n'est fondamentalement pas possible? Si oui, quelqu'un peut-il expliquer pourquoi? Je suis conscient que l'instrumentation des classes JDK de base est assez mauvaise, mais franchement, je n'ai pas vraiment d'alternative. La seule chose qui reste à essayer est d'instrumenter toutes les classes qui essayent d'instancier URLClassLoader et de les faire piquer dans le champ interne ucp et de le remplacer par mon implémentation.

+0

Vous utilisez invokespecial qui est pour les constructeurs (ce qui est correct) et non invokevirtual qui est pour les méthodes qui pourraient être surchargées. –

+0

@PeterLawrey a encore lu le code, je suis au courant de ce que vous dites. Ce n'est pas le problème. – fommil

+0

Je n'ai pas dit c'est ce qui explique pourquoi j'ai commenté. Je suppose que le URLCLassLoader est appelé avant que votre transformation puisse démarrer. Quelque chose doit charger les classes utilisées par votre transformateur. –

Répondre

0

Je l'ai eu!

ce code simple Tenir compte

import java.util.*;  
class Baz { 
    public void baz() { 
     List list = new ArrayList(); 
    } 
} 

qui produit

0: new   #2     // class java/util/ArrayList 
    3: dup 
    4: invokespecial #3     // Method java/util/ArrayList."<init>":()V 
    7: astore_1 
    8: return 

par rapport à

import java.util.*;  
class Baz { 
    public void baz() { 
     List list = new LinkedList(); 
    } 
} 

qui produit

0: new   #2     // class java/util/LinkedList 
    3: dup 
    4: invokespecial #3     // Method java/util/LinkedList."<init>":()V 
    7: astore_1 
    8: return 

Notez le mot-clé new qui a un type dedans!

Le problème est que si l'on souhaite changer le constructeur, il faut aussi changer la définition de type qui est retournée. c'est-à-dire ajouter ceci

 @Override 
     public void visitTypeInsn(int opcode, String type) { 
      if ("sun/misc/URLClassPath".equals(type)) 
       super.visitTypeInsn(opcode, "fommil/URLClassPath"); 
      else 
       super.visitTypeInsn(opcode, type); 
     }