2017-11-11 104 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中的信息來匹配變量名稱。

相關問題