Pourquoi l'exécution de code Java dans les commentaires avec certains caractères Unicode est-elle autorisée?

Le code suivant produit la sortie " Hello World!"(non, vraiment, essayez).

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 {[2] } 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!");
}

Résultant ainsi en un commentaire étant "exécuté".

Puisque cela peut être utilisé pour "cacher" du code malveillant ou tout ce qu'un programmeur maléfique peut concevoir, pourquoi est-il autorisé dans les commentaires ?

Pourquoi est-ce autorisé par Java cahier des charges?

1261
demandé sur Peter Mortensen 2015-06-09 12:02:16

8 réponses

Le décodage Unicode a lieu avant toute autre traduction lexicale. Le principal avantage de ceci est qu'il est trivial d'aller et venir entre ASCII et tout autre encodage. Vous n'avez même pas besoin de comprendre où les commentaires commencent et finissent!

Comme indiqué dans JLS section 3.3 {[5] } cela permet à tout outil ASCII de traiter les fichiers source:

[...] Le langage de programmation Java spécifie une manière standard de transformer un programme écrit en Unicode en ASCII cela transforme un programme en un formulaire qui peut être traité par des outils basés sur ASCII. [...]

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.

Pouvoir é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 peut interférer avec la sémantique dans un tel subtil ways est juste un effet secondaire (malheureux).

Il y a beaucoup de pièges sur ce thème et Puzzlers Java par Joshua Bloch et Neal Gafter inclus la variante suivante:

Est-ce un programme Java légal? Si oui, quel est-il de l'impression?

\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

