Где я могу найти список экранированных символов в строковых константах MSIL?

Я написал программу (на C#), которая читает и обрабатывает программы MSIL, созданные из программ C#. Я ошибочно предположил, что правила синтаксиса для строковых констант MSIL такие же, как и для C#, но потом столкнулся со следующей ситуацией:

Этот оператор С#

string s = "Do you wish to send anyway?";

компилируется (среди других операторов MSIL) в этот

IL_0128:  ldstr      "Do you wish to send anyway\?"

Я не ожидал обратной косой черты, которая используется, чтобы избежать вопросительного знака. Теперь я, очевидно, могу учитывать эту обратную косую черту как часть моей обработки, но в основном из любопытства я хотел бы знать, есть ли где-нибудь список символов, которые экранируются, когда компилятор C# преобразует строки констант C# в строки констант MSIL.

Спасибо.


person RenniePet    schedule 02.02.2012    source источник
comment
Почему вы работаете со строковым представлением CIL? Или экранирование присутствует и в бинарнике?   -  person svick    schedule 02.02.2012
comment
@svick: я написал свою собственную программу запутывания, которая запускает ILDAsm.exe, манипулирует исходным кодом MSIL, а затем запускает ILAsm.exe. Я никогда не работал с двоичным файлом MSIL, но я бы предположил, что там нет побега, это действительно не имеет смысла.   -  person RenniePet    schedule 02.02.2012
comment
Я бы подумал, что работать напрямую с двоичным файлом было бы проще. Для этого можно использовать библиотеку Mono Cecil.   -  person svick    schedule 02.02.2012
comment
@RenniePet - К вашему сведению, я действительно ответил на вопрос сейчас.   -  person Joshua Honig    schedule 02.02.2012


Ответы (1)


Обновить

На основе экспериментов с использованием компилятора С# + ildasm.exe: возможно, причина отсутствия списка экранированных символов в том, что их так мало: ровно 6.

Исходя из IL, сгенерированного ildasm, из программ C#, скомпилированных Visual Studio 2010:

  • IL - это строго ASCII.
  • Three traditional whitespace characters are escaped
    • \t : 0x09 : (tab)
    • \n : 0x0A : (новая строка)
    • \r : 0x0D : (возврат каретки)
  • Three punctuation characters are escaped:
    • \" : 0x22 : (double quote)
    • \? : 0x3F : (вопросительный знак)
    • \\ : 0x5C : (обратная косая черта)
  • Только следующие символы включены без изменений в литеральные строки 0x20 - 0x7E (не включая три знака пунктуации)
  • Все остальные символы, включая управляющие символы ASCII ниже 0x20 и все, начиная с 0x7F и выше, преобразуются в байтовые массивы. Точнее, любая строка, содержащая любой символ, отличный от 92 литеральных и 6 экранированных символов, указанных выше, преобразуется в массив байтов, где байты представляют собой байты с прямым порядком байтов строки UTF-16.

Пример 1: ASCII выше 0x7E: простая буква é с ударением (U+00E9)

C#: становится либо "é", либо "\u00E9" (байт E9 идет первым)

ldstr      bytearray (E9 00 )

Пример 2: UTF-16: символ суммирования ∑ (U+2211)

C#: становится либо "∑", либо "\u2211" (байт 11 идет первым)

ldstr      bytearray (11 22 )

Пример 3: UTF-32: математический с двойным ударением ???? (U +1D538)

C#: либо "????", либо суррогатная пара UTF-16 "\uD835\uDD38" становится (байты внутри char перевернуты, но двухбайтовые символы в общем порядке)

ldstr      bytearray (35 D8 38 DD )

Пример 4. Преобразование байтового массива для всей строки, содержащей не-Ascii-символ

С#: "In the last decade, the German word \"über\" has come to be used frequently in colloquial English." становится

ldstr      bytearray (49 00 6E 00 20 00 74 00 68 00 65 00 20 00 6C 00  
                      61 00 73 00 74 00 20 00 64 00 65 00 63 00 61 00  
                      64 00 65 00 2C 00 20 00 74 00 68 00 65 00 20 00  
                      47 00 65 00 72 00 6D 00 61 00 6E 00 20 00 77 00  
                      6F 00 72 00 64 00 20 00 22 00 FC 00 62 00 65 00  
                      72 00 22 00 20 00 68 00 61 00 73 00 20 00 63 00  
                      6F 00 6D 00 65 00 20 00 74 00 6F 00 20 00 62 00  
                      65 00 20 00 75 00 73 00 65 00 64 00 20 00 66 00  
                      72 00 65 00 71 00 75 00 65 00 6E 00 74 00 6C 00  
                      79 00 20 00 69 00 6E 00 20 00 63 00 6F 00 6C 00  
                      6C 00 6F 00 71 00 75 00 69 00 61 00 6C 00 20 00  
                      45 00 6E 00 67 00 6C 00 69 00 73 00 68 00 2E 00 )

Напрямую, «вы не можете» (найдите список экранированных строк MSIL), но вот несколько полезных советов...

ECMA-335, содержащий строгое определение CIL, не не указывает, какие символы должны быть экранированы в литералах QSTRING, только то, что они могут экранироваться с помощью символа обратной косой черты \. Наиболее важные примечания:

  • Литералы Unicode представлены как восьмеричные, а не шестнадцатеричные (т. е. \042, а не \u0022).
  • Строки могут быть распределены по нескольким строкам с помощью символа \ — см. ниже.

Единственные явно упомянутые escape-последовательности — это табуляция \t, перевод строки \n и восьмеричные числовые escape-последовательности. Это немного раздражает для вас, поскольку в C# нет восьмеричного литерала — вам придется выполнять собственное извлечение и преобразование, например, с помощью метода Convert.ToInt32([string], 8).

Кроме того, выбор побегов зависит от реализации «гипотетического ассемблера IL», описанного в спецификации. Таким образом, ваш вопрос справедливо касается правил для MSIL, которые являются строгой реализацией Microsoft CIL. Насколько я могу судить, М.С. не задокументировала свой выбор способов побега. Было бы полезно, по крайней мере, спросить людей из Mono, что они используют. Помимо этого, это может быть вопрос создания списка самостоятельно - создайте программу, которая объявляет строковый литерал для каждого символа \u0000 - что угодно, и посмотрите, что представляют собой скомпилированные операторы ldstr. Если я доберусь до этого первым, я обязательно опубликую свои результаты.

Дополнительные примечания:

Чтобы правильно анализировать строковые литералы *IL, известные как QSTRINGS или SQSTRINGS, вам придется учитывать не только экранирование символов. Возьмите, например, конкатенацию строк в коде (и это дословно из Раздела II:: 5.2):

Оператор «+» может использоваться для объединения строковых литералов. Таким образом, длинную строку можно разбить на несколько строк, используя «+» и новую строку в каждой строке. Альтернативой является использование «\» в качестве последнего символа в строке, и в этом случае этот символ и следующий за ним разрыв строки не вводятся в сгенерированную строку. Любые пробельные символы (пробел, перевод строки, возврат каретки и табуляция) между «\» и первым непробельным символом на следующей строке игнорируются. [Примечание: чтобы включить символ двойной кавычки в QSTRING, используйте восьмеричную управляющую последовательность. конечная нота]

Пример. В результате получаются строки, эквивалентные «Hello World from CIL!»:

ldstr "Hello " + "World " + "from CIL!"

ldstr "Hello World\ 
       \040from CIL!"
person Joshua Honig    schedule 02.02.2012
comment
Однако это, похоже, не отвечает на вопрос о побегах. - person svick; 02.02.2012
comment
Спасибо за Ваш ответ. Я ознакомился с документом ECMA, и он очень интересен. Но я не вижу там ничего, что требует или даже позволяет экранировать символ вопросительного знака. Думаю, мне следует просто принять это как причуду интерпретации/реализации Microsoft и перестать беспокоиться об этом. - person RenniePet; 02.02.2012
comment
@svick Ты прав, я знаю. Он просто казался слишком большим для простого старого комментария. Я не ожидаю проверки ответа на вопрос, но я считаю, что она по-прежнему имеет прямое отношение и полезна для проблемы ОП (и для будущих читателей) - person Joshua Honig; 02.02.2012
comment
@RenniePet Я только что внес несколько серьезных изменений. Все еще не ответ как таковой, но, надеюсь, полезный. - person Joshua Honig; 02.02.2012