Литмир - Электронная Библиотека
A
A

  int32& refInt,

  class [System.Runtime.Extensions]System.Collections.ArrayList ar,

  [out] int32& outputInt) cil managed

{

  ...

}

Исследование кодов операций CIL

Последний аспект кода CIL, который будет здесь рассматриваться, связан с ролью разнообразных кодов операций. Вспомните, что код операции — это просто лексема CIL, используемая при построении логики реализации для заданного члена.

Все коды операций CIL (которых довольно много) могут быть разделены на три обширные категории:

• коды операций, которые управляют потоком выполнения программы ;

• коды операций, которые вычисляют выражения;

• коды операций, которые получают доступ к значениям в памяти (через параметры, локальные переменные и т.д.).

В табл. 19.4 описаны наиболее полезные коды операций, имеющие прямое отношение к логике реализации членов; они сгруппированы по функциональности.

Язык программирования C#9 и платформа .NET5 - _119.png

Коды операций из следующей обширной категории (подмножество которых описано в табл. 19.5) применяются для загрузки (заталкивания) аргументов в виртуальный стек выполнения. Обратите внимание, что все эти ориентированные на загрузку коды операций имеют префикс

Id
(load — загрузить).

Язык программирования C#9 и платформа .NET5 - _120.png

В дополнение к набору кодов операций, связанных с загрузкой, CIL предоставляет многочисленные коды операций, которые явно извлекают из стека самое верхнее значение. Как было показано в нескольких начальных примерах, извлечение значения из стека обычно предусматривает его сохранение во временном локальном хранилище с целью дальнейшего использования (наподобие параметра для предстоящего вызова метода). Многие коды операций, извлекающие текущее значение из виртуального стека выполнения, снабжены префиксом

st
(store — сохранить). В табл. 19.6 описаны некоторые распространенные коды операций.

Язык программирования C#9 и платформа .NET5 - _121.png

Имейте в виду, что различные коды операций CIL будут неявно извлекать значения из стека во время выполнения своих задач. Например, при вычитании одного числа из другого с применением кода операции

sub
должно быть очевидным то, что перед самим вычислением операция
sub
должна извлечь из стека два следующих доступных значения. Результат вычисления снова помещается в стек.

Директива .maxstack

При написании кода реализации методов на низкоуровневом языке CIL необходимо помнить о специальной директиве под названием

.maxstack
. С ее помощью устанавливается максимальное количество переменных, которые могут находиться внутри стека в любой заданный момент времени на протяжении периода выполнения метода. Хорошая новость в том, что директива
.maxstack
имеет стандартное значение (
8
), которое должно подойти для подавляющего большинства создаваемых методов. Тем не менее, если вы хотите указывать все явно, то можете вручную подсчитать количество локальных переменных в стеке и определить это значение явно:

.method public hidebysig instance void

  Speak() cil managed

{

<b>  // Внутри области действия этого метода в стеке находится</b>

<b>  // в точности одно значение (строковый литерал).</b>

<b>  .maxstack 1</b>

  ldstr &quot;Hello there...&quot;

  call void [mscorlib]System.Console::WriteLine(string)

  ret

}

Объявление локальных переменных в CIL

Теперь давайте посмотрим, как объявлять локальные переменные. Предположим, что необходимо построить в CIL метод по имени

MyLocalVariables()
, который не принимает аргументы и возвращает
void
, и определить в нем три локальные переменные с типами
System.String
,
System.Int32
и
System.Object
. В C# такой метод выглядел бы следующим образом (вспомните, что локальные переменные не получают стандартные значения и потому перед использованием должны быть инициализированы):

public static void MyLocalVariables()

{

  string myStr = &quot;CIL code is fun!&quot;;

  int myInt = 33;

  object myObj = new object();

}

А вот как реализовать метод

MyLocalVariables()
на языке CIL:

.method public hidebysig static void

  MyLocalVariables() cil managed

{

  .maxstack 8

<b>  // Определить три локальные переменные.</b>

  .locals init (string myStr, int32 myInt, object myObj)

<b>  // Загрузить строку в виртуальный стек выполнения.</b>

  ldstr &quot;CIL code is fun!&quot;

<b>  // Извлечь текущее значение и сохранить его в локальной переменной [0].</b>

  stloc.0

<b>  // Загрузить константу типа i4 (сокращение для int32) со значением 33.</b>

  ldc.i4.s 33

<b>  // Извлечь текущее значение и сохранить его в локальной переменной [1].</b>

  stloc.1

<b>  // Создать новый объект и поместить его в стек.</b>

  newobj instance void [mscorlib]System.Object::.ctor()

<b>  // Извлечь текущее значение и сохранить его в локальной переменной [2].</b>

  stloc.2

  ret

354
{"b":"847442","o":1}