سؤال

أحتاج إلى إنشاء فصول جديدة (عبر جيل من رمز بايت Java) من الفئات الموجودة. سأقوم بتحليل الجسم (التعبيرات) من أساليب الفصل. ستحدد التعبيرات التعليمات البرمجية التي سوف تولدها.

بالنسبة لي، إنها الواردات لضبط الملف المصدر للفئات الجديدة (نفس ملف Java Base) بالإضافة إلى التحكم في أرقام السطر (عند إلقاء استثناء، يجب أن يحتوي StackTrace على أرقام خط من ملف Java الأساسي). < / ص>

مثال: لدي الملف baseclass.java . يقوم برنامج التحويل البرمجي بإنشاء baseclass.class من هذا. أرغب في تحليل ملف الفئة هذا وإنشاء رموز البايت ل generatedclass.class . عندما يتم إلقاء استثناء c ، يجب أن يحتوي StackTrace على "BaseClass.java Line 3". giveacodicetagpre.

سؤالي: هل هناك مكتبات تدعم هذا المتطلبات؟ جافاسي، ASM أو بيل؟ ما يجب استخدامه لهذا الغرض؟ تلمح كيفية القيام بذلك أو مثال الرمز سيكون مفيدا بشكل خاص.

تحرير: تلميح ما هي المكتبة التي لا تستخدمها لأن الشرط لا يمكن أن يكون ملواه سيكون مفيدا، أيضا :).

هل كانت مفيدة؟

المحلول

With asm, you can use the methods visitSource and visitLineNumber to create this debugging information in the generated class.

Edit: Here is a minimal example:

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();
    }
}

Running this generates a class containing a main method that throws a RuntimeException just to see the line number in the stack trace. First lets see what a disassembler makes of this:

$ 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
}

So this class was compiled from a txt file that does not exist :), the LineNumberTable says that the bytecode starting at offset 0 corresponds to line 123 of this imaginary file. Running this file shows that this file and linenumber is also contained in the stack trace:

$ java -cp target/classes/ test.Test
Exception in thread "main" java.lang.RuntimeException
        at test.Test.main(this/file/does/not/exist.txt:123)

نصائح أخرى

BCEL has classes LineNumber and LineNumberTable that represent the line number information in a classfile. By the looks of it, you can create and set the table for some class that you are code generating. Presumably, the information gets written out to the class file.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top