堆叠 ASM 访问者的正确方法是什么?

What is the proper way to stack ASM visitors?

我有以下代码:

for (Map.Entry<String, ClassReader> e : classes.entrySet())
{
    ClassReader reader = e.getValue();
    ClassWriter writer = new ClassWriter(Opcodes.ASM7);

    // Process all visitors
    reader.accept(new StringRemapper(writer, "String A", "String A!!"), ClassReader.EXPAND_FRAMES);
    reader.accept(new StringRemapper(writer, "String B", "String B!!"), ClassReader.EXPAND_FRAMES);

    // Update the class
    reader = new ClassReader(writer.toByteArray());
    e.setValue(reader);
}

上面代码的问题是它把所有的东西都写了两次,因为有两个访问者写给同一个作者(我猜)。

要解决这个问题,我需要在每个 reader.accept:

之后添加下面的代码
reader = new ClassReader(writer.toByteArray());
writer = new ClassWriter(Opcodes.ASM7);

问题是,我这样做是在滥用访问者模式吗?我的意思是,为什么我需要创建一个新的 reader/writer 并且 只访问一次 ?我不应该有多个访客吗?

我发现了这个类似的问题 Easy way to stack up a couple of ASM-Bytecode visitors? 但无法理解已接受的答案。

我尝试将第一个访问者作为参数传递给第二个而不是原始的 ClassWriter,结果相同,代码重复。

ClassVisitor last;
// Process all visitors
reader.accept(last = new StringRemapper(writer, "String A", "String A!!"), ClassReader.EXPAND_FRAMES);
reader.accept(new StringRemapper(last, "String B", "String B!!"), ClassReader.EXPAND_FRAMES);

你是对的,你确实错过了一些东西:ClassWriter 本身就是 ClassVisitor

堆叠访问者的正确方法是将其父访问者传递给构造函数:

// The root visitor is the ClassWriter
ClassVisitor cv = writer;

// Add a visitor
cv = new StringRemapper(cv, "String A", "String A!!");
// Add another one
cv = new StringRemapper(cv, "String B", "String B!!");
// maybe add more?

// Process them
reader.accept(cv, ClassReader.EXPAND_FRAMES);
byte[] classFile = writer.toByteArray();

如果您的 StringRemapper 只接受一个 ClassWriter 作为参数,那么您应该将其更改为接受 ClassVisitors instread。

我注意到的另一件事是您的 classes 变量:它包含 ClassReaders 作为值。
使用 ClassVisitors 作为值可能是一种更简单的设计 - 具有初始 ClassWriter 值。
但这是一个更大的设计变更。