2012-03-01 6 views
8

Estoy usando la biblioteca asm para realizar algunas modificaciones en el bytecode de Java, específicamente para modificar mis clases para implementar una nueva interfaz y métodos asociados. Mi enfoque actual es usar la API de núcleo asm a través de un javaagent. Me gustaría mantener este enfoque dinámico en lugar de modificar estáticamente los archivos .class.¿Cómo uso Instrumentation.retransformClasses() correctamente desde dentro del código asm?

En un nivel superior, mi problema es que si elijo modificar la clase A, que se extiende desde B, también necesito modificar B. (Dado mi entendimiento de cómo se cargan las clases en la JVM, creo que esa clase B siempre se entregará a un transformador antes de la clase A. (Por favor, corríjame si estoy equivocado). Dada esa suposición, creo que necesito volver atrás y retransformar B. Mi enfoque está capturado en este bits de código:

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 es un identificador para Instrumentation el que se pasa en en el constructor)

La parte con la que estoy teniendo problemas es el bloque marcado en los comentarios con **2**. Digamos nuevamente que A amplía B y estoy "interesado" en transformar A. Lo que espero es que vea el nombre de la superclase (B) impresa en **1** (pero no me estoy transformando porque no lo hago). creo que es interesante en el primer pase) y luego, una vez que llego al **2** y descubro que la superclase de A es B, debería estar tratando de retransformar B. En este punto espero que este método sea llamado nuevamente (a través de inst.retransformClasses()) y que yo vería que B se imprima al **1**. Sin embargo, yo no. (He agregado instrucciones de impresión y estoy seguro de que estoy llegando a la llamada de retransformación. También he comprobado que Instrumentation.isRetransformClassesSupported() y Instrumentation.isModifiableClass(c) ambos devuelven verdadero).

Creo que he configurado el agente correctamente; estableciendo tanto Can-Retransform-Classes como Can-Redefine-Class en true en el manifiesto. Además, cuando agrego el transformador a la Instrumentación en el método del agente premain hago esto:

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

Cualquier ideas en cuanto a lo que estoy haciendo mal aquí? Gracias.

+1

¿Tiene usted resolver su problema? @Jens ¿Pueden ir [aquí] (http://stackoverflow.com/questions/18657095/got-unsupportedoperationexception-when-try-to-retransformclasses) para darme algunos consejos? –

Respuesta

1

Puede cambiar su estrategia de instrumentación de bytecode, de modo que cuando se carga la clase B, encuentre todas sus subclases y decida en ese punto si necesita modificar la clase B ahora. Esto se puede optimizar manteniendo el repositorio de metadatos de clase o la memoria caché en la memoria (es decir, información sobre la jerarquía de clases), por lo que no tendrá que cargar metadatos cada vez.

Cuestiones relacionadas