Dlaczego wykonywanie kodu Java w komentarzach z pewnymi znakami Unicode jest dozwolone?

Poniższy kod daje wynik " Hello World!"(naprawdę nie, spróbuj).

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

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

Powodem tego jest to, że kompilator Javy przetwarza znak Unicode \u000d jako nową linię i przekształca się w:

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

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

W wyniku czego komentarz jest "wykonywany".

Ponieważ można tego użyć do "ukrycia" złośliwego kodu lub czegokolwiek złego programisty może sobie wyobrazić, dlaczego jest to dozwolone w komentarzach?

Dlaczego jest to dozwolone przez Javę Specyfikacja?

Author: Peter Mortensen, 2015-06-09

8 answers

Dekodowanie Unicode odbywa się przed jakimkolwiek innym tłumaczeniem leksykalnym. Kluczową zaletą tego jest to, że trywialne jest przechodzenie pomiędzy ASCII A dowolnym innym kodowaniem. Nie musisz nawet zastanawiać się, gdzie zaczynają się i kończą komentarze!

Jak podano w JLS sekcja 3.3 pozwala to dowolnemu narzędziu opartemu na ASCII przetwarzać pliki źródłowe:

[...] Język programowania Java określa standardowy sposób przekształcania programu napisanego w Unicode w ASCII to zmienia program w Formularz, który może być przetwarzany przez narzędzia oparte na ASCII. [...]

Daje to podstawową gwarancję niezależności platformy (niezależności obsługiwanych zestawów znaków), która zawsze była kluczowym celem dla platformy Java.

Możliwość zapisu dowolnego znaku Unicode w dowolnym miejscu pliku jest dobrą cechą, szczególnie ważną w komentarzach, podczas dokumentowania kodu w językach innych niż łacińskie. Fakt, że może zakłócać semantykę w takich subtelne sposoby to tylko (niefortunny) efekt uboczny.

Istnieje wiele gotchas na ten temat i Java Puzzlers przez Joshuę Blocha i Neala Gaftera dołączono następujący wariant:

Czy to legalny program Java? Jeśli tak, to co drukuje?

\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

(Ten program okazuje się być zwykłym programem "Hello World".)

W rozwiązaniu zagadki zwracają uwagę, co następuje:

Bardziej poważnie, Ta łamigłówka służy do wzmocnij lekcje z poprzednich trzech: znaki specjalne Unicode są niezbędne, gdy musisz wstawić do programu znaki, których nie można przedstawić w żaden inny sposób. Unikaj ich we wszystkich innych przypadkach.


Source: Java: wykonywanie kodu w komentarzach?!

 692
Author: aioobe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-29 03:56:24

Ponieważ to jeszcze nie zostało rozwiązane, tutaj wyjaśnienie, dlaczego tłumaczenie znaków Unikodu dzieje się przed jakimkolwiek innym przetwarzaniem kodu źródłowego:

Idea była taka, że umożliwia bezstratne tłumaczenie kodu źródłowego Javy pomiędzy różnymi kodowaniami znaków. Obecnie istnieje powszechne wsparcie dla Unicode i nie wygląda to na problem, ale wtedy programista z zachodniego kraju nie był łatwy do otrzymania kodu źródłowego od swojego azjatyckiego kolegi zawierającego Azjatycki znaków, dokonać pewnych zmian (w tym kompilacji i testowania) i wysłać wynik z powrotem, wszystko bez uszkodzenia czegoś.

Tak więc kod źródłowy Javy może być napisany w dowolnym kodowaniu i pozwala na szeroki zakres znaków w identyfikatorach, literałach i komentarzach. Następnie, aby przesłać go bezstratnie, wszystkie znaki nieobsługiwane przez kodowanie docelowe są zastępowane przez ich znaki specjalne Unicode.

Jest to proces odwracalny i interesujące jest to, że tłumaczenie może być wykonane za pomocą narzędzia, które nie musi wiedzieć nic o składni kodu źródłowego Javy, ponieważ reguła tłumaczenia nie jest od niej zależna. Działa to tak, jak tłumaczenie na ich rzeczywiste znaki Unicode wewnątrz kompilatora dzieje się niezależnie od składni kodu źródłowego Java, jak również. Oznacza to, że można wykonać dowolną liczbę kroków tłumaczenia w obu kierunkach bez zmiany znaczenia kodu źródłowego.

To jest powód dla innego dziwnego cecha, o której nawet nie wspomniano: składnia \uuuuuuxxxx:

