Java
Bytecode transformation
Write bytecode transformation with ASM.
A transforming visitor
A visitor can wrap another visitor and change the events. This decorator pattern is the basis of ASM agents. A RenameVisitor forwards events to an inner delegate visitor while renaming method names.
For example, if visitMethod does n.replace("main", "start") and forwards to the delegate, the output shows start instead of main. The transformation is transparent: the delegate still runs the same visitor logic.
Wrapping the delegate
RenameVisitor takes a ClassVisitor delegate in its constructor and stores it. On every event it first applies the transformation, then forwards to the delegate. In visitClass it passes the name unchanged, while in visitMethod it can rename.
This pattern is used in ASM agents that transform bytecode: a visitor chain is built, each link adds one operation. The last link is usually a ClassWriter.
Exercise Write the RenameVisitor class
Add a class RenameVisitor implements ClassVisitor to the structure above; it has a ClassVisitor delegate field and a RenameVisitor(ClassVisitor delegate) constructor. Its visitClass(String n) method calls delegate.visitClass(n); and visitMethod(String n) calls delegate.visitMethod(n.replace("main", "start"));. In main create ClassWriter writer = new ClassWriter();, call reader.accept(new RenameVisitor(writer)); and print with System.out.println(writer.toString());.
Show example solution
interface ClassVisitor {
void visitClass(String name);
void visitMethod(String name);
}
class ClassReader {
String name;
String[] methods;
ClassReader(String name, String[] methods) {
this.name = name;
this.methods = methods;
}
void accept(ClassVisitor v) {
v.visitClass(name);
for (String m : methods) v.visitMethod(m);
}
}
class ClassWriter implements ClassVisitor {
StringBuilder sb = new StringBuilder();
public void visitClass(String n) { sb.append("Class: ").append(n).append("\n"); }
public void visitMethod(String n) { sb.append("Method: ").append(n).append("\n"); }
public String toString() { return sb.toString().trim(); }
}
class RenameVisitor implements ClassVisitor {
ClassVisitor delegate;
RenameVisitor(ClassVisitor delegate) {
this.delegate = delegate;
}
public void visitClass(String n) { delegate.visitClass(n); }
public void visitMethod(String n) { delegate.visitMethod(n.replace("main", "start")); }
}
public class Main {
public static void main(String[] args) {
ClassReader reader = new ClassReader("Main", new String[]{"main"});
ClassWriter writer = new ClassWriter();
reader.accept(new RenameVisitor(writer));
System.out.println(writer.toString());
}
}
Test yourself
-
Complete the rename call
return name.____("main", "start");
Show answer
return name.replace("main", "start");
-
What does a transforming visitor do?
- It forwards altered events
- It compiles bytecode
- It starts threads
- It deletes the class
Show answer
It forwards altered events — A transforming visitor alters events and forwards them to the delegate.
-
Match the transformation elements
- delegate
- Wrapped visitor
- replace
- Rename
- visitMethod
- Method event
- RenameVisitor
- Transformer class
Next lesson: Putting it together