Reputation: 405
I have the following code:
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);
}
The issue with the code above is that it is writing everything twice, because there are two visitors writing to the same writer (I guess).
To fix this issue, I need to add the code below after every reader.accept
:
reader = new ClassReader(writer.toByteArray());
writer = new ClassWriter(Opcodes.ASM7);
The thing is, by doing so, am I misusing the visitor pattern? I mean, why do I need to create a new reader/writer and visit it only once? Shouldn't I be able to have multiple visitors?
I found this similar question Easy way to stack up a couple of ASM-Bytecode visitors? but couldn't understand the accepted answer.
I tried to pass the first visitor as a parameter to the second instead of the original ClassWriter, had the same result, duplicated code.
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);
Upvotes: 3
Views: 416
Reputation: 15173
You are right, you did miss something: The ClassWriter
is a ClassVisitor
itself.
The right way to stack the visitors is to pass it's parent visitor to the constructor:
// 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();
If your StringRemapper
only takes a ClassWriter
as parameter, then you should change it to accept ClassVisitor
s instread.
Another thing I noticed is your classes
variable: It contains ClassReader
s as values.
It might be an easier design to use ClassVisitors
as value - with an initial ClassWriter
value.
But this is a bigger design change.
Upvotes: 4