Рабочим названием платформы .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++; } }