Почему разрешено выполнение Java-кода в комментариях с определенными символами Юникода

java unicode comments


Следующий код производит вывод "Hello World!".(на самом деле нет,попробуй).

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

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

Причина в том, что компилятор Java анализирует символ Unicode \u000d как новую строку и преобразуется в:

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

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

В результате комментарий становится "выполненным".

Поскольку это можно использовать для «сокрытия» вредоносного кода или всего, что может представить злой программист, почему это разрешено в комментариях ?

Почему это разрешено спецификацией Java?




Answer 1 aioobe


Декодирование Юникода происходит перед любым другим лексическим переводом.Ключевым преимуществом этого является то,что он делает тривиальным перемещение между ASCII и любой другой кодировкой.Вам даже не нужно выяснять,где начинаются и заканчиваются комментарии!

Как указано в разделе 3.3 JLS, это позволяет любому инструменту на основе ASCII обрабатывать исходные файлы:

[...] The Java programming language specifies a standard way of transforming a program written in Unicode into ASCII that changes a program into a form that can be processed by ASCII-based tools. [...]

Это дает фундаментальную гарантию независимости платформы (независимость поддерживаемых наборов символов),которая всегда была ключевой целью для платформы Java.

Возможность написания любого символа Юникода где угодно в файле-это аккуратная особенность,и особенно важно в комментариях,при документировании кода на нелатинских языках.Тот факт,что он может таким тонким образом вмешиваться в семантику,является всего лишь (досадным)побочным эффектом.

Есть много ошибок на эту тему, и Java Puzzlers Джошуа Блоха и Нила Гафтера включили следующий вариант:

Это легальная Java-программа? Если да,то что она печатает?

\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

(Эта программа оказывается простой программой "Hello World".)

В решении головоломки они указывают на следующее:

Если серьезно, то эта головоломка подкрепляет уроки предыдущих трех: экранирование Unicode необходимо, когда вам нужно вставить символы, которые не могут быть представлены каким-либо другим способом в вашей программе. Избегайте их во всех других случаях.


Источник: Java: Выполнение кода в комментариях ?!




Answer 2 Holger


Поскольку это еще не решено, здесь поясняется, почему перевод экранирования Unicode происходит перед любой другой обработкой исходного кода:

Идея заключалась в том, что он позволяет переводить исходный код Java без потерь между различными кодировками символов. Сегодня широко распространена поддержка Unicode, и это не выглядит проблемой, но тогда разработчику из западной страны было нелегко получить исходный код от своего азиатского коллеги, содержащий азиатские символы, внести некоторые изменения ( включая компиляцию и тестирование) и отправку результата обратно, все без ущерба для чего-либо.

Таким образом, исходный код Java может быть написан в любой кодировке и позволяет использовать широкий диапазон символов в идентификаторах, символьных и String литералах и комментариях. Затем, чтобы передать его без потерь, все символы, не поддерживаемые целевой кодировкой, заменяются их экранированием Unicode.

Это обратимый процесс, и интересным моментом является то, что перевод может быть выполнен инструментом, которому не нужно ничего знать о синтаксисе исходного кода Java, поскольку правило перевода не зависит от него. Это работает, поскольку перевод их фактических символов Unicode внутри компилятора происходит независимо от синтаксиса исходного кода Java. Это означает, что вы можете выполнить произвольное количество шагов перевода в обоих направлениях, не меняя смысла исходного кода.

Это причина другой странной функции, о которой даже не упоминалось: синтаксис \uuuuuuxxxx :

Когда инструмент перевода экранирует символы и встречает последовательность, которая уже является экранированной последовательностью, он должен вставить дополнительный u в последовательность, преобразовав \ucafe в \uucafe . Значение не меняется, но при преобразовании в другом направлении инструмент должен просто удалить один u и заменить только последовательности, содержащие один u , их символами Unicode. Таким образом, даже экранированные символы Юникода сохраняются в своем первоначальном виде при конвертации назад и вперед. Я думаю, никто никогда не использовал эту функцию ...




Answer 3 Pepijn Schmitz


