Frage Warum ist das Ausführen von Java-Code in Kommentaren mit bestimmten Unicode-Zeichen erlaubt?


Der folgende Code erzeugt die Ausgabe "Hallo Welt!" (Nein wirklich, versuch es).

public static void main(String... args) {

   // The comment below is not a typo.
   // \u000d System.out.println("Hello World!");
}

Der Grund dafür ist, dass der Java-Compiler das Unicode-Zeichen analysiert \u000d als neue Linie und verwandelt sich in:

public static void main(String... args) {

   // The comment below is not a typo.
   //
   System.out.println("Hello World!");
}

Dies führt dazu, dass ein Kommentar "ausgeführt" wird.

Da dies verwendet werden kann, um bösartigen Code "zu verstecken" oder was auch immer ein böser Programmierer sich vorstellen kann, Warum ist es in Kommentaren erlaubt??

Warum ist dies durch die Java-Spezifikation erlaubt?


1247
2018-06-09 09:02


Ursprung


Antworten:


Die Unicode-Decodierung findet vor jeder anderen lexikalischen Übersetzung statt. Der Hauptvorteil davon ist, dass es trivial ist, zwischen ASCII und jeder anderen Kodierung hin und her zu gehen. Sie müssen nicht einmal herausfinden, wo Kommentare beginnen und enden!

Wie angegeben in JLS Abschnitt 3.3 Dies ermöglicht jedem ASCII-basierten Werkzeug, die Quelldateien zu verarbeiten:

[...] Die Java-Programmiersprache legt eine Standardmethode zur Umwandlung eines in Unicode geschriebenen Programms in ASCII fest, die ein Programm in eine Form umwandelt, die von ASCII-basierten Werkzeugen verarbeitet werden kann. [...]

Dies gibt eine grundlegende Garantie für die Plattformunabhängigkeit (Unabhängigkeit von unterstützten Zeichensätzen), die immer ein Hauptziel für die Java-Plattform war.

In der Lage zu sein, ein beliebiges Unicode-Zeichen irgendwo in die Datei schreiben zu können, ist ein nettes Feature und besonders wichtig in Kommentaren, wenn Code in nicht-lateinischen Sprachen dokumentiert wird. Die Tatsache, dass es auf so subtile Weise in die Semantik eingreifen kann, ist nur ein (unglücklicher) Nebeneffekt.

Es gibt viele Fragen zu diesem Thema und Java Puzzler von Joshua Bloch und Neal Gafter enthielt folgende Variante:

Ist das ein legales Java-Programm? Wenn ja, was wird gedruckt?

\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

(Dieses Programm stellt sich als reines "Hello World" Programm heraus.)

In der Lösung des Rätsels weisen sie auf folgendes hin:

Im Ernst, dieses Puzzle dient dazu, die Lehren der vorherigen drei zu verstärken: Unicode-Escapes sind unerlässlich, wenn Sie Zeichen einfügen müssen, die auf andere Weise nicht in Ihrem Programm dargestellt werden können. Vermeide sie in allen anderen Fällen.


Quelle: Java: Code in Kommentaren ausführen ?!


687
2018-06-09 09:13



Da dies noch nicht angesprochen wurde, hier eine Erklärung, warum die Übersetzung von Unicode-Escapes vor jeder anderen Quellcodeverarbeitung geschieht:

Die Idee dahinter war, dass es verlustfreie Übersetzungen von Java-Quellcode zwischen verschiedenen Zeichenkodierungen erlaubt. Heutzutage gibt es weit verbreitete Unicode-Unterstützung, und das sieht nicht nach einem Problem aus, aber damals war es für einen Entwickler aus einem westlichen Land nicht leicht, von seinem asiatischen Kollegen einen Quellcode mit asiatischen Zeichen zu erhalten, Änderungen vorzunehmen ( einschließlich kompilieren und testen) und das Ergebnis zurückschicken, alles ohne etwas zu beschädigen.

So kann Java-Quellcode in jede Kodierung geschrieben werden und erlaubt eine Vielzahl von Zeichen innerhalb von Bezeichnern, Zeichen und StringLiterale und Kommentare. Um sie dann verlustfrei zu übertragen, werden alle Zeichen, die nicht von der Zielcodierung unterstützt werden, durch ihre Unicode-Escapes ersetzt.

Dies ist ein reversibler Prozess und der interessante Punkt ist, dass die Übersetzung von einem Werkzeug durchgeführt werden kann, das nichts über die Java-Quellcodesyntax wissen muss, da die Übersetzungsregel nicht davon abhängig ist. Dies funktioniert, da die Übersetzung in ihre eigentlichen Unicode-Zeichen innerhalb des Compilers unabhängig von der Java-Quellcodesyntax erfolgt. Es bedeutet, dass Sie eine beliebige Anzahl von Übersetzungsschritten in beide Richtungen ausführen können, ohne die Bedeutung des Quellcodes zu ändern.

