Шрифт:
Интервал:
Закладка:
}
Первым шагом при размещении локальных переменных с помощью CIL является применение директивы .locals в паре с атрибутом init. Каждая переменная идентифицируется своим типом данных и необязательным именем. После определения локальных переменных значения загружаются в стек (с использованием различных кодов операций загрузки) и сохраняются в этих локальных переменных (с помощью кодов операций сохранения).
Отображение параметров на локальные переменные в CIL
Вы уже видели, каким образом объявляются локальные переменные в CIL с применением директивы .locals init; однако осталось еще взглянуть на то, как входные параметры отображаются на локальные переменные. Рассмотрим показанный ниже статический метод С#:
public static int Add(int a, int b)
{
return a + b;
(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})}
Такой с виду невинный метод требует немалого объема кодирования на языке CIL. Во-первых, входные аргументы (а и b) должны быть помещены в виртуальный стек выполнения с использованием кода операции ldarg (load argument — загрузить аргумент). Во-вторых, с помощью кода операции add из стека будут извлечены следующие два значения и просуммированы с сохранением результата обратно в стек. В-третьих, сумма будет извлечена из стека и возвращена вызывающему коду посредством кода операции ret. Дизассемблировав этот метод C# с применением ildasm.exe, вы обнаружите множество дополнительных лексем, которые были внедрены в процессе компиляции, но основная часть кода CIL довольно проста:
.method public hidebysig static int32 Add(int32 a,
int32 b) cil managed
{
.maxstack 2
ldarg.0 // Загрузить а в стек.
ldarg.1 // Загрузить b в стек.
add // Сложить оба значения.
ret
}
Скрытая ссылка this
Обратите внимание, что ссылка на два входных аргумента (а и b) в коде CIL производится с использованием их индексных позиций (0 и 1), т.к. индексация в виртуальном стеке выполнения начинается с нуля.
Во время исследования или написания кода CIL нужно помнить о том, что каждый нестатический метод, принимающий входные аргументы, автоматически получает неявный дополнительный параметр, который представляет собой ссылку на текущий объект (подобно ключевому слову this в С#). Скажем, если бы метод Add() был определен как нестатический:
<b>// Больше не является статическим!</b>
public int Add(int a, int b)
{
return a + b;
(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})}
то входные аргументы а и b загружались бы с применением кодов операций ldarg.1 и ldarg.2 (а не ожидаемых ldarg.0 и ldarg.1). Причина в том, что ячейка 0 содержит неявную ссылку this. Взгляните на следующий псевдокод:
<b>// Это ТОЛЬКО псевдокод!</b>
.method public hidebysig static int32 AddTwoIntParams(
MyClass_HiddenThisPointer this, int32 a, int32 b) cil managed
{
ldarg.0 // Load MyClass_HiddenThisPointer onto the stack.
ldarg.1 // Load "a" onto the stack.
ldarg.2 // Load "b" onto the stack.
...
}
Представление итерационных конструкций в CIL
Итерационные конструкции в языке программирования C# реализуются посредством ключевых слов for, foreach, while и do, каждое из которых имеет специальное представление в CIL. В качестве примера рассмотрим следующий классический цикл
for:
public static void CountToTen()
{
for(int i = 0; i < 10; i++)
{
}
}
Вспомните, что для управления прекращением потока выполнения, когда удовлетворено некоторое условие, используются коды операций br (br, bltn т.д.). В приведенном примере указано условие, согласно которому выполнение цикла for должно прекращаться, когда значение локальной переменной i становится больше или равно 10. С каждым проходом к значению i добавляется 1, после чего проверяемое условие оценивается заново.
Также вспомните, что в случае применения любого кода операции CIL, предназначенного для ветвления, должна быть определена специфичная метка кода (или две), обозначающая место, куда будет произведен переход при истинном результате оценки условия. С учетом всего сказанного рассмотрим показанный ниже (отредактированный) код CIL, который сгенерирован утилитой ildasm.exe (вместе с автоматически созданными метками):
.method public hidebysig static void CountToTen() cil managed
{
.maxstack 2
.locals init (int32 V_0, bool V_1)
IL_0000: ldc.i4.0 // Загрузить это значение в стек.
- Понимание SQL - Мартин Грубер - Базы данных