Подтвердить что ты не робот

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

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

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

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

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

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

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

Спасибо.

4b9b3361

Ответ 1

Обновление

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

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

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

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

С#: либо "é", либо "\u00E9" становится (сначала байт E9)

ldstr      bytearray (E9 00 )

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

С#: либо "∑", либо "\u2211" становится (сначала байт 11)

ldstr      bytearray (11 22 )

Пример 3: UTF-32: Двухуровневая математика 𝔸 (U + 1D538)

С#: Либо "𝔸", либо суррогатная пара 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).
  • Строки могут быть распределены по нескольким строкам с использованием символа \ - см. ниже

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

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

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

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

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

Пример: Следующий результат в строках, эквивалентных "Hello World from CIL!":

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

ldstr "Hello World\ 
       \040from CIL!"