Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Динамическая генерация кода

40K
17 ноября 2009 года
Freya
11 / / 04.11.2008
Добрый день! :) Мне нужно динамически создавать функции, вводимые пользователем, и делать проверку как входных данных, так и результата выполнения функции. Но функции проверки у меня выдают ошибку (если я правильно поняла, то при нахождении в стеке более одного элемента). :confused: Ошибку не могу понять уже несколько дней... :( помогите, plz...

вот простейший пример функции... (закомментированные строки выдают ошибку)

Код:
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();
        }
    }
}
5
17 ноября 2009 года
hardcase
4.5K / / 09.08.2005
Какого рода функции вводит пользователь?
Можно построить динамику на основе CodeDOM или eval функций JScript.NET или IronPython, а с помощью Nemerle.Evaluator можно вообще First-class функции в чистом виде получать (при том их можно легко вызывать из C# кода).
5
17 ноября 2009 года
hardcase
4.5K / / 09.08.2005
Пример "дотягивания" до eval в JScript.NET, и одновременно демонстрация работы с CodeDOM:
Код:
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);
        }

    }

}

Кратко поясню. Создается динамическая сборка с единственным классом на 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);
        }
    }
}
5
17 ноября 2009 года
hardcase
4.5K / / 09.08.2005
А вот так можно работать с замыканиями.
Функция на 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);
        }
    }
}
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог