javassist是否允许在条件表达式中修改运算符?

时间:2017-11-11 09:02:42

标签: java bytecode 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 个答案:

答案 0 :(得分:2)

字节代码级别本身没有<>,例如if_icmple用于比较 - 这意味着它是一个实际的字节代码指令。因此javassist应该能够做到这一点,它被称为构建时字节码检测,在网上有很多关于此的文章。  Here is one for example

答案 1 :(得分:1)

在方法体中更改表达式的常用模式是使用ExprEditorExpr的特定子类,其中包括以下类型的表达式(在撰写本文时):

  • type cast
  • 构造函数调用
  • 字段访问
  • catch clause
  • instanceof表达式
  • 方法调用
  • 新数组表达式
  • 新表达

然而,这些都不包括比较表达式。您可以通过查看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(&lt; =)比较并转移到else块,这是编译器处理if表达式的常见模式。 要翻转示例中的表达式,我们只需要将if_icmpleif_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中的信息来匹配变量名称。