Question 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-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!");
}

Ainsi résultant en un commentaire étant "exécuté".

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

Pourquoi cela est-il autorisé par la spécification Java?


1247
2018-06-09 09:02


origine


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 se terminent!

Comme indiqué dans JLS Section 3.3 Cela permet à n'importe quel 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 qui transforme un programme en un formulaire pouvant être traité par des outils ASCII. [...]

Cela donne une garantie fondamentale pour l'indépendance de la plate-forme (indépendance des jeux de caractères supportés) qui a toujours été un objectif clé pour la plate-forme Java.

Etre capable d'écrire n'importe quel caractère Unicode n'importe où dans le fichier est une caractéristique 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 y a beaucoup de pièges sur ce thème et Java Puzzlers par Joshua Bloch et Neal Gafter inclus la variante suivante:

Est-ce un programme Java légal? Si oui, qu'imprime-t-il?

\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 se révèle être un programme "Hello World".)

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

Plus sérieusement, ce puzzle sert à renforcer les leçons des trois précédents: Les échappements Unicode sont essentiels lorsque vous devez insérer des caractères qui ne peuvent être représentés d'aucune autre manière dans votre programme. Evitez les dans tous les autres cas.


La source: Java: Exécuter du code dans les commentaires ?!


687
2018-06-09 09:13



Comme 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 sous-jacente était qu'elle 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 être 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 des caractères asiatiques. y compris compiler et tester) et renvoyer le 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 Stringlittéraux et commentaires. Ensuite, afin de le transférer sans perte, tous les caractères non supportés par le codage 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 faite 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 directions sans jamais changer la signification du code source.

C'est la raison d'une autre caractéristique étrange qui n'a même pas mentionné: le \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 une séquence supplémentaire u dans la séquence, la conversion \ucafe à \uucafe. La signification ne change pas, mais lors de la conversion dans l'autre direction, l'outil doit simplement en retirer un u et remplacez uniquement les séquences contenant un seul u 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 d'avant en arrière. Je suppose, personne n'a jamais utilisé cette fonctionnalité ...


132
2018-06-09 17:59



Je vais ajouter tout à fait inefficacement le point, juste parce que je ne peux pas m'en empêcher et 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 en un commentaire!

En Java, le code source \ u000d est équivalent en tout point à un caractère CR ASCII. C'est une fin de ligne, pure et simple, partout où elle se produit. Le formatage dans la question est trompeur, à quoi correspond cette séquence de caractères syntaxiquement:

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

À 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 assez sophistiqués pour prendre en compte cette situation. Ils ne traitent pas du tout les échappements Unicode, ou ils le font après l'analyse du code au lieu d'avant, comme javac Est-ce que.


97
2018-06-10 17:37



le \u000d escape termine un commentaire parce que \u les échappements sont uniformément convertis en caractères Unicode correspondants avant le programme est tokenized. Vous pouvez également utiliser \u0057\u0057 au lieu de // à commencer un commentaire.

Ceci est un bug dans votre EDI, qui devrait mettre en évidence la syntaxe de 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, car cela casserait les programmes qui en dépendent. \u les échappements devraient être convertis en caractères Unicode correspondants par le compilateur uniquement dans les contextes où cela "a du sens" (littéraux et identificateurs de chaînes, et probablement nulle part ailleurs) ou il 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 le \u000déchapper, sans interférer avec les cas où \u les évasions sont utiles - notez que inclut utilisation de \u échappe à l'intérieur des commentaires comme un moyen d'encoder les commentaires dans un script non-latin, parce que l'éditeur de texte pourrait avoir une vue plus large de l'endroit où \u Les échappements sont significatifs que le compilateur. (Je ne suis au courant d'aucun éditeur ou IDE qui affichera \u s'échappe comme les 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 que les limites de commentaires ne soient déterminées, par ex.

// 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 d'analyser la façon dont les programmeurs à propos de la tokenisation et de l'analyse syntaxique. Fondamentalement, si vous avez déjà défini votre grammaire formelle et que quelqu'un arrive avec un cas particulier de syntaxe - trigraphs, backslash-newline, encodant des caractères Unicode arbitraires dans des fichiers sources limités à ASCII, quel que soit le cas - il est plus facile de ajouter une passe de transformation avant le tokenizer que de redéfinir le tokenizer pour prêter attention à 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 logique - je ne le fais pas - de vous permettre de forcer mécaniquement le code avec des lignes arbitrairement longues sur des cartes perforées. C'était toujours une décision de conception incorrecte.


63
2018-06-09 15:16



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

Pour ceux qui demandent "qui veut des escapades Unicode dans les commentaires?", Je suppose qu'ils sont des gens dont la langue maternelle utilise le jeu de 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 légalement dans un programme Java, le plus souvent dans les commentaires et les chaînes.

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


21
2018-06-09 18:45



Je suis d'accord avec @zwol que c'est une erreur de conception; mais je suis encore plus critique à ce sujet.

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

Il n'y a absolument aucun intérêt à avoir \uxxxx dans les commentaires - personne ne peut lire ça.

De même, il ne sert à rien d'utiliser \uxxxx dans une autre partie du programme. La seule exception est probablement dans les API publiques qui sont forcées 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 d'obtenir de nouveaux votes? Cette question est-elle liée à quelque part populaire?)


21
2018-06-09 16:47



Les seules personnes qui peuvent répondre à la question de savoir pourquoi les échappements Unicode ont été implémentés tels qu'ils étaient sont celles qui ont écrit la spécification.

Une raison plausible pour cela est qu'il y avait le désir d'autoriser l'intégralité du BMP comme caractères possibles du code source Java. Cela présente un problème cependant:

  • Vous voulez pouvoir utiliser n'importe quel caractère BMP.
  • Vous voulez être en mesure de saisir n'importe quel charateur BMP raisonnablement facile. Une façon de le faire est avec les échappements Unicode.
  • Vous voulez que la spécification lexicale soit facile à lire et à écrire pour les humains et qu'elle soit également facile à mettre en œuvre.

C'est incroyablement difficile quand les échappements Unicode entrent dans la mêlée: cela crée tout un tas de nouvelles règles lexer.

La solution la plus simple consiste à effectuer lex en deux étapes: d'abord, recherchez et remplacez toutes les échappées 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 de cela est qu'il est facile à spécifier, ce qui rend la spécification plus simple et facile à mettre en œuvre.

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


11
2018-06-12 11:59