【问题标题】:ASM: Stateful TransformationASM:有状态的转换
【发布时间】:2010-12-12 13:51:43
【问题描述】:

我想编写一个 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);
        }
    }
}

这是我有的,但它不会删除常量池中的旧值,它可能有错误。

【问题讨论】:

    标签: java bytecode java-bytecode-asm


    【解决方案1】:

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

    【讨论】:

    • 我热衷于寻找使用访问者 API 的解决方案,因为 ASM 非常明确地表明它是推荐的。但是,如果在这种情况下树 API 是更好的选择,我会研究它。谢谢。
    • 像现在这样使用访问者 API,不能只替换原地常量;您必须在流程中添加额外的代码才能弹出旧值并推送新值。但是,也许您应该研究子类化 ClassWriter;您可以覆盖一些虚拟方法来处理写入常量,尽管验证您是否只修改您打算修改的常量可能有点复杂。
    【解决方案2】:

    您所拥有的几乎是正确的,但不适合在 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 时替换该值。

    【讨论】:

    • 谢谢,这有点帮助,但您的解决方案似乎有点混乱。它传递旧值和新值,中间有一条 POP 指令。你确定这是唯一的方法吗? '关于替换常量:我认为这就是我正在做的,但它实际上并没有从常量池中删除旧值。
    • @someguy 您可以存储操作数,并推迟调用 visitLdcInsn。只要确保在下一个 visitInsn(opcode) != IMUL 或下一个访问方法 != visitInsn 时调用 visitLdcInsn。只有当类中没有其他代码引用它时,该常量才会被删除。
    猜你喜欢
    • 1970-01-01
    • 2015-07-28
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 2020-01-23
    • 1970-01-01
    • 1970-01-01
    • 2021-07-31
    相关资源
    最近更新 更多