问题
I need to know if with the following code and with javassist I can manipulate the code to replace the logical operator ">" with "<".
Here is the class whose bytecode I want to manipulate:
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");
}
}
}
After the manipulation the execution of the class should print: "I love apples" instead of: "I hate apples"
回答1:
there is no <
or >
at the byte code level per-se, it's if_icmple
for example used for comparison - meaning it's an actual byte code instruction. As such javassist
should be able to do it, it's called build-time byte-code instrumentation and there are quite a few article online about this.
Here is one for example
回答2:
The common pattern for altering expressions in method bodies is using ExprEditor and specific subclasses of Expr which include the following types of expressions(at the time of writing):
- type cast
- constructor call
- field access
- catch clause
- instanceof expression
- method call
- new array expression
- new expression
However none of these include comparison expressions. And you can confirm this by looking at the source of ExprEditor::loopBody
:
if (c < Opcode.GETSTATIC) // c < 178
/* skip */;
Comparison opcodes such as if_icmple
= 164 are skipped.
Javassist is frequently used due to its high-level instrumentation API, however it also has a bytecode level API under the javassist.bytecode
package. This means you can go over the opcodes in the method bytecode and swap them around.
First we need to determine the bytecode of the foo
method and expression we wish to alter(e.g. using 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
As we can see, the a > 5
was compiled into an if_icmple
(<=) compare and branch to else block, which is a common pattern for compilers handling if expressions.
To flip the expression in your example, we just need to swap if_icmple
with if_icmpgt
.
The following code demonstrates how to do it using the Javassist bytecode 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();
However this code doesn't do any additional checks to ensure the intended expression was changed. Some suggestions may be to check which operands are on the stack during the comparison. If one of them was loaded from a local variable slot, you may use information from the LocalVariableTable(if available) CodeAttribute to match variable names for example.
来源:https://stackoverflow.com/questions/47236172/does-javassist-let-modify-an-operator-in-a-conditional-expression