it-swarm.com.de

Warum darf Java Code in Kommentaren mit bestimmten Unicode-Zeichen ausgeführt werden?

Der folgende Code erzeugt die Ausgabe "Hello World!" (Nein wirklich, probier es aus).

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 Compiler Java das Unicode-Zeichen \u000d als neue Zeile analysiert und in Folgendes umgewandelt wird:

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 "verbergen" oder was auch immer ein böser Programmierer sich vorstellen kann, warum ist es in Kommentaren erlaubt?

Warum ist dies in der Spezifikation Java zulässig?

1315
Reg

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

Wie in JLS-Abschnitt 3. angegeben, können die Quelldateien mit jedem ASCII -basierten Tool verarbeitet werden:

[...] Die Programmiersprache Java gibt eine Standardmethode für die Umwandlung eines in Unicode geschriebenen Programms in ASCII an, mit der ein Programm in eine Form umgewandelt wird, die von ASCII-basierten Tools verarbeitet werden kann . [...]

Dies bietet eine grundlegende Garantie für die Plattformunabhängigkeit (Unabhängigkeit von unterstützten Zeichensätzen), die für die Plattform Java seit jeher ein zentrales Ziel darstellt.

Das Schreiben von Unicode-Zeichen an einer beliebigen Stelle in der Datei ist eine nette Funktion, die insbesondere in Kommentaren wichtig ist, wenn Code in nicht-lateinischen Sprachen dokumentiert wird. Die Tatsache, dass es die Semantik auf solch subtile Weise stören kann, ist nur ein (unglücklicher) Nebeneffekt.

Es gibt viele Fallstricke zu diesem Thema und Java Puzzlers von Joshua Bloch und Neal Gafter enthielten die folgende Variante:

Handelt es sich um 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 einfaches "Hello World" -Programm heraus.)

In der Lösung für das Rätsel weisen sie auf Folgendes hin:

Im Ernst, dieses Rätsel dient dazu, die Lektionen der vorherigen drei zu untermauern: Unicode-Escape-Zeichen sind unerlässlich, wenn Sie Zeichen einfügen möchten, die auf keine andere Weise in Ihrem Programm dargestellt werden können. Vermeiden Sie sie in allen anderen Fällen.


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

724
aioobe

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

Die Idee dahinter war, dass verlustfreie Übersetzungen von Java-Quellcode zwischen verschiedenen Zeichenkodierungen möglich sind. Heutzutage gibt es weit verbreitete Unicode-Unterstützung, und dies scheint kein Problem zu sein, aber damals war es für einen Entwickler aus einem westlichen Land nicht einfach, Quellcode von seinem asiatischen Kollegen mit asiatischen Zeichen zu erhalten. Nehmen Sie einige Änderungen vor ( einschließlich Kompilieren und Testen) und Zurücksenden des Ergebnisses, ohne dabei etwas zu beschädigen.

Daher kann Java Quellcode in einer beliebigen Codierung geschrieben werden und ermöglicht eine breite Palette von Zeichen in Bezeichnern, Zeichen und String Literalen und Kommentaren. Um diese dann verlustfrei zu übertragen, werden alle Zeichen, die von der Zielcodierung nicht unterstützt werden, durch ihre Unicode-Escapezeichen ersetzt.

Dies ist ein reversibler Prozess, und der interessante Punkt ist, dass die Übersetzung mit einem Tool 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 der tatsächlichen Unicode-Zeichen im Compiler auch unabhängig von der Quellcodesyntax von Java erfolgt. Dies 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 weitere seltsame Funktion, die noch nicht einmal erwähnt wurde: die \uuuuuuxxxx -Syntax:

Wenn ein Übersetzungstool Zeichen maskiert und auf eine Sequenz stößt, bei der es sich bereits um eine maskierte Sequenz handelt, sollte es ein zusätzliches u in die Sequenz einfügen und \ucafe in \uucafe konvertieren. Die Bedeutung ändert sich nicht, aber beim Konvertieren in die andere Richtung sollte das Tool nur ein u entfernen und nur Sequenzen, die ein einzelnes u enthalten, durch ihre Unicode-Zeichen ersetzen. Auf diese Weise bleiben auch Unicode-Escapezeichen beim Konvertieren in der ursprünglichen Form erhalten. Ich denke, niemand hat diese Funktion jemals benutzt ...

137
Holger

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

In Java Quellcode\u000d entspricht in jeder Hinsicht einem ASCII CR-Zeichen. Es ist ein Zeilenende, schlicht und einfach, wo immer es auftritt. Die Formatierung in der Frage ist irreführend, was diese Folge von Zeichen tatsächlich syntaktisch entspricht:

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

Meiner Meinung nach lautet die richtigste Antwort daher: Der Code wird ausgeführt, weil er nicht in einem Kommentar enthalten ist. 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 ausgefeilt genug sind, um diese Situation zu berücksichtigen. Entweder verarbeiten sie die Unicode-Escape-Zeichen überhaupt nicht, oder sie analysieren den Code nach dem Parsen, statt zuvor, wie dies javac tut.

102
Pepijn Schmitz

