0
public class ChainingVisitors {
    public static void main(String[] args) throws Exception {
        ClassReader reader = new ClassReader("com.foo.Bar");
        ClassWriter writer = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES);
        ClassVisitor v3 = new Link(3, writer);
        ClassVisitor v2 = new Link(2, v3);
        ClassVisitor v1 = new Link(1, v2);
        reader.accept(v1, 0);
        System.out.printf("how many bytes in %s? %d\n", Runnable.class, writer.toByteArray().length);
    }

    private static class Link extends ClassVisitor {
        private final int order;

    public Link(int order, ClassVisitor next) {
        super(ASM4, next);
        this.order = order;
    }

    @Override public void visit(int i, int i2, String s, String s2, String s3, String[] strings) {
        System.out.printf("In visitor %d\n", order);
        super.visit(i, i2, s, s2, s3, strings);
    }
}

I'd like to hand an arbitrary chain of ClassVisitors off to a method. That method would read in the bytes of a class via a ClassReader, transform the class using the ClassVisitors in turn, and write the result via a ClassWriter. Basically, I want to use a chain of these visitors (which, let's assume, I don't control) "in between" a ClassReader and ClassWriter, whose instantiation the method would control.

How can I do this? It seems as though the chaining of visitors wants you to build the chain from "back" (last link, tied to a ClassWriter) to "front" (first link, accept()ed by a ClassReader).

pholser
  • 4,908
  • 1
  • 27
  • 37

2 Answers2

2

I would argue such design. If some component is capable of creating visitors, why can't it run ClassReader and ClassWriter on a bytecode passed to it?

But if you really want to create chain of visitors yourself, you can use Factory design pattern. So instead of visitors, pass in factories to create those visitors, then you can construct your visitor chain yourself without knowing about visitors.

Eugene Kuleshov
  • 31,461
  • 5
  • 66
  • 67
  • The specific scenario I was imagining is that of a ClassLoader whose loadClass() method: 1) slurps up the bytes of the named class via a ClassReader it instantiates 2) applies a sequence of transformations (via ClassVisitors that the loader is created with) 3) writes the resulting bytes to a ClassWriter it instantiates 4) returns the result of defineClass() given writer.toByteArray(). – pholser Feb 28 '13 at 16:06
  • 1
    Generally, visitors can have internal state, so many common ASM's visitors can't be reused. So, factory-based approach would be more appropriate. As an alternative you can declare something like Transformer interface and let it's implementation create class reader, writer, as well as all required visitors. Then you pass Transfomer to your class loader... – Eugene Kuleshov Feb 28 '13 at 16:10
1

You could try to pass just the visitor classes to your method and instantiate them there via reflection.

ruediste
  • 2,434
  • 1
  • 21
  • 30