2012-05-21 8 views
7

Estoy experimentando con una herramienta que realiza análisis estáticos. La herramienta funciona en bytecode en lugar de código fuente. (Sin embargo, también tengo el código fuente).¿Cómo obtener el código fuente de Java para una posición determinada de bytecode de forma automatizada?

La herramienta genera algunos números de línea del bytecode y ahora necesito una forma fácil de volver al código fuente. Netbeans/Eclipse hacen esto todo el tiempo (cuando hago clic en un método en una biblioteca incluida, el IDE me lleva a la fuente (si está disponible)), así que sé que esto es posible. Simplemente no pude encontrar una manera de hacerlo.

Como ejemplo, considere la siguiente hola programa mundial:

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 + "!"); 
    } 
} 

El código de bytes generada (obtenido de javap) es:

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 


} 

La salida de la herramienta es algo así como:

<mypackage.MyMainClass> 39, 69, 99 

Corresponden a los números de línea del bytecode. Manualmente puedo imaginar que las líneas deben corresponder a las siguientes líneas en el código fuente:

System.out.println("Caught an exception!"); 
System.out.println("Hello " + name0 + "!"); 
System.out.println("Hello " + name1 + "!"); 

Sin embargo, necesito para automatizar este proceso. Cualquier ayuda sería apreciada.

+0

Puede usar el decompilador de datos para ese –

Respuesta

-1

Puede usar JD para su fin. Una vez que tenga la herramienta, puede descompilar a través del mensaje utilizando:

jdi-gui.exe YourClassFile.class.

Puede iniciar un subprocess usando la clase Process, descompilar su archivo .class y luego volver a escribir un nuevo archivo con los respectivos números de línea. Luego, solo seleccione los números de línea que devuelve su herramienta.

+0

Ya tengo la fuente, por lo que no quiero usar JD. Es posible que el código de JD no coincida con la fuente original. – Jus12

1

Otra opción a considerar es aprovechar las herramientas de análisis estático existentes, como FindBugs, que tienen muchas funciones para mostrar los números de línea para las secciones marcadas de código, así como para producir buenos informes HTML. Puede extender FindBugs con su propio análisis de código de bytes con relativa facilidad. He seguido el tutorial aquí con éxito: http://www.ibm.com/developerworks/library/j-findbug2/

1

Si tiene acceso tanto al archivo de origen como al número de línea correspondiente, la tarea debe ser tan simple como cargar el archivo por línea y simplemente elegir el correspondiente a ese número de línea.

El problema con su enfoque es que se basa en los metadatos opcionales almacenados en el código compilado según el class file format, en la forma de attributes. Los dos en cuestión, a saber, SourceFile y LineNumberTable son ambos opcional, lo que significa que no hay garantía de que estén presentes en su código. ¡Verifique que los archivos de clase que está analizando estén compilados para contener esta información!

Nota: Estos mismos atributos se utilizan para proporcionar la información para los rastros de pila a través de Throwable.getStackTrace, en caso de que se lo pregunte.

+0

No creo que depender de 'SourceFile' y' LineNumberTable' sea un problema. Si compila su archivo de clase sin información de depuración, obtendrá ... bueno, no hay información de depuración. – biziclop

1

Soot hace esto por usted.Ejecutará su archivo de clase a través de Soot, que lo convierte en Jimple, luego puede solicitar el ValueBox de cualquier línea de código, y hay un método: .getJavaSourceStartLineNumber() y .getJavaSourceStartColumnNumber() que, de acuerdo con su documentación, devuelve el número de línea o columna número del código fuente.

Esto es, por mucho, la forma más fácil de hacerlo. Soot es una herramienta de análisis estático construida explícitamente para Java.

Cuestiones relacionadas