Dies ist der Grund für eine andere seltsame Eigenschaft, die nicht einmal erwähnt hat: die \uuuuuuxxxx Syntax:

Wenn ein Übersetzungstool Zeichen verlässt und auf eine Sequenz trifft, die bereits eine Escape-Sequenz ist, sollte eine zusätzliche eingefügt werden u in die Sequenz, umwandeln \ucafe zu \uucafe. Die Bedeutung ändert sich nicht, aber beim Konvertieren in die andere Richtung sollte das Werkzeug nur eines entfernen u und ersetzen Sie nur Sequenzen, die einen einzigen enthalten u durch ihre Unicode-Zeichen. Auf diese Weise bleiben selbst Unicode-Escapes beim Konvertieren in die ursprüngliche Form erhalten. Ich denke, niemand hat jemals diese Funktion benutzt ...


132
2018-06-09 17:59



Ich werde den Punkt völlig wirkungslos hinzufügen, nur weil ich mir selbst nicht helfen kann und ich habe noch nicht gesehen, dass die Frage ungültig ist, da sie eine verborgene Prämisse enthält, die falsch ist, nämlich dass der Code drin ist ein Kommentar!

In Java entspricht der Quelltext \ u000d in jeder Hinsicht einem ASCII-CR-Zeichen. Es ist eine klare und einfache Linie, wo immer es auch vorkommt. Die Formatierung in der Frage ist irreführend, was diese Zeichenfolge syntaktisch entspricht:

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

IMHO ist die richtigste Antwort daher: der Code wird ausgeführt, weil er nicht in einem Kommentar steht; es ist in der nächsten Zeile. "Code in Kommentaren ausführen" ist in Java nicht erlaubt, genau wie Sie es erwarten würden.

Ein Großteil der Verwirrung rührt von der Tatsache her, dass Syntax-Highlighter und IDEs nicht ausgereift genug sind, um diese Situation zu berücksichtigen. Entweder verarbeiten sie die Unicode-Escapes überhaupt nicht, oder sie tun es nach dem Parsen des Codes statt vorher, wie javac tut.


97
2018-06-10 17:37



Das \u000d Escape beendet einen Kommentar, weil \u Escapes werden einheitlich in die entsprechenden Unicode-Zeichen konvertiert Vor Das Programm wird in Token umgewandelt. Du könntest es genauso benutzen \u0057\u0057 Anstatt von // zu Start ein Kommentar.

Dies ist ein Fehler in Ihrer IDE, der die Zeile durch Syntax markieren sollte, um klarzustellen, dass der \u000d beendet den Kommentar.

Dies ist auch ein Designfehler in der Sprache. Es kann jetzt nicht korrigiert werden, weil das Programme unterbrechen würde, die davon abhängen. \u Escapezeichen sollten vom Compiler nur in Kontexten konvertiert werden, in denen dies "sinnvoll" ist (Stringliterale und -bezeichner, und wahrscheinlich nirgendwo anders), oder es hätte ihnen verboten werden dürfen, Zeichen im U + 0000-007F-Bereich zu generieren , oder beides. Jede dieser Semantiken hätte verhindert, dass der Kommentar von der \u000dentkommen, ohne die Fälle zu stören \u Fluchten sind nützlich - beachte das beinhaltet Gebrauch von \u Es können Kommentare in einem nicht lateinischen Skript codiert werden, da der Texteditor einen breiteren Überblick über die Position erhalten kann \u Escapes sind bedeutsamer als der Compiler. (Mir ist kein Editor oder eine IDE bekannt, die angezeigt wird \u entkommt als die entsprechenden Zeichen in irgendein Kontext jedoch.)

Es gibt einen ähnlichen Designfehler in der C-Familie,1 wobei Backslash-Newline verarbeitet wird, bevor Kommentargrenzen festgelegt werden, so z.

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

Ich führe dies auf, um zu zeigen, dass es einfach ist, diesen speziellen Designfehler zu machen, und nicht zu erkennen, dass es ein Fehler ist, bis es zu spät ist, um es zu korrigieren, wenn man daran denkt, wie Compiler-Programmierer denken über Tokenization und Parsing. Wenn Sie Ihre formale Grammatik bereits definiert haben und jemand dann einen syntaktischen Spezialfall vorstellt - Trigraphen, Backslash-Zeilenumbruch, Kodierung beliebiger Unicode-Zeichen in auf ASCII beschränkten Quelldateien, was auch immer - muss eingekeilt werden fügen Sie einen Umwandlungsdurchlauf hinzu Vor der Tokenizer, als den Tokenizer neu zu definieren, um darauf zu achten, wo es sinnvoll ist, diesen speziellen Fall zu verwenden.

