¿Error de código inalcanzable contra advertencia de código muerto en Java bajo Eclipse?

Alguien sabe por qué:

public void foo() { System.out.println("Hello"); return; System.out.println("World!"); } 

Se informaría como un “error inalcanzable” en Eclipse, pero

 public void foo() { System.out.println("Hello"); if(true) return; System.out.println("World!"); } 

¿Solo activa una advertencia de “Código muerto”?

La única explicación que puedo pensar es que el comstackdor de Java solo marca el primero, y que un análisis adicional en Eclipse descubre el segundo. Sin embargo, si ese es el caso, ¿por qué el comstackdor de Java no puede resolver este caso en tiempo de comstackción?

¿El comstackdor de Java no se daría cuenta en el momento de la comstackción de que el if (verdadero) no tiene ningún efecto, por lo tanto, producirá bytecode que es esencialmente idéntico? ¿En qué punto se aplica el análisis de código alcanzable?

Supongo que una forma más general de pensar en esta pregunta es: “¿cuándo se aplica el análisis del código accesible?” En la transformación del segundo fragmento de código Java al bytecode final, estoy seguro de que en algún momento se eliminará el equivalente de tiempo de ejecución “if (true)” y las representaciones de los dos progtwigs serán idénticas. ¿El comstackdor de Java no aplicaría de nuevo el análisis del código accesible?

El primero no comstack (tienes un error), el segundo comstack (solo tienes una advertencia). Esa es la diferencia.

En cuanto a por qué Eclipse detecta el código muerto, bueno, eso es solo la conveniencia de una herramienta de desarrollo integrada con un comstackdor incorporado que puede definirse mejor que JDK para detectar este tipo de código.

Actualización : el JDK en realidad elimina el código muerto.

 public class Test { public void foo() { System.out.println("foo"); if(true)return; System.out.println("foo"); } public void bar() { System.out.println("bar"); if(false)return; System.out.println("bar"); } } 

javap -c dice:

 la prueba de clase pública extiende java.lang.Object {
 prueba pública ();
   Código:
    0: aload_0
    1: invokespecial # 1;  // Método java / lang / Object. "" :() V
    4: regreso

 public void foo ();
   Código:
    0: getstatic # 2;  // Campo java / lang / System.out: Ljava / io / PrintStream;
    3: ldc # 3;  // String foo
    5: invokevirtual # 4;  // Método java / io / PrintStream.println: (Ljava / lang / StrV
    8: regreso

 barra vacía pública ();
   Código:
    0: getstatic # 2;  // Campo java / lang / System.out: Ljava / io / PrintStream;
    3: ldc # 5;  // Barra de cadenas
    5: invokevirtual # 4;  // Método java / io / PrintStream.println: (Ljava / lang / String;) V
    8: getstatic # 2;  // Campo java / lang / System.out: Ljava / io / PrintStream;
    11: ldc # 5;  // Barra de cadenas
    13: invokevirtual # 4;  // Método java / io / PrintStream.println: (Ljava / lang / String;) V
    16: regreso

 }

En cuanto a por qué (Sun) no da una advertencia al respecto, no tengo ni idea 🙂 Al menos el comstackdor JDK tiene incorporado DCE (Dead Code Elimination).

El código inalcanzable es un error según Java Language Spec .

Para citar del JLS:

La idea es que debe haber alguna ruta de ejecución posible desde el comienzo del constructor, el método, el inicializador de la instancia o el inicializador estático que contiene la statement en la statement misma. El análisis toma en cuenta la estructura de los enunciados. Excepto por el tratamiento especial de while, do, y para las declaraciones cuya expresión de condición tiene el valor constante verdadero, los valores de las expresiones no se tienen en cuenta en el análisis de flujo.

Lo que eso significa es que el bloque if no se tiene en cuenta, ya que si se recorre una de las rutas de la instrucción if , se puede llegar a la instrucción de impresión final. Si cambiaste tu código para que sea:

 public void foo() { System.out.println("Hello"); if (true) return; else return; System.out.println("World!"); } 

luego, de repente ya no se comstackrá, ya que no hay una ruta a través de la instrucción if que permita llegar a la última línea.

Es decir, un comstackdor compatible con Java no puede comstackr el primer fragmento de código. Para citar adicionalmente el JLS:

Como ejemplo, la siguiente instrucción da como resultado un error en tiempo de comstackción:

 while (false) { x=3; } 

porque la statement x = 3; no es accesible; pero el caso superficialmente similar:

 if (false) { x=3; } 

no da como resultado un error en tiempo de comstackción. Un comstackdor de optimización puede darse cuenta de que la statement x = 3; nunca se ejecutará y puede optar por omitir el código para esa statement del archivo de clase generado, pero la instrucción x = 3; no se considera “inalcanzable” en el sentido técnico especificado aquí.

La segunda advertencia que da Eclipse, sobre el código muerto, es una advertencia generada por el comstackdor, que no es “inalcanzable”, según el JLS, pero en la práctica sí lo es. Esta es una verificación de estilo de pelusa adicional que proporciona Eclipse. Esto es completamente opcional y, al usar la configuración de Eclipse, se puede deshabilitar o convertir en un error de comstackción en lugar de una advertencia.

Este segundo bloque es un “olor a código”, if (false) bloques if (false) normalmente se colocan para deshabilitar el código con fines de depuración, dejarlo atrás suele ser accidental y, por lo tanto, la advertencia.

