El siguiente código produce el resultado "¡Hola, mundo!" (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 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 "ejecutado".
Dado que esto puede usarse para "ocultar" código malicioso o lo que sea que un programador malvado pueda concebir, ¿por qué está permitido en los comentarios ?
¿Por qué está permitido por la especificación de Java?
Respuestas:
La decodificación Unicode tiene lugar antes de 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 necesita saber dónde comienzan y terminan los comentarios!
Como se indicó en la Sección 3.3 de JLS, esto permite que cualquier herramienta basada en ASCII procese los archivos fuente:
Esto ofrece una garantía fundamental para la independencia de la plataforma (independencia de los conjuntos de caracteres compatibles) que siempre ha sido un objetivo clave para la plataforma Java.
Poder escribir cualquier carácter Unicode en cualquier parte del archivo es una característica interesante, y especialmente importante en los comentarios, al documentar código en idiomas no latinos. El hecho de que pueda interferir con la semántica de manera tan sutil es solo un efecto secundario (desafortunado).
Hay muchos problemas con este tema y Java Puzzlers de Joshua Bloch y Neal Gafter incluyeron la siguiente variante:
(Este programa resulta ser un simple programa de "Hola mundo").
En la solución al rompecabezas, señalan lo siguiente:
Fuente: Java: ¿Ejecutar código en los comentarios?
fuente
\u000d
y la parte posterior debe tener resaltados de código.// C:\user\...
que conduce a un error de compilación ya\user
que no es una secuencia de escape Unicode válida.\u000d
se resalta parcialmente. Después de presionar Ctrl + Shift + F, el carácter se reemplaza con una nueva línea y el resto de la línea se\u002A/
Debería terminar el comentario.Como esto aún no se ha abordado, aquí hay una explicación de por qué la traducción de los escapes de Unicode ocurre antes de cualquier otro procesamiento del código fuente:
La idea detrás de esto era que permite traducciones sin pérdida de código fuente de Java entre diferentes codificaciones de caracteres. Hoy en día, existe un amplio soporte de 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 compilarlo 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 codificación y permite una amplia gama de caracteres dentro de identificadores, caracteres y
String
literales y comentarios. Luego, para transferirlo sin pérdidas, todos los caracteres no admitidos 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 puede hacerse 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 ya que la traducción a sus caracteres Unicode reales dentro del compilador también ocurre independientemente de la sintaxis del código fuente de Java. Implica que puede realizar una cantidad arbitraria 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
\uuuuuuxxxx
sintaxis:Cuando una herramienta de traducción está escapando caracteres y encuentra una secuencia que ya es una secuencia escapada, debe insertar una
u
secuencia adicional en la secuencia, convirtiéndola\ucafe
en\uucafe
. El significado no cambia, pero cuando se convierte en la otra dirección, la herramienta solo debe eliminar unau
y reemplazar solo las secuencias que contienen una solau
por sus caracteres Unicode. De esa manera, incluso los escapes de Unicode se conservan en su forma original al convertir de ida y vuelta. Supongo que nadie usó esa característica ...fuente
native2ascii
no parece usar la\uu...xxxx
sintaxis,native2ascii
tenía la intención de ayudar a preparar paquetes de recursos al convertirlos a iso-latin-1, ya queProperties.load
se corrigió para leer solo latin-1. Y allí, las reglas son diferentes, sin\uuu…
sintaxis ni etapa de procesamiento temprana. En los archivos de propiedades,property=multi\u000aline
es de hecho lo mismo queproperty=multi\nline
. (Contradiciendo a la frase "usar escapes Unicode como se define en la sección 3.3 de La especificación del lenguaje Java ™" de la documentación)\u
escapes para generar caracteres en el rango U + 0000–007F. (Todos estos caracteres pueden ser representados de forma nativa por todas las codificaciones nacionales que eran relevantes en la década de 1990, bueno, tal vez, excepto algunos de los caracteres de control, pero no es necesario escribir aquellos a Java de todos modos.)Voy a agregar completamente ineficazmente el punto, solo 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, simple y llano, donde sea que ocurra. El formato en la pregunta es engañoso, a lo que corresponde esa secuencia de caracteres sintácticamente es:
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 línea siguiente. "Ejecutar código en 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. No procesan los escapes Unicode en absoluto, o lo hacen después de analizar el código en lugar de antes, como lo
javac
hace.fuente
El
\u000d
escape finaliza un comentario porque los\u
escapes se convierten uniformemente a los caracteres Unicode correspondientes antes de que el programa se tokenice. Se podría utilizar igualmente\u0057\u0057
en lugar de//
a comenzar un comentario.Este es un error en su IDE, que debe sintaxis resaltar la línea para dejar en claro que
\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 programas que dependen de él.
\u
el compilador debe convertir los escapes al carácter Unicode correspondiente solo en contextos donde eso "tiene 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 terminara con el\u000d
escape, sin interferir con los casos en los que los\u
escapes son útiles; tenga en cuenta que eso incluye el uso de\u
escapes dentro de los 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
los escapes son significativos que el compilador. (Sin embargo, no conozco ningún editor o IDE que muestre\u
escapes como los caracteres correspondientes en ningún contexto).Hay un error de diseño similar en la familia C, 1 donde la barra diagonal inversa-nueva línea se procesa antes de que se determinen los límites de los comentarios, por ejemplo
Menciono esto para ilustrar que resulta fácil cometer este error de diseño en particular, y no darme cuenta de que es un error hasta que sea demasiado tarde para corregirlo, si estás acostumbrado a pensar en la tokenización y analizar la forma en que piensan los programadores del compilador. sobre tokenización y análisis. Básicamente, si ya ha definido su gramática formal y luego a alguien se le ocurre un caso especial sintáctico: trigrafos, barra invertida-nueva línea, codificación de caracteres Unicode arbitrarios en archivos fuente limitados a ASCII, lo que sea, que necesita ser encajado, 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 los pedantes: Soy consciente de que este aspecto de C fue 100% intencional, con la razón, no estoy inventando esto, de que te permitiría forzar mecánicamente el código de ajuste forzado con líneas arbitrariamente largas en tarjetas perforadas. Todavía era una decisión de diseño incorrecta.
fuente
\u
fue menos absurda que la decisión de seguir el ejemplo de C al usar ceros iniciales para la notación octal. Si bien la notación octal a veces es útil, todavía no he escuchado a nadie articular un argumento de por qué un cero inicial es una buena manera de indicarlo.\u
como transformación previa a la tokenización si estuviera prohibido producir caracteres en el rango U + 0000..U + 007F. Es la combinación de "esto funciona en todas partes" y "este alias de caracteres ASCII con significado sintáctico" lo que lo degrada de incómodo a completamente equivocado.//
comentario de una sola línea no existía . Y dado que C tiene un terminador de declaración que no es una línea nueva, se usaría principalmente para cadenas largas, excepto que, por lo que puedo determinar, la "concatenación literal de cadenas" estaba allí desde K&R.Esta fue una elección de diseño intencional que se remonta al diseño original de Java.
Para aquellas personas que preguntan "¿quién quiere escapar de Unicode 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 pueda 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 IDE) que se utilizan para ver el texto fuente de que dichos programas no pueden interpretar los escapes de Unicode y mostrar el glifo correspondiente.
fuente
Estoy de acuerdo con @zwol en que esto es un error de diseño; pero lo critico aún más.
\u
escape es útil en cadenas y char literales; y ese es el único lugar donde debería existir. Debe manejarse de la misma manera que otros escapes como\n
; y"\u000A"
debe significar exactamente"\n"
.No tiene ningún sentido tener
\uxxxx
comentarios, nadie puede leer eso.Del mismo modo, no tiene sentido usarlo
\uxxxx
en otra parte del programa. La única excepción es probablemente en las API públicas que están obligadas a 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 incorrecta.
(pregunta para los lectores: ¿por qué esta pregunta sigue obteniendo nuevos votos? ¿Esta pregunta está vinculada desde algún lugar popular?)
fuente
int \u5431
cuando puedes hacerlo?int 整
UTF-8
apoyo en 1995). Solo tiene que llamar a un método y no desea instalar el paquete de soporte de idiomas asiáticos de su sistema operativo (recuerde, los noventa) para ese método único ...Las únicas personas que pueden responder por qué se implementaron los escapes de Unicode tal como fueron son 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. Sin embargo, esto presenta un problema:
Esto es increíblemente difícil cuando los escapes de Unicode entran en juego: crea una carga completa de nuevas reglas lexer.
La salida fácil es hacer lexing en dos pasos: primero busque y reemplace todos los escapes Unicode con el carácter que representa, y luego analice 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.
fuente