1 Für Pedanten: Ich bin mir bewusst, dass dieser Aspekt von C zu 100% beabsichtigt war, mit der Begründung - ich entwerfe das nicht -, dass es Ihnen erlauben würde, Code mit beliebig langen Linien mechanisch auf Lochkarten zu drücken. Es war immer noch eine falsche Designentscheidung.


63
2018-06-09 15:16



Dies war eine bewusste Designentscheidung, die bis zum ursprünglichen Design von Java zurückreicht.

Den Leuten, die fragen "Wer will Unicode in Kommentaren?" Nehme ich an, dass es sich um Leute handelt, deren Muttersprache den lateinischen Zeichensatz verwendet. Mit anderen Worten, es ist dem ursprünglichen Design von Java inhärent, dass Leute willkürliche Unicode-Zeichen verwenden können, wo immer sie in einem Java-Programm legal sind, am typischsten in Kommentaren und Strings.

Es ist wohl ein Manko in Programmen (wie IDEs), die verwendet werden, um den Quelltext anzuzeigen, dass solche Programme die Unicode-Escapes nicht interpretieren können und das entsprechende Glyph anzeigen.


21
2018-06-09 18:45



Ich stimme @zwol zu, dass dies ein Konstruktionsfehler ist; aber ich bin noch kritischer.

\u Escape ist nützlich in String- und Char-Literalen; und das ist der einzige Ort, an dem es existieren sollte. Es sollte genauso gehandhabt werden wie andere Escapes \n; und "\u000A"  sollte genau bedeuten "\n".

Es hat absolut keinen Sinn zu haben \uxxxx in Kommentaren - niemand kann das lesen.

Ebenso hat es keinen Sinn zu verwenden \uxxxx in einem anderen Teil des Programms. Die einzige Ausnahme sind wahrscheinlich öffentliche APIs, die gezwungen sind, einige nicht-ASCII-Zeichen zu enthalten - was ist das letzte Mal, dass wir das gesehen haben?

Die Designer hatten ihre Gründe im Jahr 1995, aber 20 Jahre später scheint dies eine falsche Wahl zu sein.

(Frage an die Leser - warum bekommt diese Frage immer neue Stimmen? Ist diese Frage von irgendwo populär?)


21
2018-06-09 16:47



Die einzigen Personen, die die Unicode-Escapes so implementieren können, wie sie waren, sind die Leute, die die Spezifikation geschrieben haben.

Ein plausibler Grund dafür ist, dass der Wunsch bestand, den gesamten BMP als mögliche Zeichen des Java-Quellcodes zuzulassen. Dies stellt jedoch ein Problem dar:

  • Sie möchten ein beliebiges BMP-Zeichen verwenden können.
  • Sie möchten jeden BMP-Charakter relativ einfach eingeben können. Ein Weg, dies zu tun, ist mit Unicode-Escapes.
  • Sie möchten, dass die lexikalische Spezifikation für Menschen einfach zu lesen und zu schreiben ist und relativ einfach zu implementieren ist.

Das ist unglaublich schwierig, wenn Unicode-Escapes in den Kampf eintreten: Es erzeugt eine ganze Ladung neuer Lexer-Regeln.

Der einfache Ausweg besteht darin, in zwei Schritten zu lexieren: Zuerst alle Unicode-Escapes mit dem Zeichen, das sie repräsentiert, suchen und ersetzen und dann das resultierende Dokument analysieren, als ob Unicode-Escapes nicht existieren.

Der Vorteil ist, dass es einfach zu spezifizieren ist, so dass die Spezifikation einfacher und einfacher zu implementieren ist.

Der Nachteil ist, na ja, dein Beispiel.


11
2018-06-12 11:59



Der Compiler übersetzt Unicode-Escapes nicht nur in die Zeichen, die sie repräsentieren, bevor er ein Programm in Tokens analysiert, sondern tut dies auch, bevor Kommentare und Leerräume verworfen werden.

Dieses Programm enthält einen einzelnen Unicode-Escape (\ u000d), der sich in seinem einzigen Kommentar befindet. Wie der Kommentar sagt, stellt dieser Escape das Zeilenvorschubzeichen dar, und der Compiler übersetzt es ordnungsgemäß bevor Sie den Kommentar verwerfen.

Dies ist plattformabhängig. Auf bestimmten Plattformen wie UNIX wird es funktionieren; auf anderen, wie Windows, wird es nicht. Obwohl die Ausgabe mit dem bloßen Auge gleich aussehen mag, könnte es leicht Probleme verursachen, wenn sie in einer Datei gespeichert oder an ein anderes Programm zur weiteren Verarbeitung weitergeleitet würde.


1
2017-11-02 13:01