(Ce programme s'avère être un programme "Hello World".)

Dans la solution au casse-tête, ils soulignent ce qui suit:

Plus sérieusement, ce puzzle sert à renforcez les leçons des trois précédents: les évasions Unicode sont essentielles lorsque vous devez insérer des caractères qui ne peuvent pas être représentés d'une autre manière dans votre programme. Éviter dans tous les autres cas.


Source: Java: exécution du code dans les commentaires?!

692
répondu aioobe 2016-10-29 03:56:24

Comme cela n'a pas encore été abordé, 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 ressemble pas à un 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 Asian caractères, faites quelques changements (y compris la compilation et le test) et renvoyez le résultat, le tout sans endommager quelque chose.

Ainsi, le code source Java peut être écrit dans n'importe quel encodage et permet un large éventail de caractères dans les identifiants, les caractères et les littéraux String 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.

C'est 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 savoir quoi que ce soit sur la syntaxe du code source Java car la règle de traduction n'en dépend pas. Cela fonctionne comme la traduction de leurs caractères Unicode réels à l'intérieur du compilateur se produit 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'un autre bizarre fonctionnalité qui n'a même pas mentionné: la syntaxe \uuuuuuxxxx:

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 u supplémentaire dans la séquence, convertissant \ucafe en \uucafe. La signification ne change pas, mais lors de la conversion dans l'autre sens, l'outil doit simplement supprimer un u et remplacer uniquement les séquences contenant un seul u par leurs caractères Unicode. De cette façon, même les évasions Unicode sont conservées dans leur forme originale lors de la conversion d'avant en arrière. Je suppose que personne n'a jamais utilisé cette fonctionnalité ...

133
répondu Holger 2015-07-11 15:31:38

Je vais complètement ajouter 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 est invalide car elle contient une prémisse cachée qui est fausse, à savoir que le code est dans un commentaire!

Dans le code source Java, \u000d est équivalent à un caractère CR ASCII. C'est une fin de ligne, pure et simple, où qu'elle se produise. La mise en forme dans la question Est trompeuse, ce que cette séquence de caractères en fait syntaxiquement correspond à est:

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

IMHO la réponse la plus correcte est donc: le code s'exécute car 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, tout comme vous vous y attendriez.

Une grande partie de la confusion provient du fait que les surligneurs de syntaxe et les IDE ne sont pas assez sophistiqués pour prendre en compte cette situation. Ils ne traitent pas du tout les échappements unicode, ou ils le font après avoir analysé le code au lieu d'avant, comme le fait javac.

98
répondu Pepijn Schmitz 2015-06-10 17:37:17

L'échappement \u000d termine un commentaire car les échappements \usont 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.

Ceci est un bug dans votre IDE, qui devrait mettre en surbrillance la ligne pour indiquer clairement que le \u000d termine le commentaire.

C'est aussi une erreur de conception dans la langue. Il ne peut pas être corrigé maintenant, parce que cela casserait les programmes qui en dépendent. \u les évasions doivent soit être converties au caractère Unicode correspondant par le compilateur uniquement dans des contextes où cela "a du sens" (littéraux de chaîne et identifiants, et probablement nulle part ailleurs), soit il leur aurait été interdit de 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 terminé par l'échappement \u000d, sans interférer avec les cas où les échappements \u sont utiles-note que inclut l'utilisation de \u escapes dans les commentaires comme un moyen d'encoder les commentaires dans un script non Latin, car l'éditeur de texte pourrait avoir une vue plus large de l'endroit où \u escapes sont significatifs que le compilateur. (Je ne connais aucun éditeur ou IDE qui affichera \u escapes en tant que caractères correspondants dans Tout contexte, cependant.)

Il y a une erreur de conception similaire dans la famille C,1 où backslash-newline est traité avant le commentaire les limites sont déterminées, par exemple

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

J'apporte ceci pour illustrer qu'il est facile de faire cette erreur de conception particulière, et 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 à la tokenisation et à Fondamentalement, si vous avez déjà défini votre grammaire formelle et que quelqu'un arrive avec un cas spécial syntaxique-trigraphes, barre oblique inverse-nouvelle ligne, encodage de caractères Unicode arbitraires dans les fichiers source limités à ASCII, quoi qu'il en soit, il est plus facile d'ajouter un passage de transformation avant le tokenizer que de redéfinir le tokenizer pour faire attention à l'endroit où il est logique d'utiliser ce cas particulier.

1 pour les pédants: je suis conscient que cet aspect de C était 100% intentionnel, avec la justification-Je ne l'invente pas - qu'il vous permettrait de forcer mécaniquement le code avec arbitrairement longues lignes sur les cartes perforées. C'était toujours une décision de conception incorrecte.

63
répondu zwol 2015-06-15 17:43:58

C'était un choix de conception intentionnel qui remonte à la conception originale de Java.

À ceux qui demandent " Qui veut que Unicode s'échappe dans les commentaires?", Je présume qu'ils sont des gens dont la langue maternelle utilise les caractères latins. 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.

C'est sans doute une lacune dans les programmes (comme IDE) utilisé pour afficher le texte source que ces programmes ne peuvent pas interpréter les échappements Unicode et afficher le glyphe correspondant.

21
répondu Jonathan Gibbons 2015-07-01 17:42:47

Je suis d'accord avec @zwol que c'est une erreur de conception; mais j'en suis encore plus critique.

\u escape est utile dans les littéraux string et char; et c'est le seul endroit où il devrait exister. Il doit être géré de la même manière que d'autres échappements comme \n; et "\u000A" devrait signifier exactement "\n".

Il est absolument inutile d'avoir \uxxxx dans les commentaires-personne ne peut lire cela.

De même, il ne sert à rien d'utiliser \uxxxx dans une autre partie du programme. La seule l'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 avaient leurs raisons en 1995, mais 20 ans plus tard, cela semble être un mauvais choix.

(question aux lecteurs - pourquoi cette question reçois des nouvelles des votes? cette question Est-elle liée à quelque part populaire?)

21
répondu ZhongYu 2015-07-08 23:19:53

Les seules personnes qui peuvent répondre pourquoi les évasions Unicode ont été implémentées telles quelles 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 comme caractères possibles du code source Java. Cela présente cependant un problème:

  • vous voulez pouvoir utiliser N'importe quel caractère BMP.
  • vous voulez pouvoir entrer n'importe quel charater BMP raisonnablement facile. Une façon de le faire est avec des échappements Unicode.
  • vous voulez pour garder la spécification lexicale facile pour les humains à lire et à écrire, et relativement facile à mettre en œuvre ainsi.

C'est incroyablement difficile lorsque les échappées Unicode entrent dans la mêlée: cela crée toute une charge de nouvelles règles lexer.

La solution facile consiste à faire du lexing en deux étapes: d'abord rechercher et remplacer toutes les échappements Unicode par le caractère qu'il représente, puis analyser le document résultant comme si les échappements Unicode n'existaient pas.

L'avantage est qu'il est facile de spécifiez, donc cela rend la spécification plus simple, et c'est facile à implémenter.

L'inconvénient est, eh bien, votre exemple.

11
répondu Martijn 2015-07-11 15:33:32

Le compilateur traduit non seulement les évasions Unicode dans les caractères qu'ils représentent avant d'analyser un programme en jetons, mais il le fait avant de supprimer les commentaires et les espaces blancs.

Ce programme contient un seul échappement Unicode (\u000d), situé dans son seul commentaire. Comme le commentaire vous le dit, cet échappement représente le caractère de saut de ligne, et le compilateur le traduit dûment avant de supprimer le commentaire .

Cela dépend de la plate-forme. Sur certaines plateformes, comme UNIX, cela fonctionnera; sur D'autres, comme Windows, ce ne sera pas le cas.bien que la sortie puisse être la même à l'œil nu, elle pourrait facilement causer des problèmes si elle était enregistrée dans un fichier ou acheminée vers un autre programme pour un traitement ultérieur.

1
répondu Arp 2018-05-24 11:05:25