CLR IL-значение квадратных скобок в .locals init

Я пытаюсь создать динамическую сборку, используя Reflection & Emit в .NET. Я получаю сообщение об ошибке «Common Language Runtime обнаружил недопустимую программу». Я создал еще одну программу, которая имеет нужные мне функции, используя жестко запрограммированные типы. Функциональность, которую я пытаюсь написать, в конечном итоге будет использовать динамические типы, но я могу использовать ILDasm, чтобы увидеть IL, который мне нужно сгенерировать. Я сравниваю IL, который я генерирую, с IL, который генерирует компилятор. В объявлении инициализации .locals одного метода я вижу дополнительный элемент в коде, сгенерированном компилятором,

сгенерированный компилятором:

.locals init ([0] class [System.Core]System.Linq.Expressions.ParameterExpression CS$0$0000,
           [1] class [System.Core]System.Linq.Expressions.ParameterExpression[] CS$0$0001)

мой:

.locals init (class [System.Core]System.Linq.Expressions.ParameterExpression V_0,  
       class [System.Core]System.Linq.Expressions.ParameterExpression[] V_1)

Я не понимаю значения «[0]» и «[1]» в коде, сгенерированном компилятором. Кто-нибудь может сказать мне, что это значит?

Что касается более общего вопроса, я могу без особых проблем следить за большинством выводов ILDasm. Но время от времени я сталкиваюсь с проблемным выражением. Например, в этой строке из ILDasm

callvirt   instance class [EntityFramework]System.Data.Entity.ModelConfiguration.EntityTypeConfiguration`1<!!0> [EntityFramework]System.Data.Entity.DbModelBuilder::Entity<class DynamicEdmxTrial.HardFooAsset>()

«!!0», вероятно, относится к общему типу Entity‹>, но я не знаю наверняка, и мне интересно, есть ли ключ к выводу ILDasm, который объяснил бы мне его более неясный вывод.


person jrv    schedule 23.09.2013    source источник
comment
«Я получаю сообщение об ошибке, Common Language Runtime обнаружил недопустимую программу». Вы пытались запустить PEVerify на своей сборке?   -  person svick    schedule 23.09.2013
comment
Кроме того, как именно вы получаете эти .locals? Оба от ILDasm?   -  person svick    schedule 23.09.2013
comment
Недействительная программа меня не так беспокоит. Я думаю, что нашел это. Оба объявления .local были из ILDasm. Очевидно, он нашел некоторую разницу между кодом C# и моим Reflection.Emit.   -  person jrv    schedule 24.09.2013
comment
Учитывая, что вывод ILDasm содержит имена переменных (которые не представлены в IL), возможно, числа как-то связаны с отладочной информацией?   -  person svick    schedule 24.09.2013
comment
Для тех из вас, кто ищет закрытие такого рода вещей, проблема с недопустимой программой не была связана с [0]. Я вызывал базовый конструктор, который принимал параметр, не помещая его в стек. Я передумал, какой конструктор мне нужно вызывать, но забыл добавить параметр.   -  person jrv    schedule 24.09.2013


Ответы (1)


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

!! указан в разделе II.7.1 Типы:

Type ::=       | Description                             | Clause
  ‘!’ Int32    | Generic parameter in a type definition, | §II.9.1
               | accessed by index from 0                |
| ‘!!’ Int32   | Generic parameter in a method           | §II.9.2
               | definition, accessed by index from 0    |
...

Другими словами, внутри метода, который C# вызовет f<T, U>(), !!0 будет T, а !!1 будет U.

Однако [0] — хороший вопрос. Спецификация, похоже, не касается этого. Директива .locals описана в II.15.4.1.3 Директива .locals, в которой синтаксис указан как

MethodBodyItem ::= ...
 | .locals [ init ] ‘(’ LocalsSignature ‘)’
LocalsSignature ::= Local [ ‘,’ Local ]*
Local ::= Type [ Id ]

Кажется, нет ничего, что разрешало бы [0], если только оно не является частью Type, а Type также не допускает ничего, начинающегося с [. Я предполагаю, что это недокументированная особенность, специфичная для реализации Microsoft, предназначенная для того, чтобы помочь читателю увидеть, что местоположение 0 является локальной переменной CS$0$0000, когда сгенерированные инструкции обращаются к локальным переменным по индексу.

Эксперименты с ILAsm показывают, что это именно то, что он означает. Берем простую программу на С#:

static class Program {
    static void Main() {
        int i = 0, j = 1;
    }
}

и компиляция, а затем дизассемблирование (csc test.cs && ildasm /text test.exe >test.il) показывает:

....
.locals init (int32 V_0,
         int32 V_1)
IL_0000:  nop
IL_0001:  ldc.i4.0
IL_0002:  stloc.0
IL_0003:  ldc.i4.1
IL_0004:  stloc.1
IL_0005:  ret
....

Изменение .locals на

.locals init ([0] int32 V_0, [0] int32 V_1)

дает полезное предупреждающее сообщение:

test.il(41) : warning : Local var slot 0 is in use

И действительно, объявление переменных разных типов, затем переупорядочивание их с помощью [2], [1], [0], сборка и сразу дизассемблирование результата показывает, что переменные переупорядочены.

person Community    schedule 23.09.2013
comment
У меня есть спецификация, но, как вы говорите, она не объясняет [0], поэтому я не пробовал !!. - person jrv; 23.09.2013
comment
Правильно ли я понимаю, что [0] описывает, в каком локальном слоте находится переменная, что важно для инструкций ldloc и stloc? Пока инструкции ldloc и stloc загружаются и сохраняются в правильном слоте переменной, [0] и [1] хороши, но не нужны? - person jrv; 23.09.2013
comment
@jrv Довольно много. Если [0] используется для того, чтобы сделать его явным слотом, который он уже неявно имел, он ничего не добавляет (кроме ясности). - person ; 24.09.2013
comment
Я прошел спец. В полной спецификации языка (в конце) каждый параметр в .locals является sigArg, который будет состоять из типа paramAttr. Одним из типов paramAttr является '[' int32 ']', что я и вижу. Я не вижу возможности в LocalBuilder добавить этот атрибут параметра, но приятно видеть, что спецификация языка включает его. - person jrv; 24.09.2013
comment
@jrv Хорошая находка. VI.C.3 начинается как Эта грамматика предоставляет ряд простых в использовании функций, не предусмотренных в грамматике Раздела II, а также поддерживает некоторые функции, которые не переносимы между реализациями и, следовательно, не являются частью этого стандарта. хотя. Что касается того, когда вы используете LocalBuilder, слот будет основан на порядке, в котором вы добавляете локальные жители, поэтому просто добавляйте их в том порядке, в котором вы хотите :) - person ; 24.09.2013