Reputation: 131
I'm using the asm library to perform some Java bytecode modification - specifically to modify my classes to implement a new interface and associated methods. My current approach is using the core asm API via a javaagent. I'd like to keep this dynamic approach as opposed to statically modifying .class files.
At a higher level, my problem is that if I choose to modify class A, which extends from B, I also need to modify B. (Given my understanding of how classes are loaded in the JVM, I believe that class B will always be handed to a transformer before class A. (Please correct me if I'm wrong). Given that assumption, I'm thinking that I then need to go back and retransform B. My approach is captured 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 a handle to Instrumentation
which gets passed in in the constructor)
The part I'm having a hard time with is the block marked in the comments with **2**
. Let's say again that A extends B and I'm 'interested' in transforming A. What I'm expecting is that I would see the name of the superclass (B) being printed at **1**
(but not getting transformed because I don't think it's interesting on the first pass) and then, once I get to **2**
and discover that A's superclass is B, I should be trying to retransform B. At this point I'm expecting this method to be called again (via inst.retransformClasses()
) and that I would see B getting printed at **1**
. However, I don't. (I have added print statements and am sure I'm reaching the retransform call. I've also checked that Instrumentation.isRetransformClassesSupported()
and Instrumentation.isModifiableClass(c)
both return true).
I believe I've set up the agent correctly; setting both Can-Retransform-Classes and Can-Redefine-Classes to true in the manifest. Also, when I add the transformer to the Instrumentation in the agent's premain
method I do this:
public static void premain(String agentArgs, Instrumentation inst) {
inst.addTransformer(new PyClassFileTransformer(inst), true);
}
Any insights as to what I'm doing wrong here? Thanks.
Upvotes: 8
Views: 1856
Reputation: 31795
You could change your bytecode instrumentation strategy, so when class B is loaded, you find all its subclasses and decide at that point if you need to modify class B now. This can be optimized by maintaining class-metadata repository or cache in memory (i.e. information about class hierarchy), so you won't have to load metadata every time.
Upvotes: 1