¿Por qué se está ejecutando código Java en comentarios con ciertos caracteres Unicode permitidos?

El siguiente código produce la salida “Hello World!” (en realidad no, pruébalo).

public static void main(String... args) { // The comment below is not a typo. // \u000d System.out.println("Hello World!"); } 

La razón de esto es que el comstackdor de Java analiza el carácter Unicode \u000d como una nueva línea y se transforma en:

 public static void main(String... args) { // The comment below is not a typo. // System.out.println("Hello World!"); } 

Por lo tanto, resulta en un comentario que se “ejecuta”.

Dado que esto se puede utilizar para “ocultar” código malicioso o lo que sea que un mal progtwigdor pueda concebir, ¿por qué está permitido en los comentarios ?

¿Por qué esto es permitido por la especificación de Java?

La deencoding Unicode tiene lugar antes que cualquier otra traducción léxica. El beneficio clave de esto es que hace que sea trivial ir y venir entre ASCII y cualquier otra encoding. ¡Ni siquiera necesita averiguar dónde comienzan y terminan los comentarios!

Como se indica en la Sección 3.3 de JLS, esto permite que cualquier herramienta basada en ASCII pueda procesar los archivos fuente:

[…] El lenguaje de progtwigción Java especifica una forma estándar de transformar un progtwig escrito en Unicode en ASCII que cambia un progtwig en un formulario que puede ser procesado por herramientas basadas en ASCII. […]

Esto proporciona una garantía fundamental para la independencia de la plataforma (independencia de los conjuntos de caracteres admitidos) que siempre ha sido un objective clave para la plataforma Java.

Poder escribir cualquier caracter Unicode en cualquier lugar del archivo es una característica clara, y especialmente importante en los comentarios, cuando se documenta el código en idiomas que no son latinos. El hecho de que pueda interferir con la semántica de manera tan sutil es solo un efecto secundario (desafortunado).

Hay muchos errores en este tema y Java Puzzlers por Joshua Bloch y Neal Gafter incluyen la siguiente variante:

¿Es este un progtwig legal de Java? Si es así, ¿qué imprime?

 \u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020\u0020 \u0063\u006c\u0061\u0073\u0073\u0020\u0055\u0067\u006c\u0079 \u007b\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020 \u0020\u0020\u0020\u0020\u0073\u0074\u0061\u0074\u0069\u0063 \u0076\u006f\u0069\u0064\u0020\u006d\u0061\u0069\u006e\u0028 \u0053\u0074\u0072\u0069\u006e\u0067\u005b\u005d\u0020\u0020 \u0020\u0020\u0020\u0020\u0061\u0072\u0067\u0073\u0029\u007b \u0053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074 \u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e\u0028\u0020 \u0022\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u0022\u002b \u0022\u006f\u0072\u006c\u0064\u0022\u0029\u003b\u007d\u007d 

(Este progtwig resulta ser un simple progtwig “Hello World”).

En la solución al rompecabezas, señalan lo siguiente:

Más en serio, este rompecabezas sirve para reforzar las lecciones de los tres anteriores: los escapes Unicode son esenciales cuando necesitas insertar caracteres que no se pueden representar de ninguna otra forma en tu progtwig. Evítelos en todos los demás casos.


Fuente: Java: ¿ejecutando el código en los comentarios?

Como esto aún no se ha abordado, aquí una explicación, por qué la traducción de Unicode se escapa antes de cualquier otro procesamiento de código fuente:

La idea detrás de esto era que permite traducciones sin pérdidas de código fuente Java entre diferentes codificaciones de caracteres. Hoy en día, existe una amplia compatibilidad con Unicode, y esto no parece un problema, pero en aquel entonces no era fácil para un desarrollador de un país occidental recibir algún código fuente de su colega asiático que contenía caracteres asiáticos, hacer algunos cambios ( incluyendo comstackr y probarlo) y devolver el resultado, todo sin dañar algo.

Por lo tanto, el código fuente de Java se puede escribir en cualquier encoding y permite una amplia gama de caracteres dentro de identificadores, caracteres y literales de String y comentarios. Luego, para transferirlo sin pérdida, todos los caracteres que no son compatibles con la encoding objective se reemplazan por sus escapes Unicode.

Este es un proceso reversible y lo interesante es que la traducción se puede realizar con una herramienta que no necesita saber nada sobre la syntax del código fuente de Java, ya que la regla de traducción no depende de él. Esto funciona a medida que la traducción a sus caracteres Unicode reales dentro del comstackdor también ocurre independientemente de la syntax del código fuente de Java. Implica que puede realizar un número arbitrario de pasos de traducción en ambas direcciones sin cambiar el significado del código fuente.

Esta es la razón de otra característica extraña que ni siquiera ha mencionado: la syntax \uuuuuuxxxx :

Cuando una herramienta de traducción escapa de los caracteres y encuentra una secuencia que ya es una secuencia escapada, debe insertar una u adicional en la secuencia, convirtiendo \ucafe a \uucafe . El significado no cambia, pero cuando se convierte en la otra dirección, la herramienta solo debe eliminar una y reemplazar solo las secuencias que contienen una sola u por sus caracteres Unicode. De esta forma, incluso los escapes de Unicode se conservan en su forma original al convertir de ida y vuelta. Supongo que nadie usó esa característica …

Agregaré el punto de manera totalmente ineficaz, simplemente porque no puedo evitarlo y no lo he visto aún, que la pregunta no es válida, ya que contiene una premisa oculta que es incorrecta, a saber, que el código está en ¡un comentario!

En Java el código fuente \ u000d es equivalente en todos los sentidos a un carácter ASCII CR. Es un final de línea, claro y simple, donde sea que ocurra. El formato de la pregunta es engañoso, a lo que esa secuencia de caracteres corresponde sintácticamente es a:

 public static void main(String... args) { // The comment below is no typo. // System.out.println("Hello World!"); } 

En mi humilde opinión, la respuesta más correcta es por lo tanto: el código se ejecuta porque no está en un comentario; está en la siguiente línea. “Ejecutar código en comentarios” no está permitido en Java, como cabría esperar.

Gran parte de la confusión proviene del hecho de que los marcadores de syntax y los IDE no son lo suficientemente sofisticados como para tener en cuenta esta situación. O bien no procesan los escapes Unicode en absoluto, o lo hacen después de analizar el código en lugar de antes, como lo hace javac .

El \u000d escape termina un comentario porque \u escapes se convierten uniformemente a los caracteres Unicode correspondientes antes de que el progtwig se convierta en simbólico. También puede usar \u0057\u0057 lugar de // para comenzar un comentario.

Este es un error en su IDE, que debe syntax: resaltar la línea para dejar en claro que el \u000d finaliza el comentario.

Esto también es un error de diseño en el lenguaje. No se puede corregir ahora, porque eso rompería los progtwigs que dependen de él. \u escapes deben ser convertidos al carácter Unicode correspondiente por el comstackdor solo en contextos donde “tiene sentido” (literales e identificadores de cadena, y probablemente en ningún otro lado) o se les debería haber prohibido generar caracteres en el U + 0000- Rango 007F, o ambos. Cualquiera de esas semánticas habría impedido que el comentario termine por el escape \u000d , sin interferir con los casos donde \u escapes son útiles. Tenga en cuenta que eso incluye el uso de \u escapa dentro de los comentarios como una forma de codificar comentarios en un no -Latin script, porque el editor de texto podría tener una visión más amplia de dónde \u escapes son significativos de lo que hace el comstackdor. (No tengo conocimiento de ningún editor o IDE que muestre \u escapes como los caracteres correspondientes en cualquier contexto, sin embargo).

Hay un error de diseño similar en la familia C, 1 donde la barra diagonal inversa-línea nueva se procesa antes de que se determinen los límites de los comentarios, por ejemplo

 // this is a comment \ this is still in the comment! 

Lo menciono para ilustrar que es fácil hacer este error de diseño en particular, y no me doy cuenta de que es un error hasta que sea demasiado tarde para corregirlo, si estás acostumbrado a pensar en tokenización y analizar la forma en que los progtwigdores de comstackción piensan sobre tokenización y análisis. Básicamente, si ya ha definido su gramática formal y luego alguien presenta un caso sintáctico especial – trigrafos, barra diagonal inversa – línea nueva, encoding de caracteres Unicode arbitrarios en archivos fuente limitados a ASCII, lo que sea – que debe ser insertado, es más fácil agregue un pase de transformación antes del tokenizador que redefinir el tokenizador para prestar atención a dónde tiene sentido usar ese caso especial.

1 Para pedantes: soy consciente de que este aspecto de C fue 100% intencional, con la razón, no me lo estoy inventando, de que te permitiría ajustar mecánicamente el código con líneas arbitrariamente largas en tarjetas perforadas. Todavía era una decisión de diseño incorrecta.

Esta fue una elección de diseño intencional que se remonta al diseño original de Java.

Para aquellos que preguntan “¿quién quiere escapar de Unicode en los comentarios?”, Supongo que son personas cuyo idioma nativo usa el conjunto de caracteres latinos. En otras palabras, es inherente al diseño original de Java que la gente pueda usar caracteres Unicode arbitrarios siempre que sean legales en un progtwig Java, más típicamente en comentarios y cadenas.

Podría decirse que es un defecto en los progtwigs (como IDEs) que se utilizan para ver el texto de origen que dichos progtwigs no pueden interpretar los escapes Unicode y mostrar el glifo correspondiente.

Estoy de acuerdo con @zwol en que este es un error de diseño; pero soy aún más crítico con eso.

\u escape es útil en los literales de cadena y de caracteres; y ese es el único lugar donde debería existir. Se debe manejar de la misma manera que otras escapes como \n ; y "\u000A" debe significar exactamente "\n" .

No tiene ningún sentido tener \uxxxx en comentarios, nadie puede leer eso.

Del mismo modo, no tiene sentido usar \uxxxx en otra parte del progtwig. La única excepción es probablemente en las API públicas que están obligadas a contener algunos caracteres no ascii: ¿cuál fue la última vez que hemos visto eso?

Los diseñadores tenían sus razones en 1995, pero 20 años después, esta parece ser una elección incorrecta.

(pregunta a los lectores: ¿por qué esta pregunta sigue recibiendo nuevos votos? ¿esta pregunta está vinculada desde algún lugar popular?)

Las únicas personas que pueden responder por qué Unicode escapa fueron implementadas como lo fueron las personas que escribieron la especificación.

Una razón plausible para esto es que existía el deseo de permitir que todo el BMP fuera posible como caracteres del código fuente de Java. Esto presenta un problema sin embargo:

  • Desea poder usar cualquier personaje BMP.
  • Desea poder ingresar cualquier chapaleta BMP de forma razonablemente fácil. Una forma de hacerlo es con Unicode escapes.
  • Desea que las especificaciones léxicas sean fáciles de leer y escribir para los seres humanos, y también razonablemente fáciles de implementar.

Esto es increíblemente difícil cuando Unicode escapa entra en la refriega: crea una carga completa de nuevas reglas lexer.

La salida más fácil es hacer el léxico en dos pasos: primero buscar y reemplazar todos los escapes Unicode con el carácter que representa, y luego analizar el documento resultante como si los escapes Unicode no existieran.

La ventaja de esto es que es fácil de especificar, por lo que simplifica la especificación y es fácil de implementar.

La desventaja es, bueno, tu ejemplo.

El comstackdor no solo traduce los escapes Unicode en los caracteres que representan antes de analizar un progtwig en tokens, sino que lo hace antes de descartar los comentarios y el espacio en blanco.

Este progtwig contiene un único escape Unicode (\ u000d), ubicado en su único comentario. Como dice el comentario, este escape representa el carácter de salto de línea, y el comstackdor lo traduce debidamente antes de descartar el comentario .

Esto depende de la plataforma. En ciertas plataformas, como UNIX, funcionará; en otros, como Windows, no lo hará. Aunque la salida puede parecer igual a simple vista, fácilmente podría causar problemas si se guardara en un archivo o se transfiriera a otro progtwig para su posterior procesamiento.