Разработка компиляторов

       

Генерация кода: исключения, метки


Теперь мы можем перейти к собственно генерации кода; генерируется присваивание, начинается блок try, определяется и ставится метка

ILGenerator il = mb.GetILGenerator(); LocalBuilder var_i = il.DeclareLocal (typeof(int)); il.Emit (OpCodes.Ldc_I4_0); il.Emit (OpCodes.Stloc, var_i); Label EndTry = il.BeginExceptionBlock(); Label Start = il.DefineLabel(); il.MarkLabel (Start);

Теперь мы можем создать генератор кода и приступить к порождению машинного кода для метода Run. Класс ILGenerator содержит в себе методы, необходимые для порождения команд MSIL. Далее мы создаем локальную переменную, на которую можем дальше ссылаться с использованием ее билдера var_I. В случае наличия вложенных блоков их рекомендуется ограничивать вызовами BeginScope() и EndScope().

ILGenerator il = mb.GetILGenerator(); LocalBuilder var_i = il.DeclareLocal (typeof (int));

Теперь мы генерируем присваивание значения 0 в эту переменную. Метод Emit является основным методом для генерации кода MSIL и в качестве своего первого параметра принимает код команды. OpCodes содержит константы, соответствующие всем командам MSIL. Отметим, что метод Emit перегружен и может иметь различные параметры, тип которых зависит от генерируемой команды. Например, в последующем операторе мы используем параметр типа LocalBuilder для ссылки на локальную переменную:

il.Emit (OpCodes.Ldc_I4_0); il.Emit (OpCodes.Stloc, var_i);

Для оформления try-catch блока используется метода BeginExceptionBlock , который "открывает" блок обработки исключений и создает метку, на которую впоследствии можно будет сослаться.

Label EndTry = il.BeginExceptionBlock();

Теперь мы заводим метку, которая понадобится для организации цикла. Интересно, что метод DefineLabel не генерирует никакого кода, а просто создает значение типа Label , которое в дальнейшем может быть использовано для генерации переходов. Рано или поздно метка должна быть привязана к позиции в MSIL при помощи вызова MarkLabel. Таким образом, DefineLabel соответствует объявлению идентификатора метки (в явном виде такая операция есть только в Pascal), в то время как MarkLabel соответствует определению (установке) метки.

Label Start = il.DefineLabel(); il.MarkLabel (Start);



Содержание раздела