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

1215

Следующий код выводит результат "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?

  • 44
    «Почему это разрешено» мне кажется слишком основанным на мнении. Языковые дизайнеры приняли решение, что еще нужно знать? Если вы не найдете утверждение человека, принимающего такое решение, мы можем только строить догадки.
  • 3
    Хотя это и странно, я не вижу в этом реальной проблемы. Обычные пользователи не будут знать разницу между кодом, скрытым в комментарии, и обычным кодом, поэтому для них это не имеет значения. Тогда это может быть член команды, скрывающий код от других участников, но разработчики среагируют, увидев странный комментарий, подобный этому, и либо удалят его, либо исследуют. Если это будет сделано и введено в действие, VCS скажет вам, кто это сделал, так что вас поймают.
Показать ещё 35 комментариев
Теги:
comments
unicode

8 ответов

704
Лучший ответ

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

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

[...] Язык программирования Java определяет стандартный способ преобразования программы, написанной в Unicode, в ASCII, которая изменяет программу на форму, которая может обрабатываться инструментами на основе ASCII. [...]

Это дает фундаментальную гарантию независимости платформы (независимость поддерживаемых наборов символов), которая всегда была ключевой целью платформы 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).

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

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


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

  • 83
    Короче говоря, Java намеренно позволяет это: «ошибка» в IDE OP?
  • 59
    @Bathsheba: это больше в головах людей. Люди не пытаются понять, как работает синтаксический анализ Java, поэтому IDE иногда отображают код неправильно. В приведенном выше примере комментарий должен заканчиваться на \u000d а часть после него должна иметь подсветку кода.
Показать ещё 19 комментариев
130

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

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

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

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

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

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

  • 1
    Интересно, что native2ascii , native2ascii , не использует синтаксис \uu...xxxx ,
  • 5
    Да, native2ascii предназначался, чтобы помочь подготовить пакеты ресурсов, преобразовав их в iso-latin-1, так как Properties.load был исправлен для чтения только latin-1. И там, правила не отличаются, нет \uuu… синтаксис и не на ранней стадии обработки. В файлах property=multi\u000aline действительно совпадает с property=multi\nline . (В отличие от фразы «использование экранирования Unicode, как определено в разделе 3.3 спецификации языка Java ™» документации)
Показать ещё 3 комментария
92

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

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

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

ИМХО самый правильный ответ: код выполняется потому, что он не находится в комментарии; это на следующей строке. "Выполнение кода в комментариях" не разрешено на Java, как и следовало ожидать.

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

  • 5
    Я согласен, это не Java-ошибка проектирования, но это ошибка IDE.
  • 2
    Вопрос скорее в том, почему код, который выглядит как комментарий для человека, не знакомого с этим конкретным аспектом языка и, возможно, без ссылки на подсветку синтаксиса, на самом деле не является комментарием. Возражение на основании предпосылки вопроса является недействительным.
Показать ещё 2 комментария
62

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

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

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

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

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

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

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

  • 17
    Я бы не сказал, что это ошибка проектирования. Я мог бы согласиться с вами, что это был неудачный выбор дизайна или выбор с неблагоприятными последствиями, но я все еще думаю, что он работает так, как задумывалось дизайнерами языка: он позволяет вам использовать любой символ юникода в любом месте файла, сохраняя при этом кодировку ASCII файла.
  • 0
    Я думаю, что если бы обоснование было таким, как указано, то обратный слеш, за которым следовал какой-то конкретный другой символ (например ! ), Должен был указывать, что оставшаяся часть физической строки должна игнорироваться, а первый символ следующей строки следует рассматривать как прямой после символа перед обратной косой чертой. Это позволило бы \! пробивается в столбцах 71-72, оставляя восемь столбцов доступными для порядковых номеров. В некоторых контекстах трюк с маркерной полосой может уменьшить потребность в машиночитаемых числах, но я не думаю, что это устранит его.
Показать ещё 10 комментариев
22

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

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

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

  • 7
    В настоящее время мы используем UTF-8 для нашего исходного кода и можем использовать символы Unicode напрямую, без необходимости экранирования.
22

Я согласен с @zwol, что это ошибка дизайна; но я даже более критично отношусь к нему.

\u escape полезен в строках и char литералах; и что единственное место, в котором оно должно существовать. Его следует обрабатывать так же, как и другие escape-последовательности, такие как \n; и "\u000A" должно означать точно "\n".

Нет абсолютно никакого смысла иметь \uxxxx в комментариях - никто не может это прочитать.

Точно так же нет смысла использовать \uxxxx в другой части программы. Единственное исключение, вероятно, в общедоступных API, которые принудительно содержат некоторые символы не-ascii - что в последний раз мы видели?

У дизайнеров были свои причины в 1995 году, но через 20 лет это кажется неправильным выбором.

(вопрос читателям - почему этот вопрос продолжает получать новые голоса? этот вопрос связан с чем-то популярным?)

  • 5
    Я предполагаю, что вы не зависаете, где не API-символы используются в API. Есть люди, использующие его (не я), например, в азиатских странах. И когда вы используете не идентификаторы ASCII в идентификаторах, запрещать их в комментариях к документации не имеет большого смысла. Тем не менее, позволить им внутри токена и позволить им изменить значение или границу токена - это разные вещи.
  • 15
    они могут использовать правильную кодировку файлов. зачем писать int \u5431 когда вы можете сделать int 整
Показать ещё 16 комментариев
11

Единственные люди, которые могут ответить, почему экраны Unicode были реализованы, так как они были людьми, которые написали спецификацию.

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

  • Вы хотите иметь возможность использовать любой символ BMP.
  • Вы хотите иметь возможность вводить любой BMP charater достаточно легко. Способ сделать это - с экранами Unicode.
  • Вы хотите, чтобы лексическая спецификация была легкой для людей, чтобы читать и писать, а также разумно легко реализовать.

Это невероятно сложно, когда Unicode выйдет из игры: он создает целый набор новых правил лексера.

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

Поверхность этого заключается в том, что ее легко указать, поэтому упрощает ее спецификацию и ее легко реализовать.

Недостатком является, ну, ваш пример.

  • 2
    Или ограничьте использование \ uxxxx идентификаторами, строковыми литералами и символьными константами. Именно это и делает С11.
  • 0
    это действительно усложняет правила синтаксического анализа, потому что именно они определяют эти вещи, и я полагаю, что это одна из причин того, что это так.
1

Компилятор не только переводит escape-последовательности Unicode в символы, которые они представляют, прежде чем он анализирует программу в токенах, но делает это до удаления комментариев и пробелов.

Эта программа содержит единственный Unicode escape (\ u000d), расположенный в единственном комментарии. Как говорится в комментарии, этот escape представляет символ перевода строки, а компилятор переводит его перед , отбрасывая комментарий.

Это зависит от платформы. В некоторых формах плат, таких как UNIX, он будет работать на других, таких как Windows, это не будет. Хотя результат может выглядеть невооруженным глазом, он может легко вызвать проблемы, если он был сохранен в файле или передан в другую программу для последующей обработки.

  • 3
    Как бы ни был красноречив ваш «ответ», на самом деле это вовсе не ответ. Вопрос ОП был «Почему это разрешено?», Но здесь объясняется, как это работает ... какой ОП уже предоставил.
  • 3
    Есть ли у вас источники, чтобы подтвердить, что это зависит от платформы? Если это правда, я бы посчитал, что Java полностью сломана (я так или иначе делаю, это просто еще один гвоздь в гробу).
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню