0

Ich versuche, ASM in einem javaagent to change the class that is being constructed (sun/misc/URLClassPath) zu einem anderen (fommil/URLClassPath) zu verwenden, der davon erbt und alle Methoden überschreibt. Ich weiß, dass die Zielklasse (java/net/URLClassLoader), die ich bin retransforming, ist das einzige, was sun/misc/URLClassPath s und nur in seinem Konstruktor erstellt.wie man eine andere Klasse durch Instrumentierung/ASM instanziiert

Die Grundidee ist so etwas wie dieses:

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

ich einen println in fommil/URLClassPath ‚s Konstruktor setzen können und sehen es aufgebaut ist!

Es wird jedoch keiner der Methoden fommil.URLClassPath aufgerufen. Nur die Methoden der Superklasse werden immer aufgerufen.

Auch wenn ich den obigen Code ändern, so dass nicht nur invokespecial/<init>, aber alle Anrufe auf URLClassPath neu geschrieben werden, so dass ihre invokevirtual Punkte zu meiner Klasse, meine Methoden noch nie genannt. Ich habe sogar versucht, dies für alle inneren Klassen von URLClassLoader zu tun.

Warum finden die invokevirtual s nicht die Override-Methoden, auch wenn sie erneut umgewandelt werden? Was ich mache - die Art des zu konstruierenden Gegenstandes zu verändern - ist grundsätzlich nicht möglich? Wenn ja, kann jemand bitte erklären warum?

Ich bin mir bewusst, dass die Instrumentierung JDK Kern-Klassen ist ziemlich böse, aber ehrlich gesagt habe ich nicht wirklich viel von einer Alternative.

Das einzige, was noch zu versuchen ist, ist das Instrumentieren aller Klassen, die versuchen, URLClassLoader zu instanziieren und sie in das interne ucp Feld stecken zu lassen und es durch meine Implementierung zu ersetzen.

+0

hinzu. Sie verwenden Invokespecial, das für Konstruktoren ist (was korrekt ist), nicht invokevirtual, das für Methoden ist, die überschrieben werden können. –

+0

@PeterLawrey lesen Sie den Code erneut, ich weiß, was Sie sagen. Das ist nicht das Problem. – fommil

+0

Ich habe nicht gesagt, dass es war, weshalb ich kommentiert habe. Ich vermute, dass der URLCLassLoader aufgerufen wird, bevor die Umwandlung eintreten kann. Etwas muss die Klassen laden, die Ihr Transformator verwendet. –

Antwort

0

Ich habe es!

Betrachten Sie diese einfachen Code

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

die

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

produziert im Vergleich zu

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

die

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

Beachten Sie das new Schlüsselwort, das einen Typ enthält!

Das Problem ist, dass man, wenn man den Konstruktor ändern möchte, auch die Typdefinition ändern muss, die zurückgegeben wird. Fügen Sie das