Gdy narzędzie do tłumaczenia używa znaków specjalnych i natrafia na sekwencję, która jest już sekwencją ucieczki, powinno wstawić dodatkowe u do sekwencji, zamieniając \ucafe na \uucafe. Znaczenie nie zmienia się, ale podczas konwersji w innym kierunku, narzędzie powinno po prostu usunąć jedną u i zastąpić tylko sekwencje zawierające jedną u ich znakami Unicode. W ten sposób zachowywane są nawet ucieczki Unicode w ich pierwotnej formie podczas konwersji tam iz powrotem. Myślę, że nikt nigdy nie używał tej funkcji ...

 133
Author: Holger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-11 15:31:38

Zamierzam całkowicie nieefektywnie dodać punkt, tylko dlatego, że nie mogę się powstrzymać i nie widziałem go jeszcze, że pytanie jest nieważne, ponieważ zawiera ukrytą przesłankę, która jest błędna, a mianowicie, że kod jest w komentarzu!

W Javie kod źródłowy \u000d jest równoważny pod każdym względem znakowi ASCII CR. Jest to zakończenie linii, proste i proste, gdziekolwiek występuje. Formatowanie w pytaniu jest mylące, co ten ciąg znaków faktycznie składniowo odpowiada is:

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

IMHO najbardziej poprawna odpowiedź brzmi: kod wykonuje się, ponieważ nie jest w komentarzu, jest w następnej linii. "Wykonywanie kodu w komentarzach" nie jest dozwolone w Javie, tak jak można się tego spodziewać.

Wiele zamieszania wynika z faktu, że zakreślacze składni i IDE nie są na tyle wyrafinowane, aby uwzględnić tę sytuację. Albo w ogóle nie przetwarzają Unikodu, albo robią to po przetworzeniu kodu zamiast przed, jak javac robi.
 98
Author: Pepijn Schmitz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-10 17:37:17

\u000d escape kończy komentarz, ponieważ \u Escape są równomiernie konwertowane na odpowiednie znaki Unicode przed Program jest tokenizowany. Możesz również użyć \u0057\u0057 zamiast //, Aby rozpocząć komentarz.

Jest to błąd w IDE, który powinien podświetlić linię, aby było jasne, że \u000d kończy komentarz.

Jest to również błąd projektowy w języku. Nie da się tego teraz poprawić, bo to by się złamało programy, które od niej zależą. \u znaki specjalne powinny być albo konwertowane przez kompilator na odpowiedni znak Unicode tylko w kontekstach, w których "ma to sens" (literały łańcuchów i identyfikatory, i prawdopodobnie nigdzie indziej), albo powinny być zakazane generowanie znaków w zakresie U+0000–007F, albo oba te znaki. Każda z tych semantyki uniemożliwiłaby zakończenie komentarza przez \u000d escape, bez ingerowania w przypadki, w których \u Escape jest przydatny-Uwaga że zawiera użycie \u Escape wewnątrz komentarzy jako sposobu kodowania komentarzy w skrypcie nie-łacińskim, ponieważ edytor tekstu może mieć szerszy pogląd na to, gdzie znaki specjalne \u są znaczące niż kompilator. (Nie znam jednak żadnego edytora ani IDE, który wyświetli \u znaki specjalne jako odpowiadające im znaki w w dowolnym kontekście.)

Jest podobny błąd konstrukcyjny w rodzinie C,1 gdzie backslash-newline jest przetwarzany przed komentarzem granice są określone, więc np.

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

Poruszam to, aby zilustrować, że łatwo jest popełnić ten konkretny błąd projektowy, i nie zdaję sobie sprawy, że jest to błąd, dopóki nie jest za późno, aby go poprawić, jeśli jesteś przyzwyczajony do myślenia o tokenizacji i parsowaniu sposobu, w jaki Programiści kompilatorów myślą o tokenizacji i parsowaniu. Zasadniczo, jeśli zdefiniowałeś już swoją gramatykę formalną i wtedy ktoś wymyśli specjalny przypadek składniowy-trygrafy, ukośnik wsteczny-newline, kodowanie dowolnych znaków Unicode w plikach źródłowych ograniczonych do ASCII, cokolwiek-to musi być zaklinowane, łatwiej jest dodać Przejście transformacji przed tokenizerem niż redefiniować tokenizer, aby zwrócić uwagę na to, gdzie sensowne jest użycie tego specjalnego przypadku.

