+7 (812) 670-9095
Обратная связьEnglish
Главная → Статьи → Системное ПО → Сам себе Microsoft
Полезный совет
Если вы часто работаете с объёмными таблицами, постоянно вносите в них новые данные, используете сортировку, фильтры, но делаете всё это вручную, то крайне полезной функцией для вас окажутся Динамические таблицы Excel.Подробнее
Версия для печати

Сам себе Microsoft

23 мая 2017

Аркадий Пчелинцев, архитектор проектов, АстроСофтАркадий Пчелинцев, архитектор проектов в АстроСофт, рассказал о создании собственного генератора, который строит MSIL-код «на лету» и исполняет его. Статья также размещена на Хабре.




Мы уже рассмотрели способ создания встраиваемого скриптового движка на основе CodeDom.Compiler и класса CSharpCodeProvider. Поставим теперь более амбициозную задачу, где не будем полагаться на готовый компилятор. Будем писать свой собственный генератор, который строит MSIL-код «на лету» и исполняет его.


Для начала, попробуем сложить два числа и распечатать результат как бы на C#, но не пользуясь его языковыми конструкциями:

 
using System;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            Type[] par = new Type[] { typeof(Int32), typeof(Int32) };
            DynamicMethod func = new DynamicMethod("AddTwoValues", typeof(Int32), par, false);
            ILGenerator il = func.GetILGenerator();

            // это то же самое как: int AddTwoValues(int x, int y) { return x+y; }
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Add);
            il.Emit(OpCodes.Ret);

            Object[] param = new Object[] { 13, 12 };   // готовим параметры 12 и 13

            int iRet=(int)func.Invoke(null, param);     // выполняем

            // выводим что получилось
            Console.WriteLine("{0}+{1}={2}", param[0], param[1], iRet);  
        }
    }
}

Запускаем на исполнение:

Сам себе Microsoft

Итак, в простейшем случае мы уже смогли сгенерировать свой собственный MSIL-код для сложения двух чисел. На этом уже вполне можно было бы написать приложение с функциональностью старого калькулятора «Электроника», который с 80-х годов прошлого века пылится у меня в столе. Теперь добавим в сгенерированный код вызовы функций, например, пусть параметры распечатываются непосредственно в том коде, который создавался нами «на лету»:

 
using System;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            Type[] par = new Type[] { typeof(Int32), typeof(Int32) };
            DynamicMethod func = new DynamicMethod("AddTwoValues", typeof(Int32), par, false);
            ILGenerator il = func.GetILGenerator();

            MethodInfo fnWriteLine = typeof(Console).
                                     GetMethod("WriteLine", new Type[] { typeof(Int32) });

            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Call, fnWriteLine);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Call, fnWriteLine);

            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Add);
            il.Emit(OpCodes.Ret);

            Object[] param = new Object[] { 13, 12 };   // готовим параметры 12 и 13

            int iRet=(int)func.Invoke(null, param);     // выполняем

            // выводим что получилось
            Console.WriteLine("{0}+{1}={2}", param[0], param[1], iRet);
        }
    }
} 

Запускаем на исполнение:

Сам себе Microsoft

Итак, теперь мы умеем генерировать MSIL-код таким образом, что из него можно вызывать функции. Обратите внимание, что мы генерируем непосредственно MSIL в памяти без всяких промежуточных файлов. И в нашей власти теперь всё, мы можем менять генерацию кода как угодно – например, для нотификации чего-либо вставлять через строчку вызов какого-либо callback-метода или что-то ещё. Что нас ещё отделяет от написания собственного языка программирования? Да только формальный синтаксис и его парсер! Если мы напишем соответствующий парсер, то ничто нам не сможет помешать создать свой собственный язык программирования, например, с таким синтаксисом:

 
ЕСЛИ КЛИЕНТ АКТИВЕН
НАЧАЛО БЛОКА
КУПИТЬ БИЛЕТ НА ПОЕЗД 120 на 31/12/2017 КУПЕЙНЫЙ НИЖНЕЕ
РАСПЕЧАТАТЬ БЛАНК
КОНЕЦ БЛОКА

О парсере и формальном синтаксисе разговор пойдёт в следующий статье.


Теги: CodeDom.Compiler, CSharpCodeProvider, MSIL-код, C#, .net