2011-06-17 10 views
57

Teniendo en cuenta este programa:extraño entrada de la tabla de excepción producida por javac de Sun

class Test { 
    public static void main(String[] args) { 
     try { 
      throw new NullPointerException(); 
     } catch (NullPointerException npe) { 
      System.out.println("In catch"); 
     } finally { 
      System.out.println("In finally"); 
     } 
    } 
} 

de Sun javac (v 1.6.0_24) produce el siguiente código de bytes:

public static void main(java.lang.String[]); 

     // Instantiate/throw NPE 
    0: new  #2;   // class NullPointerException 
    3: dup 
    4: invokespecial #3; // Method NullPointerException."<init>":()V 
    7: athrow 

     // Start of catch clause 
    8: astore_1 
    9: getstatic  #4; // Field System.out 
    12: ldc  #5;   // "In catch" 
    14: invokevirtual #6; // Method PrintStream.println 
    17: getstatic  #4; // Field System.out 

     // Inlined finally block 
    20: ldc  #7;   // String In finally 
    22: invokevirtual #6; // Method PrintStream.println 
    25: goto 39 

     // Finally block 
     // store "incomming" exception(?) 
    28: astore_2 
    29: getstatic  #4; // Field System.out 
    32: ldc  #7;   // "In finally" 
    34: invokevirtual #6; // Method PrintStream.println 

     // rethrow "incomming" exception 
    37: aload_2 
    38: athrow 

    39: return 

Con la tabla siguiente excepción:

Exception table: 
    from to target type 
    0  8  8 Class NullPointerException 
    0 17 28 any 
    28 29 28 any 


Mi pregunta es: ¿Por qué incluye esa última entrada en la tabla de excepciones?

lo que tengo entendido, que básicamente dice "si el astore_2 se produce una excepción, lo coge y vuelva a intentar la misma instrucción".

Dicha entrada se produce incluso con vacío try/catch/finally cláusulas tales como

try {} catch (NullPointerException npe) {} finally {} 

Algunas observaciones

  • Eclipse compilador no produce ninguna entrada tales tabla de excepciones
  • La especificación de JVM no documenta ninguna excepción de tiempo de ejecución para the astore instruction.
  • Sé que es legal que la JVM arroje VirtualMachineError para cualquier instrucción. Supongo que la entrada peculiar evita que dichos errores se propaguen desde esa instrucción.
+6

Voy a publicar esto como un comentario, ya que no he logrado entender este concepto. Hay una entrada sobre este tema, sobre por qué se genera la última entrada en [un blog] (http://cliffhacks.blogspot.com/2008/02/java-6-tryfinally-compilation-without.html). Aparentemente, el comportamiento del compilador para compilar el bloque finally, como se especifica en la especificación de la VM, está un poco fuera de lugar, en lo que respecta al compilador de Sun/Oracle. La última entrada de la tabla de excepciones está en su lugar para proteger el "controlador de excepciones generado". No descubrí cómo funciona la guardia y por qué debería funcionar de esa manera. –

Respuesta

8

Existen solo dos explicaciones posibles: el compilador contiene un error o está colocando un tipo de marca de agua por motivos desconocidos.

Esa entrada es ciertamente falsa porque cualquier excepción lanzada por un bloque finally debe enviar el flujo de ejecución al manejador de excepción externo o finalmente al bloque, pero nunca "ejecutar nuevamente" el mismo bloque finally.

Además, una buena evidencia de que es un error/marca de agua, es el hecho de que Eclipse (y tal vez otros compiladores de Java) no generan dicha entrada, y aún así las clases generadas por Eclipse funcionan bien en la JVM de Sun.

Dicho esto, esta publicación es interesante porque parece que el archivo de clase es válido y verificado. Si fuera un implementador de JVM, ¡ignoraría esa entrada y llenaría un error para Sun/Oracle!

+3

"* cualquier excepción lanzada por un bloque finally debe enviar el flujo de ejecución al controlador externo de excepciones *" - No, no si tiene un catch-block interno. Además, estás mezclando Java con bytecode: el programa bytecode debería implementar la semántica del programa Java que se compilará. Puede hacerlo arrojando/atrapando todo tipo de locas excepciones. No existe una forma "correcta" o "incorrecta" de compilar el programa. Mi sospecha es que la entrada es un efecto secundario de una construcción más general en el compilador. Quizás tal entrada incluso sea emitida por el compilador de eclipses en algún raro caso de esquina. – aioobe

+0

@aioobe Aunque JVM puede ser un destino para otros lenguajes en lugar de Java, esta pregunta muestra explícitamente un programa Java que se está compilando para JVM. La JVM puede permitir que una excepción sea manejada por el mismo código que la arrojó, pero la instrucción astore_2 pertenece a un bloque de Java finally que no tiene intent interno, por lo que cualquier excepción lanzada debe enviar el flujo de ejecución a un controlador externo de excepciones. "Mi sospecha es que la entrada es un efecto secundario de una construcción más general en el compilador". - También creo que sí, pero sin embargo esto rompe el contrato del lenguaje Java. – fernacolo

+0

* esto rompe el contrato de lenguaje Java * - no, no es así. Por ejemplo, podría razonar así: cualquier excepción lanzada por cualquier enunciado dentro de la cláusula finally debería propagarse fuera de la cláusula. Por lo tanto, si no existe ninguna declaración en la cláusula finally, no se podrán propagar excepciones desde allí. Por lo tanto, es perfectamente legal suprimir todas las excepciones en el "bloque compilado finalmente" si el bloque que se compiló no contiene instrucciones. – aioobe

1

Según entiendo, la segunda entrada de la tabla de excepción es la cláusula catch all implícita añadida por el compilador para cubrir cualquier excepción/error arrojado en el cuerpo o los manejadores catch y la tercera entrada es la guardia en esa captura implícita forzar el flujo a través de la ejecución finalmente.

5

Si examina el código fuente de OpenJDK 7, me atrevo a adivinar la razón de que duran 28 29 28 any entrada de la tabla de excepción se debe a que el código que maneja el código de bytes astore (ver code a partir de la línea 1871) puede lanzar una java.lang.LinkageError excepción si el valor extraído de la pila del operando no es un tipo returnAddress o reference (consulte la especificación de la máquina virtual Java para astore) y quiere que esta condición de error aparezca en el seguimiento de la pila.

En el caso de que hay un mal tipo de operando en la pila de operandos, la JVM borrar la pila de operandos (deshacerse de ese mal operando), poner un LinkageError en la pila de operandos, y ejecutar el código de bytes astore de nuevo, esta vez ejecutando con éxito el bytecode astore usando una referencia de objeto LinkageError JVM. Consulte la documentación de athrow para obtener más información.

sospecharía en gran medida la causa de lanzar una LinkageError durante astore procesamiento se debe a la complexities JSR/RET subroutines introducir en la verificación de código de bytes (OpenJDK cambia 6878713, 6932496 y 7020373 son evidencia reciente de JSR 's continuó complejidad; I' m sure Sun/Oracle tiene otras pruebas de código cerrado que no estamos viendo en OpenJDK). El cambio OpenJDK 7020373 usa LinkageError para validar/invalidar los resultados de la prueba.

+0

Hmm .. No sería este tipo de errores quedar atrapado en la etapa de verificación de código de bytes? (Antes de ejecutar el código.) – aioobe

+0

actualizado mi respuesta con información acerca de JSR/RET complejidad subrutina, lo que hace que la verificación de código de bytes dolorosa. –

Cuestiones relacionadas