Le code suivant produit la sortie "Hello World!" (non vraiment, essayez-le).
public static void main(String... args) {
// The comment below is not a typo.
// \u000d System.out.println("Hello World!");
}
La raison en est que le compilateur Java analyse le caractère Unicode \u000d
comme une nouvelle ligne et se transforme en:
public static void main(String... args) {
// The comment below is not a typo.
//
System.out.println("Hello World!");
}
De ce fait, un commentaire est "exécuté".
Puisque cela peut être utilisé pour "cacher" du code malveillant ou tout ce qu'un mauvais programmeur peut concevoir, pourquoi est-il autorisé dans les commentaires ?
Pourquoi cela est-il autorisé par la spécification Java?
Réponses:
Le décodage Unicode a lieu avant toute autre traduction lexicale. Le principal avantage de ceci est qu'il est trivial de faire des allers-retours entre ASCII et tout autre encodage. Vous n'avez même pas besoin de savoir où commencent et finissent les commentaires!
Comme indiqué dans la section 3.3 de JLS, cela permet à tout outil basé sur ASCII de traiter les fichiers source:
Cela donne une garantie fondamentale pour l'indépendance de la plate-forme (indépendance des jeux de caractères pris en charge) qui a toujours été un objectif clé pour la plate-forme Java.
La possibilité d'écrire n'importe quel caractère Unicode n'importe où dans le fichier est une fonctionnalité intéressante, et particulièrement importante dans les commentaires, lors de la documentation du code dans des langues non latines. Le fait qu'il puisse interférer avec la sémantique de manière si subtile n'est qu'un effet secondaire (malheureux).
Il existe de nombreux pièges sur ce thème et Java Puzzlers de Joshua Bloch et Neal Gafter comprenait la variante suivante:
(Ce programme s'avère être un simple programme "Hello World".)
Dans la solution au casse-tête, ils soulignent ce qui suit:
Source: Java: exécuter du code dans les commentaires?!
la source
\u000d
et la partie qui suit doit avoir des reflets de code.// C:\user\...
ce qui entraîne une erreur de compilation car il\user
ne s'agit pas d'une séquence d'échappement Unicode valide.\u000d
est partiellement mis en évidence. Après avoir appuyé sur Ctrl + Maj + F, le caractère est remplacé par une nouvelle ligne et le reste de la ligne est\u002A/
devrait terminer le commentaire.Puisque cela n'a pas encore été résolu, voici une explication, pourquoi la traduction des échappements Unicode se produit avant tout autre traitement de code source:
L'idée derrière cela était qu'il permet des traductions sans perte de code source Java entre différents encodages de caractères. Aujourd'hui, il existe un support Unicode répandu, et cela ne semble pas poser de problème, mais à l'époque, il n'était pas facile pour un développeur d'un pays occidental de recevoir du code source de son collègue asiatique contenant des caractères asiatiques, apportez quelques modifications ( y compris la compilation et le test) et l'envoi du résultat, le tout sans endommager quelque chose.
Ainsi, le code source Java peut être écrit dans n'importe quel encodage et permet une large gamme de caractères dans les identifiants, les caractères et les
String
littéraux et les commentaires. Ensuite, afin de le transférer sans perte, tous les caractères non pris en charge par l'encodage cible sont remplacés par leurs échappements Unicode.Il s'agit d'un processus réversible et le point intéressant est que la traduction peut être effectuée par un outil qui n'a pas besoin de connaître la syntaxe du code source Java car la règle de traduction n'en dépend pas. Cela fonctionne car la traduction de leurs caractères Unicode réels à l'intérieur du compilateur s'effectue également indépendamment de la syntaxe du code source Java. Cela implique que vous pouvez effectuer un nombre arbitraire d'étapes de traduction dans les deux sens sans jamais changer la signification du code source.
C'est la raison d'une autre fonctionnalité étrange qui n'a même pas mentionné: la
\uuuuuuxxxx
syntaxe:Lorsqu'un outil de traduction échappe des caractères et rencontre une séquence qui est déjà une séquence échappée, il doit insérer un élément supplémentaire
u
dans la séquence, en le convertissant\ucafe
en\uucafe
. La signification ne change pas, mais lors de la conversion dans l'autre sens, l'outil doit simplement en supprimer uneu
et remplacer uniquement les séquences contenant une seuleu
par leurs caractères Unicode. De cette façon, même les échappements Unicode sont conservés dans leur forme d'origine lors de la conversion dans les deux sens. Je suppose que personne n'a jamais utilisé cette fonctionnalité…la source
native2ascii
ne semble pas utiliser la\uu...xxxx
syntaxe,native2ascii
était destiné à aider à la préparation des bundles de ressources en les convertissant en iso-latin-1 comme il aProperties.load
été corrigé pour lire latin-1 uniquement. Et là, les règles sont différentes, pas de\uuu…
syntaxe et pas de stade de traitement précoce. Dans les fichiers de propriétés,property=multi\u000aline
est en effet le même queproperty=multi\nline
. (En contradiction avec l'expression «à l'aide des échappements Unicode tels que définis dans la section 3.3 de la spécification du langage Java ™» de la documentation)\u
échappements pour générer des caractères dans la plage U + 0000–007F. (Tous ces caractères peuvent être représentés nativement par tous les encodages nationaux qui étaient pertinents dans les années 1990 - enfin, peut-être à l'exception de certains des caractères de contrôle, mais vous n'en avez pas besoin de toute façon pour écrire Java.)Je vais ajouter complètement inefficacement le point, juste parce que je ne peux pas m'en empêcher et que je ne l'ai pas encore vu, que la question n'est pas valide car elle contient une prémisse cachée qui est erronée, à savoir que le code est en un commentaire!
En Java, le code source \ u000d est à tous égards équivalent à un caractère ASCII CR. C'est une fin de ligne, claire et simple, où qu'elle se produise. La mise en forme de la question est trompeuse, à quoi correspond exactement cette séquence de caractères syntaxiquement:
À mon humble avis, la réponse la plus correcte est donc: le code s'exécute parce qu'il n'est pas dans un commentaire; c'est sur la ligne suivante. "Exécuter du code dans les commentaires" n'est pas autorisé en Java, comme vous pouvez vous y attendre.
Une grande partie de la confusion provient du fait que les surligneurs de syntaxe et les IDE ne sont pas suffisamment sophistiqués pour prendre en compte cette situation. Soit ils ne traitent pas du tout les échappements unicode, soit ils le font après avoir analysé le code plutôt qu'avant, comme c'est le
javac
cas.la source
L'
\u000d
évasion termine un commentaire car les\u
échappements sont uniformément convertis en caractères Unicode correspondants avant que le programme ne soit tokenisé. Vous pouvez également utiliser\u0057\u0057
au lieu de//
pour commencer un commentaire.Il s'agit d'un bogue dans votre IDE, qui devrait mettre en surbrillance la ligne pour indiquer clairement que
\u000d
le commentaire se termine.Il s'agit également d'une erreur de conception dans la langue. Cela ne peut pas être corrigé maintenant, car cela casserait les programmes qui en dépendent.
\u
les échappements doivent être convertis en caractère Unicode correspondant par le compilateur uniquement dans des contextes où cela "fait sens" (littéraux de chaîne et identificateurs, et probablement nulle part ailleurs) ou ils doivent avoir été interdits pour générer des caractères dans la plage U + 0000–007F , ou les deux. L'une ou l'autre de ces sémantiques aurait empêché le commentaire d'être interrompu par l'\u000d
évasion, sans interférer avec les cas où les\u
échappements sont utiles - notez que cela inclut l' utilisation des\u
échappements dans les commentaires comme moyen d'encoder les commentaires dans un script non latin, car le éditeur de texte pourrait avoir une vision plus large de l'endroit où\u
les échappements sont significatifs par rapport au compilateur. ( Cependant, je ne connais aucun éditeur ou IDE qui affichera les\u
échappements en tant que caractères correspondants dans n'importe quel contexte.)Il existe une erreur de conception similaire dans la famille C, 1 où la barre oblique inversée est traitée avant que les limites des commentaires ne soient déterminées, par exemple
J'apporte cela pour illustrer qu'il se trouve qu'il est facile de faire cette erreur de conception particulière, et de ne pas réaliser que c'est une erreur jusqu'à ce qu'il soit trop tard pour la corriger, si vous avez l'habitude de penser à la tokenisation et à l'analyse de la façon dont les programmeurs du compilateur pensent sur la tokenisation et l'analyse. Fondamentalement, si vous avez déjà défini votre grammaire formelle et que quelqu'un propose un cas spécial syntaxique - trigraphes, barre oblique inversée, encodage de caractères Unicode arbitraires dans des fichiers source limités en ASCII, peu importe - qui doit être coincé, il est plus facile de ajouter un passage de transformation avant le tokenizer que de redéfinir le tokenizer pour faire attention à l'endroit où il est judicieux d'utiliser ce cas spécial.
1 Pour les pédants: je suis conscient que cet aspect de C était 100% intentionnel, avec la justification - je n'invente rien - qu'il vous permettrait de forcer mécaniquement l'ajustement de code avec des lignes arbitrairement longues sur des cartes perforées. C'était toujours une décision de conception incorrecte.
la source
\u
était moins absurde que la décision de suivre l'exemple de C en utilisant des zéros de tête pour la notation octale. Bien que la notation octale soit parfois utile, je n'ai encore entendu personne expliquer pourquoi un zéro non significatif est un bon moyen de l'indiquer.\u
comme transformation de pré-tokenisation s'il était interdit de produire des caractères dans la plage U + 0000..U + 007F. C'est la combinaison de "cela fonctionne partout" et de "cela alias des caractères ASCII avec une signification syntaxique" qui le rétrograde de maladroit à complètement faux.//
commentaire sur une seule ligne n'existait pas . Et puisque C a un terminateur de déclaration qui n'est pas une nouvelle ligne, il serait principalement utilisé pour les chaînes longues, sauf que pour autant que je puisse déterminer la "concaténation littérale de chaîne" était là de K&R.Il s'agissait d'un choix de conception intentionnel qui remonte à la conception originale de Java.
À ceux qui demandent "qui veut échapper à Unicode dans les commentaires?", Je suppose que ce sont des gens dont la langue maternelle utilise le jeu de caractères latin. En d'autres termes, il est inhérent à la conception originale de Java que les gens puissent utiliser des caractères Unicode arbitraires partout où cela est légal dans un programme Java, le plus souvent dans les commentaires et les chaînes.
Il s'agit sans doute d'une lacune dans les programmes (comme les IDE) utilisés pour afficher le texte source que ces programmes ne peuvent pas interpréter les échappements Unicode et afficher le glyphe correspondant.
la source
Je suis d'accord avec @zwol qu'il s'agit d'une erreur de conception; mais j'en suis encore plus critique.
\u
escape est utile dans les littéraux chaîne et char; et c'est le seul endroit où cela devrait exister. Il doit être traité de la même manière que les autres évasions comme\n
; et"\u000A"
devrait signifier exactement"\n"
.Il est absolument inutile d'avoir des
\uxxxx
commentaires - personne ne peut le lire.De même, il est inutile d'utiliser
\uxxxx
dans une autre partie du programme. La seule exception est probablement dans les API publiques qui sont contraintes de contenir des caractères non ascii - quelle est la dernière fois que nous avons vu cela?Les concepteurs ont eu leurs raisons en 1995, mais 20 ans plus tard, cela semble être un mauvais choix.
(question aux lecteurs - pourquoi cette question continue-t-elle d'obtenir de nouveaux votes? cette question est-elle liée d'un endroit populaire?)
la source
int \u5431
quand vous pouvez le faireint 整
UTF-8
prise en charge généralisée en 1995). Vous n'avez qu'à appeler une méthode et vous ne voulez pas installer le pack de prise en charge des langues asiatiques de votre système d'exploitation (rappelez-vous, les années 90) pour cette seule méthode…Les seules personnes qui peuvent expliquer pourquoi les échappements Unicode ont été implémentés tels quels sont les personnes qui ont écrit la spécification.
Une raison plausible à cela est qu'il y avait le désir d'autoriser l'ensemble du BMP en tant que caractères possibles du code source Java. Cela pose cependant un problème:
C'est incroyablement difficile lorsque les échappements Unicode entrent dans la mêlée: cela crée toute une série de nouvelles règles de lexer.
La solution la plus simple consiste à effectuer une lexing en deux étapes: recherchez et remplacez d'abord tous les échappements Unicode par le caractère qu'il représente, puis analysez le document résultant comme si les échappements Unicode n'existaient pas.
L'avantage est qu'il est facile à spécifier, il simplifie donc la spécification et est facile à mettre en œuvre.
L'inconvénient est bien votre exemple.
la source