Java
Putting it together
Combine all parts to write a bytecode inspector.
Project: combine
In the final lesson you will combine all pieces and complete the visitor chain. A void visitEnd(); method is added to ClassVisitor; it is called at the end of the traversal. ClassReader.accept calls v.visitEnd() after all methods are visited.
ClassWriter.visitEnd adds a closing line; RenameVisitor.visitEnd forwards it to the delegate. This builds a full pipeline: ClassReader, RenameVisitor, ClassWriter.
The full pipeline
The full pipeline works like this: ClassReader walks the class and its methods; each event first reaches RenameVisitor, which applies the transformation and forwards it to ClassWriter; ClassWriter accumulates the text. At the end visitEnd adds the closing line.
This model mirrors how ASM works in Java agents. In real ASM ClassReader parses bytecode; here we built the same flow with a name and a method list.
Exercise Add visitEnd and complete the chain
Add a void visitEnd(); method to the ClassVisitor interface. Make ClassReader.accept call v.visitEnd(); after all methods are visited. Add public void visitEnd() { sb.append("End").append("\n"); } to ClassWriter. Add public void visitEnd() { delegate.visitEnd(); } to RenameVisitor. Keep main the same: reader.accept(new RenameVisitor(writer)); and System.out.println(writer.toString());.
Show example solution
interface ClassVisitor {
void visitClass(String name);
void visitMethod(String name);
void visitEnd();
}
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);
v.visitEnd();
}
}
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 void visitEnd() { sb.append("End").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 void visitEnd() { delegate.visitEnd(); }
}
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
-
What is added in the combine step?
- A new thread
- A closing call with visitEnd
- A database
- A graphics window
Show answer
A closing call with visitEnd — visitEnd makes a closing call at the end of the traversal.
-
Complete the visitEnd declaration
void ____();
Show answer
void visitEnd();
-
Summarise the project parts
- ClassReader
- Walks the class
- RenameVisitor
- Renames
- ClassWriter
- Produces text
- visitEnd
- Closes the traversal
Next lesson: Project design