¿Por qué se permite ejecutar código Java en comentarios con ciertos caracteres Unicode?


El siguiente código produce la salida "Hello World!"(no realmente, 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 compilador 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!");
}

Lo que resulta en un comentario siendo "ejecutado".

Dado que esto se puede usar para "ocultar" código malicioso o lo que un programador malvado pueda concebir, ¿por qué se permite en los comentarios?

¿Por qué está permitido esto por Java especificación?

Author: Peter Mortensen, 2015-06-09

8 answers

La decodificación 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 codificación. ¡Ni siquiera necesitas averiguar dónde comienzan y terminan los comentarios!

Como se indica en JLS Sección 3.3 esto permite que cualquier herramienta basada en ASCII procese los archivos fuente:

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

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

Ser capaz de escribir cualquier carácter Unicode en cualquier lugar del archivo es una característica interesante, y especialmente importante en los comentarios, cuando se documenta código en idiomas no latinos. El hecho de que puede interferir con la semántica en tal las formas sutiles son solo un (desafortunado) efecto secundario.

Hay muchas trampas sobre este tema y Rompecabezas de Java by Joshua Bloch and Neal Gafter included the following variant:

¿ Es este un programa Java legal? 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 programa resulta ser un programa simple de "Hola Mundo".)

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

Más en serio, este rompecabezas sirve para refuerce las lecciones de los tres anteriores: Los escapes Unicode son esenciales cuando necesita insertar caracteres que no se pueden representar de ninguna otra manera en su programa. Evitarlos en todos los demás casos.


Source: Java: ¿Ejecutando código en los comentarios?!

 692
Author: aioobe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-29 03:56:24

Dado que esto aún no se ha abordado, aquí una explicación, por qué la traducción de los escapes Unicode ocurre antes que 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, hay un soporte Unicode generalizado, y esto no parece un problema, pero en ese 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 asiático hacer algunos cambios (incluyendo compilar y probar) y enviar el resultado de vuelta, todo sin dañar algo.

Por lo tanto, el código fuente de Java se puede escribir en cualquier codificación y permite una amplia gama de caracteres dentro de los identificadores, caracteres y String literales y comentarios. Luego, para transferirlo sin pérdidas, todos los caracteres no soportados por la codificación de destino son reemplazados por sus escapes Unicode.

Este es un proceso reversible y el punto interesante es que la traducción se puede hacer mediante una herramienta que no necesita saber nada sobre la sintaxis del código fuente de Java, ya que la regla de traducción no depende de ella. Esto funciona a medida que la traducción a sus caracteres Unicode reales dentro del compilador ocurre independientemente de la sintaxis del código fuente de Java también. 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 otro extraño característica que ni siquiera ha mencionado: la sintaxis \uuuuuuxxxx:

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

 133
Author: Holger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-11 15:31:38

Voy a agregar completamente ineficazmente el punto, solo porque no puedo evitarlo y no lo he visto hecho todavía, 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 el código fuente de Java \u000d es equivalente en todos los sentidos a un carácter ASCII CR. Es un final de línea, simple y llanamente, dondequiera que ocurra. El formato en la pregunta es engañoso, lo que esa secuencia de caracteres en realidad sintácticamente corresponde a is:

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: el código se ejecuta porque no está en un comentario; está en la siguiente línea. "Ejecutar código en los comentarios" no está permitido en Java, como es de esperar.

Gran parte de la confusión proviene del hecho de que los resaltadores de sintaxis 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 javac hace.

 98
Author: Pepijn Schmitz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-10 17:37:17

El escape \u000d termina un comentario porque \u los escapes se convierten uniformemente a los caracteres Unicode correspondientes antes de que el programa sea tokenizado. También puedes usar \u0057\u0057 en lugar de // para comenzar un comentario.

Este es un error en su IDE, que debería resaltar la línea para dejar claro que \u000d termina el comentario.