Я собираюсь совершенно безрезультатно добавить,что вопрос недействителен,так как содержит скрытую предпосылку,которая ошибочна,а именно то,что код находится в комментарии!

В исходном коде Java \u000d во всех отношениях эквивалентен символу ASCII CR.Это окончание строки,простое и понятное,где бы оно ни происходило.Форматирование в вопросе вводит в заблуждение,чему на самом деле синтаксически соответствует эта последовательность символов:

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

Таким образом,наиболее правильный ответ-IMHO:код выполняется,потому что его нет в комментарии,он находится в следующей строке."Выполнение кода в комментариях" не разрешено на Java,как и следовало ожидать.

Большая часть путаницы проистекает из того факта, что подсветки синтаксиса и IDE не достаточно сложны, чтобы принять во внимание эту ситуацию. Они либо вообще не обрабатывают экранирование Юникода, либо делают это после анализа кода, а не до того, как это делает javac .




Answer 4 zwol


\u000d побег заканчивается комментарий , потому что \u побеги равномерно преобразуются в соответствующие символы Unicode , прежде чем программа лексемы. Вы можете в равной степени использовать \u0057\u0057 вместо // , чтобы начать комментарий.

Это ошибка в вашей IDE, которая должна синтаксически выделять строку, чтобы было ясно, что \u000d заканчивает комментарий.

Это также ошибка дизайна в языке. Это не может быть исправлено сейчас, потому что это сломало бы программы, которые зависят от него. \u escapes должны быть либо преобразованы компилятором в соответствующий символ Unicode только в тех контекстах, где это «имеет смысл» (строковые литералы и идентификаторы, и, вероятно, нигде больше), либо им должно быть запрещено генерировать символы в U + 0000– Диапазон 007F или оба. Любая из этих семантик помешала бы завершению комментария при помощи \u000d экранирования, не затрагивая случаи, когда \u экранирование полезно - обратите внимание, что это включает использование \u экранирование внутри комментариев как способ кодирования комментариев в нелатинском скрипте, потому что текстовый редактор может иметь более широкое представление о том, где \u экранирования значительнее, чем компилятор. (Я не знаю ни одного редактора или IDE, которые будут отображать экранированные символы \u как соответствующие символы в любом контексте.)

Существует аналогичная ошибка проектирования в семействе C, 1 когда обратная косая черта обрабатывается до определения границ комментариев, например,

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

Я привожу это, чтобы проиллюстрировать, что бывает легко совершить эту конкретную ошибку проектирования, и не понимаю, что это ошибка, пока не стало слишком поздно ее исправлять, если вы привыкли думать о токенизации и разбирать способ, которым думают программисты компилятора о токенизации и разборе. По сути, если вы уже определили свою формальную грамматику, а затем кто-то придумает синтаксический особый случай - триграфы, обратную косую черту, кодирование произвольных символов Unicode в исходных файлах, ограниченных ASCII, что угодно, - что нужно вставить в код, проще добавить проход преобразования перед токенизатором, чем переопределить токенизатор, чтобы обратить внимание на то, где имеет смысл использовать этот особый случай.

1 Для педантов: я знаю, что этот аспект C был на 100% преднамеренным, с обоснованием - я не придумываю это - что это позволило бы вам механически втиснуть код произвольно длинными линиями в перфокарты. Это было все еще неправильное дизайнерское решение.




Answer 5 Jonathan Gibbons


Это был преднамеренный выбор дизайна,который восходит к оригинальному дизайну Java.

Для тех,кто спрашивает "кто хочет,чтобы Unicode сбежал в комментариях?",я полагаю,что это люди,чей родной язык использует набор латинских символов.Другими словами,оригинальному дизайну Java присущ тот факт,что люди могут использовать произвольные символы Юникода везде,где это разрешено в программах на Java,чаще всего в комментариях и строках.

Вероятно,в программах (например,IDE),используемых для просмотра исходного текста,существует недостаток,заключающийся в том,что такие программы не могут интерпретировать экранирования Юникода и выводить на экран соответствующий глиф.