Получить данные об операции с помощью javassist

Я пытаюсь проанализировать байтовый код с помощью javassist, используя простой класс MyData:

class MyData {
    private Collection<String> strings = new ArrayList<String>();

        // .....................    
    public void add(String str) {
        strings.add(str); // line number 35
    }
        // .....................    
}

Вот код, который я использую для этого класса:

ClassFile cf = new ClassFile(new DataInputStream(TryJavassist.class.getResourceAsStream("MyData.class")));
MethodInfo minfo = cf.getMethod("add");  
CodeAttribute ca = minfo.getCodeAttribute();
for (CodeIterator ci = ca.iterator(); ci.hasNext();) {
    int index = ci.next();
    int op = ci.byteAt(index);

    System.out.println(op + "=" + Mnemonic.OPCODE[op] + ": " + minfo.getLineNumber(index));
}

Он работает и печатает:

42=aload_0: 35
180=getfield: 35
43=aload_1: 35
185=invokeinterface: 35
87=pop: 35
177=return: 36

Строка 35 вызывает метод add() коллекции с именем strings. Фрагмент кода, который я разместил, извлекает только invokeinterface и строку 35. Хорошо, я могу знать, что это было поле класса (getfield).

Я хотел бы знать, как получить остальную информацию:

  • имя поля strings
  • вызываемый метод интерфейса add()

Ни гугление, ни чтение API doc пока не дали положительного результата.


person AlexR    schedule 28.02.2012    source источник


Ответы (1)


Это тяжелая работа, требующая понимания формата класса JVM и набора инструкций. Любой ответ может дать только частичные данные. Особенно с Javassist. В инструкции обычно одним параметром является индекс в пуле констант, где есть объект с индексом на имя и индекс на тип. Так что знайте, во что вы ввязываетесь.

Если класс скомпилирован с отладочной информацией, доступны имена частных полей (ClassFile.getFields).

Есть хороший инструмент Dump, упакованный javassist.

private void dumpMyDataClass() throws IOException, BadBytecode, Exception {
    ClassFile cf = new ClassFile(new DataInputStream(getClass().getResourceAsStream("MyData.class")));

    // Dump fields:
    for (Object fieldInfoObj : cf.getFields()) {
        FieldInfo fieldInfo = (FieldInfo) fieldInfoObj;
        System.out.printf("Field %s; %s%n", fieldInfo.getName(), fieldInfo.getDescriptor());
    }

    MethodInfo minfo = cf.getMethod("add");
    CodeAttribute ca = minfo.getCodeAttribute();
    for (CodeIterator ci = ca.iterator(); ci.hasNext();) {
        int address = ci.next();
        int op = ci.byteAt(address);

        String params = "";
        switch (op) {
            case Opcode.INVOKEINTERFACE:
                int a1 = ci.s16bitAt(address + 1);
                params += " " + cf.getConstPool().getInterfaceMethodrefName(a1);
                System.out.println("a1 = " + a1);
                break;
        }

        System.out.printf("Line %4d. Address %7d: %s%s%n", minfo.getLineNumber(address), address, Mnemonic.OPCODE[op], params);
    }

    // Command line tool of javassist:
    String pathToClass = System.getProperty("user.dir") + "/target/classes/jeggen/test2/MyData.class";
    Dump.main(new String[] { pathToClass });
}
person Joop Eggen    schedule 28.02.2012
comment
большое спасибо. Хотя я не совсем понял, откуда вы знаете, как звонить ci.s16bitAt(address + 1);, но он работает просто отлично. Единственное, что мне еще нужно, это получить имя типа поля. Я нашел метод getFieldrefType в ConstPool, но он все еще не работает для меня. Я попытаюсь поиграть с ним, но если вы, вероятно, знаете, что мне нужно сделать, чтобы получить эту информацию, не могли бы вы дать мне совет? - person AlexR; 29.02.2012
comment
Вам нужна ссылка на набор инструкций JVM для операндов: java.sun.com/docs/books/jvms/second_edition/html/. Я только что показал s16bitAt для первого операнда и часть соответствующей записи пула констант. В csg.ci.iu-tokyo. ac.jp/~chiba/javassist/html/index.html можно увидеть getInterfaceMethodRefNameAndType и тому подобное. - person Joop Eggen; 29.02.2012