Das Escape-Zeichen \u000d beendet einen Kommentar, da Escape-Zeichen \u einheitlich in die entsprechenden Unicode-Zeichen konvertiert werden bevor das Programm mit einem Token versehen wird. Sie können auch \u0057\u0057 anstelle von // verwenden, um begin einen Kommentar zu schreiben.

Dies ist ein Fehler in Ihrer IDE, der die Zeile syntaktisch hervorheben sollte, um zu verdeutlichen, dass \u000d den Kommentar beendet.

Dies ist auch ein Entwurfsfehler in der Sprache. Es kann jetzt nicht korrigiert werden, da dies Programme stören würde, die davon abhängen. \u Escape-Zeichen sollten entweder vom Compiler nur in Kontexten in das entsprechende Unicode-Zeichen konvertiert werden, in denen dies "sinnvoll" ist (String-Literale und Bezeichner und wahrscheinlich nirgendwo anders), oder es sollte ihnen untersagt worden sein, Zeichen im U zu generieren + 0000–007F Bereich oder beides. Eine dieser Semantiken hätte verhindert, dass der Kommentar durch das Escape \u000d beendet wurde, ohne die Fälle zu beeinträchtigen, in denen Escape \u nützlich sind. Beachten Sie, dass enthält die Verwendung von \u Escapezeichen innerhalb von Kommentaren, um Kommentare in einem nicht-lateinischen Skript zu codieren, da der Texteditor einen umfassenderen Blick darauf werfen könnte, wo \u Escapezeichen von Bedeutung sind als der Compiler. (Mir ist kein Editor oder IDE bekannt, der \u anzeigt, da die entsprechenden Zeichen im any -Kontext jedoch ausgeblendet werden.)

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

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

Ich führe dies auf, um zu veranschaulichen, dass es einfach ist, diesen bestimmten Entwurfsfehler zu machen, und nicht zu erkennen, dass es ein Fehler ist, bis es zu spät ist, ihn zu korrigieren, wenn Sie es gewohnt sind, über Tokenisierung nachzudenken und die Art und Weise zu analysieren, wie Compiler-Programmierer denken über Tokenisierung und Analyse. Grundsätzlich gilt: Wenn Sie Ihre formale Grammatik bereits definiert haben und jemand einen syntaktischen Sonderfall findet - Trigraphs, Backslash-Newline, Codierung beliebiger Unicode-Zeichen in auf ASCII beschränkten Quelldateien, was auch immer -, der eingeklemmt werden muss, ist dies einfacher Fügen Sie einen Transformations-Pass hinzu vor dem Tokenizer, als es ist, den Tokenizer neu zu definieren, um darauf zu achten, wo es sinnvoll ist, diesen Sonderfall zu verwenden.

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

65
zwol

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

Ich nehme an, dass es sich um Personen handelt, deren Muttersprache den lateinischen Zeichensatz verwendet. Mit anderen Worten, es gehört zum ursprünglichen Design von Java, dass Benutzer beliebige Unicode-Zeichen verwenden können, wo immer dies in einem Java - Programm zulässig ist, am häufigsten in Kommentaren und Zeichenfolgen.

Es ist wohl ein Mangel in Programmen (wie IDEs), die zum Anzeigen des Quelltextes verwendet werden, dass solche Programme die Unicode-Escapezeichen nicht interpretieren können, und das entsprechende Symbol anzeigen.

21

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

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

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

Ebenso macht es keinen Sinn, \uxxxx in einem anderen Teil des Programms zu verwenden. Die einzige Ausnahme sind wahrscheinlich öffentliche APIs, die dazu gezwungen werden, 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 erhält diese Frage immer wieder neue Stimmen? Wird diese Frage von einem beliebten Ort aus verlinkt?)

20
ZhongYu

Die einzigen Personen, die antworten können, warum Unicode-Escape-Befehle so implementiert wurden, wie sie waren, sind die Personen, die die Spezifikation geschrieben haben.

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

  • Sie möchten in der Lage sein, ein beliebiges BMP -Zeichen zu verwenden.
  • Sie möchten in der Lage sein, ein BMP Zeichen relativ einfach einzugeben. Ein Weg, dies zu tun, ist mit Unicode-Escapezeichen.
  • Sie möchten, dass die lexikalische Spezifikation für den Menschen leicht lesbar und schreibbar bleibt und auch einigermaßen einfach zu implementieren ist.

Das ist unglaublich schwierig, wenn Unicode aus dem Weg geht: Es erzeugt eine ganze Menge neuer Lexer-Regeln.

Der einfache Ausweg besteht darin, in zwei Schritten zu lexen: Suchen und ersetzen Sie zuerst alle Unicode-Escapezeichen mit dem Zeichen, das sie darstellen, und analysieren Sie dann das resultierende Dokument, als ob Unicode-Escapezeichen nicht vorhanden wären.

Das Beste daran ist, dass es einfach zu spezifizieren ist, so dass es die Spezifikation einfacher macht und einfach zu implementieren ist.

Der Nachteil ist, na ja, Ihr Beispiel.

11
Martijn