CodeBlanks Logo Icon CodeBlanks

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.

Practice 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());.

Starter code

Main.java
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());
    }
}

Sample solution

Main.java
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());
    }
}

Expected output

Class: Main
Method: start
End

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 What is added in the combine step?

  • A new thread
  • A closing call with visitEnd Correct answer
  • A database
  • A graphics window

Explanation: visitEnd makes a closing call at the end of the traversal.

2 Complete the visitEnd declaration

Question
void ____();
Answer
void visitEnd();

Explanation: void visitEnd(); marks the end of the traversal.

3 Summarise the project parts

Term Meaning
ClassReader Walks the class
RenameVisitor Renames
ClassWriter Produces text
visitEnd Closes the traversal

All lessons in Expert