Pregunta

Necesito generar nuevas clases (a través de la generación de código de bytes de Java) de las clases existentes. Analizaré el cuerpo (expresiones) de los métodos de una clase. Las expresiones determinarán qué código generaré.

Para mí es Importand establecer el archivo de origen para las nuevas clases (igual que el archivo Java base), así como el control de los números de línea (cuando se lanza una excepción, el stacktrace debe contener números de línea del archivo Java base). < / p>

Ejemplo: Tengo el archivo baseclass.java . El compilador genera un baseclass.class de esto. Me gustaría analizar este archivo de clase y generar los códigos de bytes para un generatsclass.class . Cuando se encuentra en C se lanza una excepción, el PackTrace debería contener "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:}

Mi pregunta: ¿Hay bibliotecas que apoyan este requisito? Javassist, ASM o BCEL? ¿Qué usar para este propósito? Los sugerencias de cómo hacerlo o un código de ejemplo serían especialmente útiles.

Editar: Insinúa qué biblioteca no debe usar porque el requisito no puede ser completo sería útil, también:).

¿Fue útil?

Solución

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)

Otros consejos

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top