控制行号时的字节码库是什么?
-
15-11-2019 - |
题
我需要从现有类生成新类(通过生成Java字节代码)。我将分析类别的身体(表达式)。表达式将确定我将生成的代码。
对我来说,它是一个设置的新类(与基本java文件相同)的源文件以及控制行号(当丢弃堆栈堆栈时应包含基本Java文件的行号)。< / p>
示例:
我有文件 baseclass.java 。编译器从中生成 baseclass.class 。我想分析这个类文件,并为生成的class.class 生成字节代码。当在 c 抛出一个例外时,堆栈应该包含“BaseClass.java第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?为此目的使用什么?提示如何做到或者示例代码是特别有用的。
编辑: 暗示不使用的库,因为要求无法填充的要求将是帮助的:)。
解决方案
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.