2017-11-11 102 views
4

我需要知道是否用以下代码和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"); 
     } 
    } 
} 

操作类的执行应该打印后: “我爱苹果” 代替: “我讨厌苹果”

回答

2

在字节代码级本身没有<>,它是if_icmple例如用于比较 - 这意味着它是一个实际的字节代码指令。因此,javassist应该可以做到,它被称为建立时间字节码仪器,并有不少文章在线关于此。 Here is one for example

0

用于改变在方法体表达的共同的图案使用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中的信息来匹配变量名称。

相关问题