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

Итак, начнем.

Как уже упоминалось в заголовке тема относится к области динамической кодогенерации в среде .NET. Именно данную тематику выбрал потому, что в свое время очень увлекся этим аспектом и можно сказать достиг определенных успехов (для себя). Все началось с лабораторной работы по численным методам, где мне пришлось «зашивать» в код программы математическое выражение, от которого я вычислял интеграл. Мне тогда пришла в голову мысль: «А почему я не могу в режиме выполнения программы задать это самое мат. выражение и чтобы программа посчитала мне результат?» Естественно, понимание подобных аспектов пришло позже, но тогда это было просто загадкой. Такой вопрос я задал преподавателю, на что получил ответ: «Не ломай себе голову. Ты все равно не сможешь и бла бла бла. А я [преподаватель] знаю и могу». Это фраза не могла меня не задеть в силу моего (также как и всех Вас читающих) самолюбия. Я активно занялся изучением этой тематики и перерыл немало ресурсов в инете. Как оказалось, русскоязычных материалов в то время по этой тематике не было вообще (это еще времена .NET 2.0). В общем, все что нашел — обратная польская нотация — но это как Вы все знаете, просто алгоритм разбора в стек + примитивный интерпретатор.
Но спустя время усилия привели меня к языку C# и собственно платформе .NET, которую я начал активно изучать и напоролся на пространство имен System.Reflection, в которой и нашел все, что мне тогда было так необходимо. Сначала это генерация кода из текста прям на языке и подключение сгенерированной сборки к коду. Но в итоге и от этого я ушел. И пришел к генерации кода на MSIL — тот самый байт-код, в который переводится любая программа, написанная в .NET. Именно из этого кода jit-компилятор генерирует нативный код на языке процессора.
Ладно, закончим столь длинное вступление (text), а то:

bool HabraInviteThisAutor(string text)
{
if (text.Lenght > Habra.MaxLenghtForStupidInviteText || text.Contains("Я") ||
text.Contains("мегапрограммист") || text.Contains("примите пожалуйста") || text.Contains("обещаю классную тему"))
return false;
else
if (text.Contains("хабрасообщество крутые перцы" || text.Contains("классные топики"))
return true;
}


— вернет false. Прошу прощения за юмор. Продолжим и приступим к основам MSIL и как его можно применять.

Как уже упоминалось выше, MSIL (Microsoft Intermediate Language) является основой всей платформы .NET в связке с jit-компилятором (jit — just-in-time, компиляция во время исполнения) и представляет собой байт-код, состоящий из опкодов. Многие найдут сходство с опкодами «книги двойных слов» компаний-производителей процессоров, на самом деле это команды процессору в их hex-коде, представленные в текстовом виде (как в ассемблере). Опкоды в MSIL — это команды стековой машины, да именно стековой. Особенности MSIL я опущу для краткости моего хабрапоста, но ссылки на информаицию по нему я дам:
Wiki
WASM.ru
INTUIT.ru
На этих сайтах можно найти подробную информацию по MSIL, также рекомендую MSDN пространство имен System.Reflection.Emit — там есть подробное описание опкодов и прочих штучек. Как жаль, что все это пришлось изучать самому без указанной Вам информации. В то время кроме англоязычной MSDN ничего не было в помощь .NET программисту.

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

Сначала создается объект сборки AssemblyBuilder, в нее добавляется объект модуля ModuleBuilder, в который добавляется объект типа TypeBuilder…

public interface IFunction
{
double Function(double x);
}


AssemblyName asmName = new AssemblyName("Temp");
AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
asmName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = asmBuilder.DefineDynamicModule("TempModule");
TypeBuilder typeBuilder = moduleBuilder.DefineType("TempClass", TypeAttributes.Public);
typeBuilder.AddInterfaceImplementation(typeof(IFunction));

Type[] paramsTypes = { typeof(double) };
Type returnType = typeof(double);

MethodBuilder methodBuilder = typeBuilder.DefineMethod(
"Function", MethodAttributes.Public | MethodAttributes.Virtual,
returnType, paramsTypes);

ILGenerator il = methodBuilder.GetILGenerator();

//Получаем IL код -----------------------------------------------
Scan(MathExpression, ref il);
il.Emit(OpCodes.Ret);
//---------------------------------------------------------------

MethodInfo methodInfo = typeof(IFunction).GetMethod("Function");
typeBuilder.DefineMethodOverride(methodBuilder, methodInfo);
typeBuilder.CreateType();

return (IFunction)asmBuilder.CreateInstance("TempClass");


Итак, надо создать некий интерфейс, в котором указать сигнатуру метода, который будем в дальнейшем генерировать. Вновь созданный тип (TypeBuilder отвечает за создание динамического типа) должен программно унаследовать этот интерфейс — typeBuilder.AddInterfaceImplementation(typeof(IFunction)).
Потом MethodBuilder, создаваемый для вышеупомянутого typeBuilder должен динамически создавать метод с сигнатурой, совпадающей с сигнатурой метода того самого интерфейса. Дальше просто указываем на переопределение тела метода typeBuilder.DefineMethodOverride(methodBuilder, methodInfo).
Создаем метод в типе в модуле в сборке typeBuilder.CreateType(), IFunction mydynamicFunc = (IFunction)asmBuilder.CreateInstance(«TempClass»).
Интерфейс нужен, чтобы из динамически созданной библиотеки вытащить сгенерированный метод, который был программно переопределен (override), т.к. класс, в котором мы генерировали тело метода, наследует этот интерфейс и обязан определить тело наследуемых из интерфейса методов.
В коде должно быть все понятно, некоторые моменты опускаю.

ILGenerator — как раз класс, который дает нам все необходимые методы для добавления нужных нам инструкций в поток MSIL байт кода. Например, нам надо сложить 2 + Cos(0) и вернуть результат:

ILGenerator il = methodInfo.GetILGenerator();
il.Emit(OpCodes.Ldc_R8, 2.0); //Ldc_R8 - загрузить в верщину стека real тип длиной 8 байт - double :) - 2.0
il.Emit(OpCodes.Ldc_R8, 0.0); //тоже самое, но для 0.0
il.Emit(Opcodes.Call, typeof(System.Math).GetMethod("Cos")); //call - вызвать функцию, второй параметр - methodInfo Cos
il.Emit(OpCodes.Add); //+
il.Emit(OpCodes.Ret); //return


В принципе все просто.

По сути, парсер может просто создавать дерево, из которого потом можно сгенерировать функцию.

В данный момент .NET 3.5 SP1 уже имеет ряд классов, позволяющих реализовать механизм кодогенерации в реальном времени гораздо проще — классы Expression и ExpressionTree.
Повторюсь, на момент экспериментов ничего такого не было, да и кодогенерация на байт-коде гораздо более гибкая и предоставляет огромные возможности. Кто хочет, может писать собственные компиляторы, интерпретаторы и прочее прочее…

Также хочу отметить, что я пошел еще дальше. Просто задумался, что если все это дело курирует jit, то получается jit это каким-то образом преобразует в native код для х86 и х64, а jit точно написан на С++, следовательно есть возможность кодогенерировать непосредственно в памяти и исполнять оттуда код… Могу предложить подготовленну статью. Но это если мне дадут инвайт сюды: Р.

Статья получилась в спешке и очень сырая. Но надеюсь будет интересна всем, кто ее прочитают. Удачи Всем! Благодарю за внимание!