7

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

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

4

2 回答 2

1

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

于 2010-12-21T13:20:17.680 回答
1

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

于 2011-01-26T23:57:07.213 回答