javassist是否允许在条件表达式中修改运算符?
我需要知道是否用以下代码和javassist我可以操纵代码来替换逻辑运算符“>”与“<”。javassist是否允许在条件表达式中修改运算符?
这里是我想要操纵其字节码类:
public class TryClass {
public void foo(){
int a =0;
if(a>5){
System.out.println("I love apples");
}
else{
System.out.println("I hate apples");
}
}
}
操作类的执行应该打印后: “我爱苹果” 代替: “我讨厌苹果”
在字节代码级本身没有<
或>
,它是if_icmple
例如用于比较 - 这意味着它是一个实际的字节代码指令。因此,javassist
应该可以做到,它被称为建立时间字节码仪器,并有不少文章在线关于此。 Here is one for example
用于改变在方法体表达的共同的图案使用ExprEditor并且其包括以下类型的表达式(在写入时)的Expr特定子类:
- 型投
- 构造请致电
- 字段访问
- catch语句
- instanceof expression
- 方法调用
- 新的数组表达
- 新表达
然而没有这些包括比较表达式。你可以通过查看ExprEditor::loopBody
来源证实了这一点:
if (c < Opcode.GETSTATIC) // c < 178
/* skip */;
比较操作码,如if_icmple
= 164被跳过。
Javassist由于其高级工具API而经常使用,但它也具有javassist.bytecode
包下的字节码级API。这意味着您可以在方法字节码中检查操作码并交换它们。
首先我们需要确定foo
方法和表达,我们希望改变(例如,使用的javap)的字节码:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmple 18
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: ldc #3 // String I love apples
12: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
15: goto 26
18: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
21: ldc #5 // String I hate apples
23: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
26: return
正如我们所看到的,a > 5
被编译成if_icmple
(< =)比较并分支到else块,这是处理表达式的编译器的常见模式。 要翻转你的例子中的表达式,我们只需要将if_icmple
换成if_icmpgt
即可。
下面的代码演示了如何使用Javassist进行字节码的API来做到这一点:
CtClass cc = ClassPool.getDefault().get("TryClass");
CtMethod fooMethod = cc.getDeclaredMethod("foo");
CodeIterator codeIterator = fooMethod.getMethodInfo().getCodeAttribute().iterator();
while (codeIterator.hasNext()) {
int pos = codeIterator.next();
int opcode = codeIterator.byteAt(pos);
if(opcode == Opcode.IF_ICMPLE) {
codeIterator.writeByte(Opcode.IF_ICMPGT, pos);
break;
}
}
TryClass test = (TryClass) cc.toClass().newInstance();
test.foo();
但是这个代码不进行任何额外的检查,以确保预期的表达被改变。一些建议可能是在比较过程中检查哪些操作数在堆栈中。如果其中一个从本地变量插槽加载,则可以使用LocalVariableTable(如果可用)CodeAttribute中的信息来匹配变量名称。