| Рабочим названием платформы .NET было |
Анализ кода на 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-го предложения мы будем выполнять следующее:
- Поиск в массиве B блока, диапазон инструкций которого содержит защищенную область i-го предложения.
Мы перебираем элементы массива B в обратном порядке, начиная с последнего элемента, поэтому найденный блок будет самым вложенным из блоков, диапазон инструкций которых содержит защищенную область i-го предложения.
- Создание нового защищенного блока и добавление его в дерево.
Возможен случай, когда диапазон инструкций блока, найденного в пункте 1, совпадает с защищенной областью i-го предложения. Если это так, то создание нового защищенного блока не требуется. В противном случае мы создаем новый блок и добавляем информацию о нем в массив B. При этом родителем для созданного блока становится блок, найденный в пункте 1.
- Создание блока обработки исключений.
На основе информации i-го предложения мы создаем новый блок обработки исключений, добавляем его в массив B и связываем его с защищенным блоком. При этом родителем созданного блока будет являться родительский блок защищенного блока.
- Создание блока фильтрации.
Если мы имеем дело с блоком обработки исключений с пользовательской фильтрацией, нам придется создать новый блок фильтрации. При этом родителем для блока фильтрации является блок обработки исключений.
На нашем псевдоязыке второй этап алгоритма можно записать следующим образом:
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++;
}
}