ASM:有状态转型

时间:2010-12-12 13:51:43

标签: java bytecode java-bytecode-asm

我想编写一个MethodVisitor来转换用于乘法的LDC指令。

字节码示例:

ldc #26
imul

这基本上推动了一个常数然后乘以它。

它必须是有状态的转换,因为我首先必须检查它是否为乘法,如果是,我需要返回到ldc指令并修改常量。我不完全确定我会怎么做,我不知道如何修改常量(当我尝试传递一个不同的值时,旧值仍然保留在常量池中。)

编辑:

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);
        }
    }
}

这就是我所拥有的,但它不会删除常量池中的旧值,并且可能存在错误。

2 个答案:

答案 0 :(得分:1)

你所拥有的只是正确的,但是不能满足在ldc之后调用的其他类型的操作码,所以你会在那里造成一些破坏,因为他们会在堆栈上寻找一些东西不存在(因为你没有访问ldc)。我不太确定删除现有常量,但你可以像这样替换常量:

@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);
}    

换句话说,请始终访问“ldc”。如果你看到一个IMUL继续它,弹出堆栈,插入一个新的常量,然后访问IMUL操作码。如果在访问ldc之后和IMUL之前访问了一些其他方法,则需要做一些工作才能使其完全安全。要偏执,你可以覆盖所有访问者方法,如果它不是visitInsn或不是IMUL,你将访问ldc并设置replace = false。

完全取代常数有点棘手。你需要记住到目前为止在类中访问过的所有方法都看到了哪些常量。如果到目前为止还没有看到该常量,则可以在访问ldc时替换该值。

答案 1 :(得分:1)

如果您有兴趣以这种方式修改字节码,您可能需要查看ASM tree API。您可以通过更舒适的DOM样式树界面轻松替换LdcInsnNode.cst,而不是您尝试使用的SAX样式的访问者界面。

相关问题