De hecho, Eclipse realiza pruebas aún más avanzadas para determinar los valores posibles de una instrucción if para determinar si es posible tomar ambas rutas o no. Por ejemplo, Eclipse también se quejaría del código muerto en el siguiente método:

 public void foo() { System.out.println("Hello"); boolean bool = Random.nextBoolean(); if (bool) return; if (bool || Random.nextBoolean()) System.out.println("World!"); } 

Generará un código inalcanzable para la segunda instrucción if, ya que puede razonar que bool solo debe ser false en este punto del código. En un fragmento de código tan corto, es obvio que las dos sentencias if están probando lo mismo, sin embargo, si hay entre 10 y 15 líneas de código en el medio, puede que ya no sea tan obvio.

Entonces, en resumen, la diferencia entre los dos: uno está prohibido por JLS, y uno no, pero Eclipse lo detecta como un servicio para el progtwigdor.

Esto es para permitir un tipo de comstackción condicional .
No es un error con if , pero el comstackdor marcará un error para while , do-while y for .
Esto esta bien:

 if (true) return; // or false System.out.println("doing something"); 

Esto son errores

 while (true) { } System.out.println("unreachable"); while (false) { System.out.println("unreachable"); } do { } while (true); System.out.println("unreachable"); for(;;) { } System.out.println("unreachable"); 

Se explica al final de JLS 14.21: Declaraciones inalcanzables :

La razón de este tratamiento diferente es permitirles a los progtwigdores definir “variables de bandera” tales como:

  static final boolean DEBUG = false; 

y luego escribe un código como:

  if (DEBUG) { x=3; } 

La idea es que sea posible cambiar el valor de DEPURAR de falso a verdadero o de verdadero a falso y luego comstackr el código correctamente sin ningún otro cambio en el texto del progtwig.

El if (true) es un poco más sutil que “inalcanzable”; porque ese return codificado siempre hará que el siguiente código sea inalcanzable, pero cambiar la condición en if podría hacer que la siguiente statement sea alcanzable.

Tener un condicional allí significa que hay una posibilidad de que la condición pueda cambiar. Hay casos en que algo más complicado que una true encuentra entre paréntesis, y para el lector humano no es obvio que el siguiente código esté “amortiguado”, pero el comstackdor advierte, por lo que es capaz de advertirlo al respecto.

Aquí se menciona Eclipse, y hace que las cosas parezcan un poco más complicadas para el usuario; pero, de hecho, debajo de Eclipse hay un comstackdor de Java (muy sofisticado) que presenta una gran cantidad de conmutadores de advertencia, etc. que Eclipse puede activar y desactivar. En otras palabras, no obtiene la amplitud de diferentes advertencias / errores de una comstackción recta de javac , ni tiene medios convenientes para activarlos o desactivarlos. Pero es el mismo trato, solo con más campanas y silbatos.

Creo que una forma de lograrlo es que el código inalcanzable probablemente sea un error, y el JLS intenta protegerlo de tales errores.

Permitir if (true) return; es una buena forma de evitar la limitación de JLS si realmente quiere hacer esto a propósito. Si el JLS detuvo esto, estaría interfiriendo. Además, también debe detenerse:

  public static boolean DEBUG = true; //In some global class somewhere else ... if (DEBUG) return; //in a completely unrelated class. ... 

Porque la constante DEBUG está completamente alineada, y funcionalmente equivale a simplemente escribir un verdadero en esa condición if. Desde una perspectiva JLS esos dos casos son muy similares.

La diferencia está en la semántica entre el tiempo de ejecución y el tiempo de comstackción. En su segundo ejemplo, el código se comstack en una twig if-else en bytecode, y eclipse es lo suficientemente inteligente como para decirle que la parte else nunca se alcanzará en tiempo de ejecución. Eclipse solo lo advierte, porque sigue siendo un código legal.

En su primer ejemplo, es un error porque el código es ilegal según la definición de java. El comstackdor no le permite crear código de bytes con declaraciones inalcanzables.

Intenté eclipse y creo que hay 3 tipos de manejo de código muerto de JDK: 1) no avisar, 2) advertir y 3) error.

Para un código de comstackción típico condicional “IF”, JDK detecta esto y no lo reportó como código muerto. Para un código muerto que es causado por un indicador booleano constante, JDK lo detecta e informa al nivel de advertencia. Para código muerto que es causado por el flujo de control del progtwig, JDK lo detecta como un error.

A continuación está mi bash:

  public class Setting { public static final boolean FianlDebugFlag = false; } class B { ..... // no warn, it is typical "IF" conditional comstacktaion code if(Setting.FianlDebugFlag) System.out.println("am i dead?"); if(false) System.out.println("am i dead?"); // warn, as the dead code is caused by a constant boolean flag if(ret!=null && Setting.FianlDebugFlag) System.out.println("am i dead?"); if(Setting.FinalDebug) return null; System.out.println("am i dea?"); // error, as the dead code is due to the program's control flow return null; System.out.println("am i dead"); } 

Si desea ignorar la advertencia “advertencia de código inactivo en Java bajo Eclipse”, haga lo siguiente dentro de eclipse *:

  1. Haga clic en Preferencias de ventana-Java-Comstackdor-Errores / Advertencias
  2. Haga clic en “Problemas potenciales de progtwigción”
  3. Elija “Ignorar” el “Código muerto, por ejemplo, si (falso)”
  4. Haga clic en Aplicar
  5. Haga clic en Aceptar

Guarde y cierre su eclipse IDE Cuando vuelva a abrir eclipse, estas advertencias específicas ya no se deben enumerar.

* Para esta solución de ejemplo, estoy usando Eclipse IDE para desarrolladores de Java – Versión: Mars.2 Release (4.5.2)