Опубликован: 28.06.2006 | Уровень: специалист | Доступ: платный | ВУЗ: Московский государственный технический университет им. Н.Э. Баумана
Лекция 8:

Анализ кода на CIL

Создание дерева блоков

На втором этапе работы алгоритма мы строим дерево блоков на основе информации, находящейся в массиве предложений обработки исключений.

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

  • Поле block.

    Это поле содержит ссылку на блок.

  • Поле offset.

    Содержит целое число, обозначающее индекс первой инструкции блока в массиве P.

  • Поле length.

    Содержит количество инструкций, входящих в блок (длина блока).

Следует отметить, что размер массива B заранее неизвестен и зависит от информации в массиве EH. Нетрудно догадаться, что минимальное количество блоков в массиве B равно M+2 (если мы имеем один защищенный блок, M блоков обработки исключений и один блок тела метода). Аналогично, максимальное количество блоков вычисляется по формуле 2*M+1 ( M защищенных блоков, M блоков обработки исключений и один блок тела метода). Таким образом, необходимо либо сделать массив B динамическим, либо выделить для него 2*M+1 записей. В любом случае, пусть в дальнейшем BN обозначает текущее количество блоков, информация о которых хранится в массиве B.

Сначала создадим блок тела метода (назовем его MBB ). Он будет являться корнем дерева, которое мы строим. К нему в дальнейшем будут "прицеплены" все остальные узлы графа, и именно его наш алгоритм будет возвращать в результате своей работы. Для блока MBB в массив B добавляется запись, поле start которой содержит значение 0, а поле length - значение N.

Напомним, что предложения обработки исключений расположены в массиве EH в определенном порядке, гарантирующем, что более вложенный блок находится ближе к началу массива, чем объемлющий его блок. Так как нам требуется строить дерево блоков в направлении от корня к листам, то есть от менее вложенных блоков к более вложенным, то мы будем просматривать массив EH от конца до начала.

Итак, пусть переменная i пробегает значения от M-1 до 0 включительно. Тогда для каждого i-го предложения мы будем выполнять следующее:

  1. Поиск в массиве B блока, диапазон инструкций которого содержит защищенную область i-го предложения.

    Мы перебираем элементы массива B в обратном порядке, начиная с последнего элемента, поэтому найденный блок будет самым вложенным из блоков, диапазон инструкций которых содержит защищенную область i-го предложения.

  2. Создание нового защищенного блока и добавление его в дерево.

    Возможен случай, когда диапазон инструкций блока, найденного в пункте 1, совпадает с защищенной областью i-го предложения. Если это так, то создание нового защищенного блока не требуется. В противном случае мы создаем новый блок и добавляем информацию о нем в массив B. При этом родителем для созданного блока становится блок, найденный в пункте 1.

  3. Создание блока обработки исключений.

    На основе информации i-го предложения мы создаем новый блок обработки исключений, добавляем его в массив B и связываем его с защищенным блоком. При этом родителем созданного блока будет являться родительский блок защищенного блока.

  4. Создание блока фильтрации.

    Если мы имеем дело с блоком обработки исключений с пользовательской фильтрацией, нам придется создать новый блок фильтрации. При этом родителем для блока фильтрации является блок обработки исключений.

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

MBB = новый блок тела метода, в который записана информация о методе:
имя метода, сигнатура и данные о типах локальных переменных;

B = новый массив размера 2*M+1 для хранения информации о блоках;
B[0].block = MBB; B[0].start = 0; B[0].length = N;
BN = 1;
for (int i = M-1; i >= 0; i--)
{
  /* Поиск в массиве B блока, диапазон инструкций которого содержит защищенную областью 
i-го предложения */
  int tryOffset = EH[i].TryOffset, tryLength = EH[i].TryLength;
  for (int j = BN-1; j >= 0; j--)
    if (tryOffset >= B[j].offset &&
      tryOffset+tryLength <= B[j].offset+B[j].length)
      break;

  if (tryOffset != B[j].offset || tryLength != B[j].length)
  {
    /* Создание нового защищенного блока и 
	        добавление его в дерево */
    B[BN].block = новый защищенный блок;
    Сделать блок B[j].block родителем блока B[BN].block;
    B[BN].offset = tryOffset;
    B[BN].length = tryLength;
    j = BN; BN++;
  }

  /* Создание блока обработки исключений */
  B[BN].block = новый блок обработки исключений, 
		 тип которого определяется значением EH[i].Flags;
  Сделать родителем блока B[BN].block тот же блок, 
		 который является родителем блока B[j].block;
  Добавить блок B[BN].block в конец списка 
		 обработчиков, ассоциированных с блоком B[j].block;
  B[BN].offset = EH[i].HandlerOffset;
  B[BN].length = EH[i].HandlerLength;
  BN++;

  if (EH[i].Flags обозначает блок с пользовательской фильтрацией)
  {
    /* Создание блока фильтрации */
    B[BN].block = новый блок фильтрации;
    Сделать блок B[BN-1].block родителем блока B[BN].block;
    B[BN].offset = EH[i].FilterOffset;
    B[BN].length = EH[i].HandlerOffset - EH[i].FilterOffset;
    BN++;
  }
}
Анастасия Булинкова
Анастасия Булинкова
Рабочим названием платформы .NET было
Bogdan Drumov
Bogdan Drumov
Молдова, Республика
Azamat Nurmanbetov
Azamat Nurmanbetov
Киргизия, Bishkek