2011-02-18 39 views
11

我需要從現有類生成新類(通過生成Java字節碼)。我將分析一個類的方法的正文(表達式)。表達式將決定我將生成的代碼。什麼字節碼庫控制行號?

對我來說,它是導入並設置新類的源文件(與基本Java文件相同)以及控制行號(當引發異常時,堆棧跟蹤應包含基本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?爲此目的使用什麼?提示如何做到這一點或示例代碼將特別有用。

編輯: 提示什麼庫不使用,因爲需求不能滿足也將是有益的:)。

+0

大多數反編譯器可以在代碼打印(如意見),其中的代碼原線當中。你可以解析這個並重新排列代碼。您可能會發現的問題是反編譯的代碼可能不是相同的順序,因爲它不完全相同。 – 2011-02-18 13:38:49

回答

5

使用asm,您可以使用方法visitSourcevisitLineNumber在生成的類中創建此調試信息。

編輯:這裏是一個小例子:

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

運行該生成包含拋出RuntimeException正好看到在堆棧跟蹤的行號main方法的類。首先來看看反彙編使得這算什麼:

$ 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

BCEL具有類LineNumber和LineNumberTable,它們表示類文件中的行號信息。通過它的外觀,你可以爲代碼生成的某個類創建和設置表格。據推測,這些信息會被寫入類文件。