Esto también es un error de diseño en el lenguaje. No se puede corregir ahora, porque eso se rompería programas que dependen de ella. \u los escapes deben ser convertidos al carácter Unicode correspondiente por el compilador solo en contextos donde "tenga sentido" (literales de cadena e identificadores, y probablemente en ningún otro lugar) o se les debería haber prohibido generar caracteres en el rango U+0000–007F, o ambos. Cualquiera de esas semánticas habría evitado que el comentario fuera terminado por el escape \u000d, sin interferir con los casos en los que los escapes \u son útiles-nota que incluye el uso de \u escapes dentro de comentarios como una forma de codificar comentarios en un script no latino, porque el editor de texto podría tener una visión más amplia de dónde \u escapes son significativos que el compilador. (No conozco 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 se procesa la barra invertida-nueva línea antes del comentario los límites se determinan, por ejemplo,

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

Traigo esto para ilustrar que resulta fácil hacer este error de diseño en particular, y no me doy cuenta de que es un error hasta que es demasiado tarde para corregirlo, si está acostumbrado a pensar en la tokenización y el análisis de la forma en que los programadores del compilador piensan sobre la tokenización y el análisis. Básicamente, si ya ha definido su gramática formal y luego alguien viene con un caso especial sintáctico-trigraphs, barra invertida-nueva línea, codificar caracteres Unicode arbitrarios en archivos de código fuente limitados a ASCII, lo que sea-eso necesita encajarse, es más fácil agregar un pase de transformación antes de el tokenizer que redefinir el tokenizer para prestar atención a dónde tiene sentido usar ese caso especial.

1 Para pedantes: Soy consciente de que este aspecto de C era 100% intencional, con la lógica-no estoy inventando esto - que le permitiría forzar mecánicamente el código con líneas arbitrariamente largas sobre tarjetas perforadas. Seguía siendo una decisión de diseño incorrecta.

 63
Author: zwol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-15 17:43:58

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

A aquellas personas que preguntan "¿quién quiere que Unicode escape en los comentarios?", supongo que son personas cuya lengua materna utiliza el conjunto de caracteres latinos. En otras palabras, es inherente al diseño original de Java que la gente podría usar caracteres Unicode arbitrarios donde sea legal en un programa Java, más típicamente en comentarios y cadenas.

Podría decirse que es una deficiencia en los programas (como IDEs) se utiliza para ver el texto fuente que dichos programas no pueden interpretar los escapes Unicode y mostrar el glifo correspondiente.

 21
Author: Jonathan Gibbons,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-01 17:42:47

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

\u escape es útil en literales string y char; y ese es el único lugar en el que debería existir. Debe manejarse de la misma manera que otros escapes como \n; y "\u000A" debe significar exactamente "\n".

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

Del mismo modo, no tiene sentido usar \uxxxx en otra parte del programa. El único la excepción está probablemente en APIs públicas que son coaccionadas para contener algunos caracteres no ascii - ¿cuál es la última vez que hemos visto eso?

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

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

 21
Author: ZhongYu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-08 23:19:53

Las únicas personas que pueden responder por qué los escapes Unicode se implementaron tal como estaban son las personas que escribieron la especificación.

Una razón plausible para esto es que había el deseo de permitir todo el BMP como posibles caracteres del código fuente de Java. Sin embargo, esto presenta un problema:

  • Desea poder utilizar cualquier carácter BMP.
  • Usted quiere ser capaz de introducir cualquier BMP charater razonablemente fácil. Una forma de hacer esto es con Unicode escapes.
  • Quieres para mantener la especificación léxica fácil de leer y escribir para los humanos, y razonablemente fácil de implementar también.

Esto es increíblemente difícil cuando los escapes Unicode entran en la refriega: crea toda una carga de nuevas reglas lexer.

La salida fácil es hacer lexing 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 especifique, por lo que hace que la especificación sea más simple y fácil de implementar.

La desventaja es, bueno, tu ejemplo.

 11
Author: Martijn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-11 15:33:32

El compilador no solo traduce los escapes Unicode a los caracteres que representan antes de analizar un programa en tokens, sino que lo hace antes de descartar comentarios y espacios en blanco.

Este programa contiene un único escape Unicode (\u000d), ubicado en su único comentario. Como dice el comentario, este escape representa el carácter linefeed, y el compilador lo traduce debidamente antes de descartar el comentario.

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

 1
Author: Arp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-24 11:05:25