I use the asm library to do some modification of Java bytecode - specifically to change my classes to implement the new interface and its associated methods. My current approach uses the asm API core via javaagent. I would like to keep this dynamic approach, rather than statically modifying .class files.
At a higher level, my problem is that if I want to change the class A, which extends from B, I also need to change B. (Given my understanding of how classes are loaded in the JVM, I believe that class B will always be passed to the transformer in front of class A. (Please correct me if I am wrong.) Given this assumption, I think that I then need to go back and re-convert B. My approach is fixed in this bit of code
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 is the Instrumentation handle that is passed in the constructor)
The parts that are difficult for me are the block marked in the comments with **2** . Let us say again that A extends B, and I am โinterestedโ in the transformation A. What I expect is that I will see how the name of the superclass (B) is printed in **1** (but not converted, therefore that I think itโs interesting on the first pass), and then, as soon as I get to **2** and find out that the superclass is B, I should try to relay B. At this point, I expect this the method will be called again (via inst.retransformClasses() ) and that I will see that B prints to **1** . However, I do not. (I have added print statements and I am sure that I am getting to the relay call. I have also verified that Instrumentation.isRetransformClassesSupported() and Instrumentation.isModifiableClass(c) both return true).
I believe that the agent was configured correctly; setting both Can-Retransform-Classes and Can-Redefine-Classes to true in the manifest. In addition, when I add a transformer to the Toolkit in the premain agent premain , I do this:
public static void premain(String agentArgs, Instrumentation inst) { inst.addTransformer(new PyClassFileTransformer(inst), true); }
Can I understand what I'm doing wrong here? Thanks.