using System;
using System.Threading;
using System.Reflection;
using System.Reflection.Emit;
class ILDemo
{
class Program
{
public static Type BuildAdderType()
{
AppDomain myDomain = Thread.GetDomain();
AssemblyName myAsmName = new AssemblyName();
myAsmName.Name = "AdderExceptionAsm";
AssemblyBuilder myAsmBldr = myDomain.DefineDynamicAssembly(myAsmName,
AssemblyBuilderAccess.Run);
ModuleBuilder myModBldr = myAsmBldr.DefineDynamicModule("AdderExceptionMod");
TypeBuilder myTypeBldr = myModBldr.DefineType("Adder", TypeAttributes.Public);
MethodBuilder adderBldr = myTypeBldr.DefineMethod("DoAdd",
MethodAttributes.Public,
typeof(double), new Type[0]);
ILGenerator generator = adderBldr.GetILGenerator();
Label failed = generator.DefineLabel();
Label end = generator.DefineLabel();
// длина стека
LocalBuilder stacklength = generator.DeclareLocal(typeof(Int32));
LocalBuilder tmp = generator.DeclareLocal(typeof(double));
generator.Emit(OpCodes.Ldc_I4, 0);
generator.Emit(OpCodes.Stloc, stacklength);
// 2
double number = 2.0;
generator.Emit(OpCodes.Ldc_R8, number);
ChangeStackLength(generator, stacklength, 1, operation.add);
AnalizeError(generator, tmp, failed);
// 3
number = 3.0;
generator.Emit(OpCodes.Ldc_R8, number);
ChangeStackLength(generator, stacklength, 1, operation.add);
//AnalizeError(generator, tmp, failed);
// +
generator.Emit(OpCodes.Add);
ChangeStackLength(generator, stacklength, 1, operation.sub);
AnalizeError(generator, tmp, failed);
generator.Emit(OpCodes.Br_S, end);
generator.MarkLabel(failed);
ChangeStackLength(generator, stacklength, 1, operation.sub);
//ClearStack(generator, stacklength);
generator.Emit(OpCodes.Ldc_R8, double.NaN);
generator.MarkLabel(end);
generator.Emit(OpCodes.Ret);
return myTypeBldr.CreateType();
}
// очищает стек
private static void ClearStack(ILGenerator generator, LocalBuilder stacklength)
{
Label loop = generator.DefineLabel();
Label endloop = generator.DefineLabel();
generator.MarkLabel(loop);
generator.Emit(OpCodes.Ldloc, stacklength);
generator.Emit(OpCodes.Ldc_I4, 1);
generator.Emit(OpCodes.Clt);
generator.Emit(OpCodes.Brtrue, endloop);
generator.Emit(OpCodes.Pop);
ChangeStackLength(generator, stacklength, 1, operation.sub);
generator.Emit(OpCodes.Br_S, loop);
generator.MarkLabel(endloop);
}
private enum operation { add, sub };
// измеряет длину стека
private static void ChangeStackLength(ILGenerator generator, LocalBuilder stacklength, int number, operation op)
{
if (number == 0) return;
generator.Emit(OpCodes.Ldloc, stacklength);
generator.Emit(OpCodes.Ldc_I4, number);
if (op == operation.add) generator.Emit(OpCodes.Add);
else generator.Emit(OpCodes.Sub);
generator.Emit(OpCodes.Stloc, stacklength);
}
// проверяет верхнее значение стека на NaN, +-Infinity
private static void AnalizeError(ILGenerator generator, LocalBuilder tmp, Label failed)
{
MethodInfo methodIsNaN = typeof(double).GetMethod("IsNaN", new Type[] { typeof(double) });
MethodInfo methodIsInfinity = typeof(double).GetMethod("IsInfinity", new Type[] { typeof(double) });
generator.Emit(OpCodes.Stloc, tmp);
// проверка на NaN, +-Infinity
generator.Emit(OpCodes.Ldloc, tmp);
generator.EmitCall(OpCodes.Call, methodIsNaN, null);
generator.Emit(OpCodes.Brtrue, failed);
generator.Emit(OpCodes.Ldloc, tmp);
generator.EmitCall(OpCodes.Call, methodIsInfinity, null);
generator.Emit(OpCodes.Brtrue, failed);
generator.Emit(OpCodes.Ldloc, tmp);
}
public static void Main()
{
Type adderType = BuildAdderType();
object addIns = Activator.CreateInstance(adderType);
object[] addParams = new object[0];
double b = (double)adderType.InvokeMember("DoAdd", BindingFlags.InvokeMethod, null, addIns, addParams);
Console.WriteLine(b.ToString());
Console.ReadLine();
}
}
}
Динамическая генерация кода
Добрый день! :) Мне нужно динамически создавать функции, вводимые пользователем, и делать проверку как входных данных, так и результата выполнения функции. Но функции проверки у меня выдают ошибку (если я правильно поняла, то при нахождении в стеке более одного элемента). :confused: Ошибку не могу понять уже несколько дней... :( помогите, plz...
Можно построить динамику на основе CodeDOM или eval функций JScript.NET или IronPython, а с помощью Nemerle.Evaluator можно вообще First-class функции в чистом виде получать (при том их можно легко вызывать из C# кода).
Код:
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.JScript;
namespace JScriptEvaluation {
public interface IEvaluator {
object Evaluate(string source);
}
public sealed class JScriptEvaluator {
static JScriptEvaluator() {
const string core_evaluator_source = @"
package JScriptEvaluation {
public class Evaluator implements JScriptEvaluation.IEvaluator {
public function Evaluate(expr : String) : Object {
return eval(expr, ""unsafe"");
}
}
}
";
ICodeCompiler compiler = new JScriptCodeProvider().CreateCompiler();
CompilerParameters parms = new CompilerParameters();
parms.GenerateInMemory = true;
parms.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().GetName().Name);
CompilerResults results = compiler.CompileAssemblyFromSource(parms, core_evaluator_source);
evaluator = (IEvaluator)Activator.CreateInstance(results.CompiledAssembly.GetType("JScriptEvaluation.Evaluator"));
}
private static readonly IEvaluator evaluator;
public static object Evaluate(string script) {
return evaluator.Evaluate(script);
}
}
}
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.JScript;
namespace JScriptEvaluation {
public interface IEvaluator {
object Evaluate(string source);
}
public sealed class JScriptEvaluator {
static JScriptEvaluator() {
const string core_evaluator_source = @"
package JScriptEvaluation {
public class Evaluator implements JScriptEvaluation.IEvaluator {
public function Evaluate(expr : String) : Object {
return eval(expr, ""unsafe"");
}
}
}
";
ICodeCompiler compiler = new JScriptCodeProvider().CreateCompiler();
CompilerParameters parms = new CompilerParameters();
parms.GenerateInMemory = true;
parms.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().GetName().Name);
CompilerResults results = compiler.CompileAssemblyFromSource(parms, core_evaluator_source);
evaluator = (IEvaluator)Activator.CreateInstance(results.CompiledAssembly.GetType("JScriptEvaluation.Evaluator"));
}
private static readonly IEvaluator evaluator;
public static object Evaluate(string script) {
return evaluator.Evaluate(script);
}
}
}
Кратко поясню. Создается динамическая сборка с единственным классом на JScript, реализующим интерфейс IEvaluator (интерфейс объявлен в этой же сборке, магия, ага), далее создается его экземпляр и приводится к этому интерфейсу.
Для работы кода нужно добавить ссылку на Microsoft.JScript.dll.
Использовать можно вот так:
Код:
using System;
using JScriptEvaluation;
namespace ConsoleApplication27 {
public class Program {
static void Main(string[] args) {
string code = @"
function Foo(a, b) {
return a + b
}
Foo(10, 17)";
object result = JScriptEvaluator.Evaluate(code);
Console.WriteLine(result.GetType().ToString() + " : " + result.ToString());
Console.ReadKey(true);
}
}
}
using JScriptEvaluation;
namespace ConsoleApplication27 {
public class Program {
static void Main(string[] args) {
string code = @"
function Foo(a, b) {
return a + b
}
Foo(10, 17)";
object result = JScriptEvaluator.Evaluate(code);
Console.WriteLine(result.GetType().ToString() + " : " + result.ToString());
Console.ReadKey(true);
}
}
}
Функция на jscript возвращает другую функцию, которую мы самостоятельно вызываем из кода:
Код:
using System;
using JScriptEvaluation;
using Microsoft.JScript;
namespace ConsoleApplication27 {
public class Program {
static void Main(string[] args) {
string code = @"
function closure() {
return function (a, b) {
return a + b;
}
}
closure()";
Closure func = (Closure)JScriptEvaluator.Evaluate(code);
object result = func.Invoke(null, new object[] { 10L, 17.0 });
Console.WriteLine(result.GetType().ToString() + " : " + result.ToString());
Console.ReadKey(true);
}
}
}
using JScriptEvaluation;
using Microsoft.JScript;
namespace ConsoleApplication27 {
public class Program {
static void Main(string[] args) {
string code = @"
function closure() {
return function (a, b) {
return a + b;
}
}
closure()";
Closure func = (Closure)JScriptEvaluator.Evaluate(code);
object result = func.Invoke(null, new object[] { 10L, 17.0 });
Console.WriteLine(result.GetType().ToString() + " : " + result.ToString());
Console.ReadKey(true);
}
}
}