Какая библиотека байт-кода при управлении номерами строк? - PullRequest
11 голосов
/ 18 февраля 2011

Мне нужно сгенерировать новые классы (посредством генерации байтового кода Java) из существующих классов.Я проанализирую тело (выражения) методов класса.Выражения будут определять, какой код я буду генерировать.

Для меня важно установить исходный файл для новых классов (такой же, как базовый Java-файл), а также управлять номерами строк (когда выдается исключениеstacktrace должен содержать номера строк базового Java-файла).

Пример: у меня есть файл BaseClass.java .Компилятор генерирует BaseClass.class из этого.Я хотел бы проанализировать этот файл класса и сгенерировать байтовые коды для GeneratedClass.class .Когда в c генерируется исключение, трассировка стека должна содержать "BaseClass.java line 3".

BaseClass.java
1: class BaseClass {
2:    void method() {
3:        call();
4:    }
5:}

GeneratesClaas.class
a: class GeneratedClass {
b:    void generatedMethod() {
c:        generatedCall();
d:    }
e:}

Мой вопрос: существуют ли библиотеки, поддерживающие это требование?Javassist, ASM или BCEL?Что использовать для этого?Подсказки, как это сделать, или пример кода будет особенно полезен.

Редактировать: Подсказки, какую библиотеку НЕ использовать, потому что требование НЕ может быть выполнено в полном объеме, также будет полезно:).

Ответы [ 2 ]

5 голосов
/ 18 февраля 2011

С asm вы можете использовать методы visitSource и visitLineNumber для создания этой информации отладки в сгенерированном классе.

Редактировать: Вот минимальный пример:

import java.io.File;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import java.io.FileOutputStream;
import java.io.IOException;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.util.CheckClassAdapter;
import static org.objectweb.asm.Opcodes.*;

public class App {
    public static void main(String[] args) throws IOException {
        ClassWriter cw = new ClassWriter(0);
        CheckClassAdapter ca = new CheckClassAdapter(cw);
        ca.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "test/Test", null, "java/lang/Object", null);
        ca.visitSource("this/file/does/not/exist.txt", null); // Not sure what the second parameter does
        MethodVisitor mv = ca.visitMethod(ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);

        mv.visitCode();
        Label label = new Label();
        mv.visitLabel(label);
        mv.visitLineNumber(123, label);
        mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
        mv.visitInsn(DUP);
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "()V");
        mv.visitInsn(ATHROW);
        mv.visitInsn(RETURN);
        mv.visitMaxs(2, 1);
        mv.visitEnd();

        ca.visitEnd();

        File target = new File("target/classes/test/");
        target.mkdirs();
        FileOutputStream out = new FileOutputStream(new File(target, "Test.class"));
        out.write(cw.toByteArray());
        out.close();
    }
}

При выполнении этого генерируется класс, содержащий метод main, который генерирует исключение RuntimeException, чтобы просто увидеть номер строки в трассировке стека. Сначала давайте посмотрим, что делает дизассемблер из этого:

$ javap -classpath target/classes/ -c -l test.Test
Compiled from "this.file.does.not.exist.txt"
public class test.Test extends java.lang.Object{
public static void main(java.lang.String[]);
  Code:
   0:   new #9; //class java/lang/RuntimeException
   3:   dup
   4:   invokespecial   #13; //Method java/lang/RuntimeException."<init>":()V
   7:   athrow
   8:   return

  LineNumberTable: 
   line 123: 0
}

Так что этот класс был скомпилирован из txt-файла, который не существует :), LineNumberTable говорит, что байт-код, начинающийся со смещения 0, соответствует строке 123 этого воображаемого файла. Запуск этого файла показывает, что этот файл и номер белья также содержатся в трассировке стека:

$ java -cp target/classes/ test.Test
Exception in thread "main" java.lang.RuntimeException
        at test.Test.main(this/file/does/not/exist.txt:123)
2 голосов
/ 18 февраля 2011

BCEL имеет классы LineNumber и LineNumberTable, которые представляют информацию о номере строки в файле класса. Судя по всему, вы можете создать и установить таблицу для некоторого класса, который вы генерируете код. Предположительно, информация записывается в файл класса.

...