2012-03-01 12 views
8

Używam biblioteki asm do przeprowadzenia modyfikacji kodu bajtowego Java - specjalnie w celu zmodyfikowania moich klas, aby zaimplementować nowy interfejs i powiązane metody. Moje obecne podejście polega na korzystaniu z podstawowego interfejsu API ASM za pośrednictwem programu javaagent. Chciałbym zachować to dynamiczne podejście w przeciwieństwie do statycznie modyfikujących plików .class.Jak poprawnie używać metody Instrumentation.retransformClasses() z poziomu kodu asm?

Na wyższym poziomie, moim problemem jest to, że jeśli zdecyduję się zmodyfikować klasę A, która rozciąga się od B, muszę również zmodyfikować B. (Biorąc pod uwagę moje zrozumienie sposobu ładowania klas w JVM, uważam, że klasa B zawsze będzie przekazany transformatorowi przed klasą A. (Proszę, popraw mnie, jeśli się mylę). Biorąc to pod uwagę, myślę, że muszę wtedy wrócić i retransform B. Moje podejście zostało uchwycone w tym kawałek kodu:

public byte[] transform(ClassLoader l, String name, Class<?> clazz, ProtectionDomain d, byte[] b) { 
     throws IllegalClassFormatException { 
    // **1** 
    System.out.println("--->>> " + name); 

    if (interestingClass(name)) { 
     try { 
      ClassReader cr = new ClassReader(b); 
      ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); 
      PyClassVisitorAdapter pv = new PyClassVisitorAdapter(cw, name); 
      cr.accept(pv, 0); 

      // **2** Retrieve the superclass and try to transform that 
      if (! "Ljava/lang/Object;".equals(pv.getSuperName())) { 
       String cName = classJvmToCanonical(pv.getSuperName()); 
       Class[] classes = inst.getAllLoadedClasses(); 
       for (Class c : classes) { 
        if (c.getName().equals(cName)) { 
         inst.retransformClasses(c); 
         break; 
        } 
       } 
      } 

      // Dump the transformed class 
      ClassReader cr2 = new ClassReader(cw.toByteArray()); 
      ClassWriter cw2 = new ClassWriter(cr2, 0); 
      TraceClassVisitor tcv = new TraceClassVisitor(cw2, new PrintWriter(System.out)); 
      cr2.accept(tcv, 0); 

      return cw2.toByteArray(); 
     } catch (Exception ex) { 
      ex.printStackTrace(); 
      return null; 
     } 
    } else { 
     return b; 
    } 
} 

(inst jest uchwyt do Instrumentation który zostanie przekazany w konstruktorze)

Część, z którą mam problemy, to blok oznaczony w komentarzach pod numerem **2**. Powiedzmy ponownie, że A rozszerza B i jestem "zainteresowany" transformacją A. Spodziewam się, że zobaczę nazwę nadklasy (B) drukowanej pod numerem **1** (ale nie będę się przekształcał, ponieważ nie myślę, że to interesujące na pierwszym przejściu), a następnie, gdy dojdę do **2** i odkryję, że superklasa A jest B, powinienem próbować ponownie przetworzyć B. W tym momencie spodziewam się, że ta metoda zostanie ponownie wywołana (przez inst.retransformClasses()) i chciałbym, aby B było drukowane pod numerem **1**. Jednak nie. (Dodałem instrukcje drukowania i jestem pewny, że osiągam wywołanie retransform. Sprawdziłem również, czy oba zwracają true).

Wydaje mi się, że ustawiłem agenta poprawnie; ustawiając klauzule Can-Retransform-Classes i Can-Redefine-Classes na true w manifeście. Również, gdy dodam transformator do Instrumentation w premain metody agenta zrobić to:

public static void premain(String agentArgs, Instrumentation inst) { 
    inst.addTransformer(new PyClassFileTransformer(inst), true); 
} 

Wszelkie rozeznanie co do tego, co robię źle tutaj? Dzięki.

+1

Czy masz problem z rozwiązaniem problemu? @ Jens Czy możesz pójść [tutaj] (http://stackoverflow.com/questions/18657095/got-unsupportedoperationexception-when-try-to-retransformclasses) by dać mi kilka porad? –

Odpowiedz

1

Można zmienić strategię oprzyrządowania kodu bajtowego, więc gdy klasa B zostanie załadowana, znajdzie się wszystkie jej podklasy i zdecyduje w tym punkcie, jeśli trzeba teraz zmodyfikować klasę B. Można to zoptymalizować, utrzymując repozytorium klasy-metadanych lub pamięć podręczną w pamięci (tj. Informacje o hierarchii klas), dzięki czemu za każdym razem nie trzeba ładować metadanych.

Powiązane problemy