我正在尝试使用静态分析的工具。该工具适用于字节码而不是源代码。 (但是,我也有源代码。)
该工具从字节码输出一些行号,现在我需要一种简单的方法来映射回源代码。 Netbeans / Eclipse一直这样做(当我点击包含库中的方法时,IDE会将我带到源(如果它可用)),所以我知道这是可能的。我只是想办法做到这一点。
作为示例,请考虑以下hello world程序:
package mypackage;
import java.io.*;
class MyMainClass {
public static void main(String[] args) {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String name0 = "Alice";
String name1 = "Bob";
try {
name0 = in.readLine();
}
catch(Exception e) {
System.out.println("Caught an exception!");
}
System.out.println("Hello " + name0 + "!");
System.out.println("Hello " + name1 + "!");
}
}
生成的字节码(从javap获得)是:
Compiled from "MyMainClass.java"
class mypackage.MyMainClass extends java.lang.Object{
mypackage.MyMainClass();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2; //class java/io/BufferedReader
3: dup
4: new #3; //class java/io/InputStreamReader
7: dup
8: getstatic #4; //Field java/lang/System.in:Ljava/io/InputStream;
11: invokespecial #5; //Method java/io/InputStreamReader."<init>":(Ljava/io/InputStream;)V
14: invokespecial #6; //Method java/io/BufferedReader."<init>":(Ljava/io/Reader;)V
17: astore_1
18: ldc #7; //String Alice
20: astore_2
21: ldc #8; //String Bob
23: astore_3
24: aload_1
25: invokevirtual #9; //Method java/io/BufferedReader.readLine:()Ljava/lang/String;
28: astore_2
29: goto 42
32: astore 4
34: getstatic #11; //Field java/lang/System.out:Ljava/io/PrintStream;
37: ldc #12; //String Caught an exception!
39: invokevirtual #13; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
42: getstatic #11; //Field java/lang/System.out:Ljava/io/PrintStream;
45: new #14; //class java/lang/StringBuilder
48: dup
49: invokespecial #15; //Method java/lang/StringBuilder."<init>":()V
52: ldc #16; //String Hello
54: invokevirtual #17; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
57: aload_2
58: invokevirtual #17; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
61: ldc #18; //String !
63: invokevirtual #17; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
66: invokevirtual #19; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
69: invokevirtual #13; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
72: getstatic #11; //Field java/lang/System.out:Ljava/io/PrintStream;
75: new #14; //class java/lang/StringBuilder
78: dup
79: invokespecial #15; //Method java/lang/StringBuilder."<init>":()V
82: ldc #16; //String Hello
84: invokevirtual #17; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
87: aload_3
88: invokevirtual #17; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
91: ldc #18; //String !
93: invokevirtual #17; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
96: invokevirtual #19; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
99: invokevirtual #13; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
102: return
Exception table:
from to target type
24 29 32 Class java/lang/Exception
}
该工具的输出类似于:
<mypackage.MyMainClass> 39, 69, 99
这些对应于字节码行号。手动我可以弄清楚这些行必须与源代码中的以下行对应:
System.out.println("Caught an exception!");
System.out.println("Hello " + name0 + "!");
System.out.println("Hello " + name1 + "!");
但是,我需要自动执行此过程。任何帮助,将不胜感激。
答案 0 :(得分:2)
如果您可以访问源文件和相应的行号,则任务应该像在每行上加载文件一样简单,只需选择与该行号对应的文件。
您的方法存在的问题是,它依赖class
file format形式的attributes中存储在已编译代码中的可选元数据。有问题的两个,即SourceFile
和LineNumberTable
都是可选,这意味着无法保证它们会出现在您的代码中。验证您正在分析的类文件是否已编译为包含此信息!
注意:如果您想知道,这些相同的属性用于通过Throwable.getStackTrace
提供堆栈跟踪的信息。
答案 1 :(得分:1)
另一个需要考虑的选择是利用现有的静态分析工具,比如FindBugs,它有很多功能来显示标记的代码段的行号,以及生成漂亮的HTML报告。您可以相对轻松地使用自己的字节码分析扩展FindBugs。我已经成功地遵循了教程:http://www.ibm.com/developerworks/library/j-findbug2/
答案 2 :(得分:1)
Soot为您做到这一点。您将通过Soot运行您的类文件,将其转换为Jimple
,然后您可以询问任何代码行的ValueBox
,并且有一个方法:.getJavaSourceStartLineNumber()
和{{ 1}}根据他们的文档,它返回源代码的行号或列号。
就目前而言,这是最简单的方法。 Soot是一个为Java明确构建的静态分析工具。
答案 3 :(得分:-1)
您可以将JD用于您的目的。获得该工具后,您可以使用以下方法通过提示进行反编译:
jdi-gui.exe YourClassFile.class
。
您可以使用subprocess
类启动Process
,反编译.class
文件,然后重新编写包含相应行号的新文件。然后,只选择工具返回的那些行号。