3

所以我最近一直在使用 Javassist,但遇到了一个我无法找到答案的问题。CtMethod 的 insertAt 方法允许您在特定行号处插入代码,但它是覆盖该行还是保留它,以及如何使其与默认情况相反?我有一个应用程序,它根据 XML 文件中的“钩子”在运行时使用 Javassist 修改源代码。我想这样做以便可以覆盖一条线,或者可以将一条线放在该线上方而不是覆盖它。显然有一些骇人听闻的方法可以做到这一点,但我宁愿使用正确的方法。

4

1 回答 1

10

容易的部分

CtMethod 对象中存在的方法insertAt(int lineNumber, String src)允许在给定行中的代码之前注入在src中编写的代码。

例如,采用以下(简单)示例程序:

public class TestSubject {

   public static void main(String[] args) {
     TestSubject testSubject = new TestSubject();
     testSubject.print();
   }

   private void print() {
    System.out.println("One"); // line 9
    System.out.println("Two"); // line 10
    System.out.println("Three"); // line 11
   }
}

通过简单的编码(记住方法变量必须是打印方法的 CtMethod 表示):

   // notice that I said line 10, which is where the sysout of "two" is
   method.insertAt(10, true, "System.out.println(\"one and an half\");");

将在类中注入新的 sysout 指令。新类的输出将是:

 one
 one and an half
 two 
 three

困难的部分

Javassist 没有提供删除一行代码的简单方法,所以如果你真的想替换它,你别无选择,只能破解你的方式。

怎么做?好吧,让我向您介绍您的新朋友(如果您还不知道的话),CodeAttribute对象。

CodeAttribute 对象负责保存表示方法流的字节码,除了 code 属性还有另一个名为LineNumberAttribute的属性,它可以帮助您将行号映射到字节码数组中。所以总结这个对象有你需要的一切!

以下示例中的想法非常简单。将字节码数组中的字节与应删除的行相关联,并用无操作码替换字节。

再一次,method 是方法print的 CtMethod 表示

    // let's erase the sysout "Two"
    int lineNumberToReplace = 10;
    // Access the code attribute
    CodeAttribute codeAttribute = method.getMethodInfo().getCodeAttribute();

    // Access the LineNumberAttribute
    LineNumberAttribute lineNumberAttribute = (LineNumberAttribute)      codeAttribute.getAttribute(LineNumberAttribute.tag);

    // Index in bytecode array where the instruction starts
    int startPc = lineNumberAttribute.toStartPc(lineNumberToReplace);

    // Index in the bytecode array where the following instruction starts
    int endPc = lineNumberAttribute.toStartPc(lineNumberToReplace+1);

    System.out.println("Modifying from " + startPc + " to " + endPc);

    // Let's now get the bytecode array
    byte[] code = codeAttribute.getCode();
    for (int i = startPc; i < endPc; i++) {
      // change byte to a no operation code
       code[i] = CodeAttribute.NOP;
    }

在原始TestSubject 类中运行此修改,将产生一个具有以下输出的注入类:

 one
 three

加起来

当您需要添加一行并仍保留现有行时,如果要替换该行,只需使用简单部分中给出的示例,您必须首先使用中给出的示例删除现有行硬部分,然后使用第一个示例注入新行。

另外请记住,在示例中,我假设您已经熟悉 javassist 的基础知识,只向您展示了多汁的部分,而不是全部交易。这就是为什么,例如,在示例中没有 ctClass.writeFile... 你仍然需要这样做,我只是把它省略了,因为我确实希望你应该知道你必须这样做。

如果您在代码示例中需要任何额外帮助,请询问。我很乐意提供帮助。

于 2013-04-19T14:51:41.953 回答