1 do pedantów: zdaję sobie sprawę, że ten aspekt C był w 100% zamierzony, z uzasadnieniem - nie zmyślam - że pozwoliłby mechanicznie dopasować kod do dowolnie długie linie na perforowane karty. Nadal była to błędna decyzja projektowa.

 63
Author: zwol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-15 17:43:58

Był to celowy wybór projektu, który sięga aż do oryginalnego projektu Javy.

Do tych ludzi, którzy pytają " Kto chce Unicode ucieka w komentarzach?", Przypuszczam, że są to ludzie, których język ojczysty używa zestawu znaków łacińskich. Innymi słowy, jest to nieodłączne w oryginalnym projekcie Javy, że ludzie mogą używać dowolnych znaków Unicode wszędzie tam, gdzie są legalne w programie Java, najczęściej w komentarzach i ciągach znaków.

Jest to prawdopodobnie niedociągnięcie w programach (podobnie jak IDE) używane do wyświetlania tekstu źródłowego, który nie może zinterpretować znaków specjalnych Unicode i wyświetla odpowiedni glif.

 21
Author: Jonathan Gibbons,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-01 17:42:47

Zgadzam się z @Zwola, że jest to błąd projektowy, ale jestem jeszcze bardziej krytyczny.

\u escape jest użyteczny w literałach string i char; i to jest jedyne miejsce, w którym powinien istnieć. Powinno być traktowane tak samo jak inne ucieczki jak \n; oraz "\u000A" powinno oznaczać dokładnie "\n".

Nie ma absolutnie sensu mieć \uxxxx w komentarzach-nikt nie może tego przeczytać.

Podobnie, nie ma sensu używać \uxxxx w innej części programu. Jedyny wyjątek jest prawdopodobnie w publicznych API, które są zmuszane do przechowywania znaków innych niż ascii - co ostatnio widzieliśmy?

[6]}projektanci mieli swoje powody w 1995 roku, ale 20 lat później, wydaje się, że to zły wybór.

(pytanie do czytelników-dlaczego to pytanie wciąż zdobywa nowe głosy? czy to pytanie jest powiązane z jakimś popularnym miejscem?)

 21
Author: ZhongYu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-08 23:19:53

Jedynymi osobami, które mogą odpowiedzieć, dlaczego Unicode escapes zostały zaimplementowane w taki sposób, w jaki były, są osoby, które napisały specyfikację.

Prawdopodobnym powodem tego jest chęć dopuszczenia całego BMP jako możliwych znaków kodu źródłowego Javy. To jednak stanowi problem:

  • chcesz móc używać dowolnego znaku BMP.
  • chcesz być w stanie wprowadzić dowolny BMP charater dość łatwo. Sposobem na to jest użycie Unicode escapes.
  • chcesz aby utrzymać leksykalną specyfikację łatwą do odczytu i zapisu dla ludzi, a także rozsądnie łatwą do wdrożenia.

Jest to niezwykle trudne, gdy Unicode ucieka enter the fray: tworzy cały ładunek nowych reguł lexer.

Najprostszym wyjściem jest wykonanie lexingu w dwóch krokach: najpierw Wyszukaj i zamień wszystkie znaki specjalne Unicode na znak, który reprezentuje, a następnie przeanalizuj wynikowy dokument tak, jakby znaki specjalne Unicode nie istniały.

Plusem tego jest to, że łatwo jest określ, dzięki czemu specyfikacja jest prostsza i łatwa do wdrożenia.

Minusem jest, No cóż, twój przykład.

 11
Author: Martijn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-11 15:33:32

Kompilator nie tylko tłumaczy znaki ucieczki Unicode na znaki, które reprezentują, zanim przetworzy program na tokeny, ale robi to przed odrzuceniem komentarzy i białych spacji.

Ten program zawiera pojedynczy Unicode escape (\u000d), znajdujący się w jego jedynym komentarzu. Jak mówi komentarz, ta ucieczka reprezentuje znak linefeed, a kompilator odpowiednio ją tłumaczy przed odrzuceniem komentarza .

Jest to zależne od platformy. Na niektórych platformach, na przykład UNIX, to będzie działać; na innych, takich jak Windows, To nie będzie. chociaż wyjście może wyglądać tak samo gołym okiem, to może łatwo spowodować problemy, jeśli zostanie zapisane w pliku lub rurociągu do innego programu do późniejszego przetwarzania.

 1
Author: Arp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-24 11:05:25