Java ASM:有状态转换

Java ASM:有状态转换,java,bytecode,java-bytecode-asm,Java,Bytecode,Java Bytecode Asm,我想写一个MethodVisitor来转换用于乘法的LDC指令 字节码示例: ldc #26 imul 这基本上是推动一个常数,然后乘以它 它必须是有状态转换,因为我首先必须检查它是否用于乘法,如果是,我需要返回ldc指令并修改常量。我不完全确定我将如何处理这个问题,我也不知道如何修改常量(当我尝试传递一个不同的值时,旧值仍然保留在常量池中) 编辑: 这就是我所拥有的,但它并没有删除常量池中的旧值,它可能有bug。您得到的几乎是正确的,但不适合在ldc之后调用其他类型的操作码,因此您会在那里造

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

字节码示例:

ldc #26
imul
这基本上是推动一个常数,然后乘以它

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

编辑:


这就是我所拥有的,但它并没有删除常量池中的旧值,它可能有bug。

您得到的几乎是正确的,但不适合在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);
}    
换言之,始终访问“最不发达国家”。如果您随后看到一个IMUL正在执行此操作,请弹出堆栈,插入一个新常量,然后访问IMUL操作码。您需要做一些工作以确保此操作完全安全,以防在访问ldc之后和IMUL之前访问其他方法。如果您偏执,您可以覆盖所有访问者方法,如果它不是visitInsn或不是IMUL,您将访问ldc并将replace设置为false


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

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

谢谢,这有点帮助,但您的解决方案似乎有点混乱。它正在传递旧值和新值,中间有一条POP指令。您确定这是唯一的方法吗?'关于替换常量:我以为这就是我要做的,但实际上并没有从常量池中删除旧值。@有些人可以存储操作数,并推迟调用visitLdcInsn。只要确保在下次访问时调用visitLdcInsn(操作码)!=IMUL,或下一个访问的方法!=visitInsn。只有在类中没有其他代码引用该常量时,才会删除该常量。我非常希望使用visitor API寻找解决方案,因为ASM非常明确地表示建议使用它。但是,如果在这种情况下树API是更好的选择,我将研究它。而不是ks.像现在这样使用visitor API,您不能只替换常量;您必须向流中添加额外的代码以弹出旧值并推送新值。不过,也许您应该研究类编写器的子类化;您可以重写一些虚拟方法来处理常量的编写,尽管这可能有点复杂x来验证您仅修改了要修改的常量。
@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);
}