ASM: Stateful Transformation
I want to write a MethodVisitor that transforms LDC instructions that are for multiplication.
Example bytecode:
ldc #26
imul
This basically pushes a constant and then multiplies it.
It has to be a stateful transformation because I first have to check that it is for multiply and, if it is, I need to go back to the ldc instruction and modify the constant. I'm not entirely sure how I would go about this, and I don't know how to modify the constant (when I tried to pass a different value, the old value still remained in the constant pool).
Edit:
public class AdditionTransformer extends MethodAdapter {
boolean replace = false;
int operand = 0;
AdditionTransformer(MethodVisitor mv) {
super(mv);
}
@Override
public void visitInsn(int opcode) {
if (opcode == IMUL && replace) {
operand *= 2;
visitLdcInsn(operand);
replace = false;
}
mv.visitInsn(opcode);
}
@Override
public开发者_如何学编程 void visitLdcInsn(Object cst) {
if (cst instanceof Integer && !replace) {
operand = (Integer) cst;
replace = true;
} else {
mv.visitLdcInsn(cst);
}
}
}
This is what I have, but it doesn't remove the old value in the constant pool, and it may have bugs.
If you are interested in modifying bytecode in such a manner, you may want to look into the ASM tree API. You can easily replace LdcInsnNode.cst through a more comfortable DOM-style tree interface as opposed to the SAX-style visitor interface you are attempting to use.
What you've got is just about right, but doesn't cater for other types of opcodes being called after the ldc, so you'll cause some breakage there, since they'll be looking for something on the stack that isn't there (since you didn't visit the ldc). I'm not so sure about removing the existing constant, but you can replace the constant like so:
@Override
public void visitInsn(int opcode) {
if (opcode == IMUL && replace) {
operand *= 2;
mv.visitInsn(POP);
mv.visitLdcInsn(operand);
replace = false;
}
mv.visitInsn(opcode);
}
@Override
public void visitLdcInsn(Object cst) {
if (cst instanceof Integer && !replace) {
operand = (Integer) cst;
replace = true;
}
mv.visitLdcInsn(cst);
}
In other words, always visit the "ldc". If you then see an IMUL proceeding it, pop the stack, insert a new constant, and then visit the IMUL opcode. You'd need to do a bit of work to make this completely safe, in case some other method is visited after visiting ldc and before IMUL. To be paranoid, you could override all visitor methods, and if it's not visitInsn or not IMUL, you would visit the ldc and set replace = false.
Completely replacing the constant is a bit more tricky. You'd need to remember which constants have been seen by all methods visited in the class so far. If you haven't seen that constant so far, you can just replace the value when you visit the ldc.
精彩评论