using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; using System.Text; using Jace.Operations; namespace Jace.Util { /// /// An adapter for creating a func wrapper around a func accepting a dictionary. The wrapper /// can create a func that has an argument for every expected key in the dictionary. /// public class FuncAdapter { /// /// Wrap the parsed the function into a delegate of the specified type. The delegate must accept /// the parameters defined in the parameters collection. The order of parameters is respected as defined /// in parameters collection. ///
/// The function must accept a dictionary of strings and doubles as input. The values passed to the /// wrapping function will be passed to the function using the dictionary. The keys in the dictionary /// are the names of the parameters of the wrapping function. ///
/// The required parameters of the wrapping function delegate. /// The function that must be wrapped. /// A delegate instance of the required type. public Delegate Wrap(IEnumerable parameters, Func, double> function) { Jace.Execution.ParameterInfo[] parameterArray = parameters.ToArray(); return GenerateDelegate(parameterArray, function); } // Uncomment for debugging purposes //public void CreateDynamicModuleBuilder() //{ // AssemblyName assemblyName = new AssemblyName("JaceDynamicAssembly"); // AppDomain domain = AppDomain.CurrentDomain; // AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(assemblyName, // AssemblyBuilderAccess.RunAndSave); // ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, "test.dll"); // TypeBuilder typeBuilder = moduleBuilder.DefineType("MyTestClass"); // MethodBuilder method = typeBuilder.DefineMethod("MyTestMethod", MethodAttributes.Static, typeof(double), // new Type[] { typeof(FuncAdapterArguments), typeof(int), typeof(double) }); // ILGenerator generator = method.GetILGenerator(); // GenerateMethodBody(generator, new List() { // new Calculator.Execution.ParameterInfo() { Name = "test1", DataType = DataType.Integer }, // new Calculator.Execution.ParameterInfo() { Name = "test2", DataType = DataType.FloatingPoint }}, // (a) => 0.0); // typeBuilder.CreateType(); // assemblyBuilder.Save(@"test.dll"); //} private Delegate GenerateDelegate(Jace.Execution.ParameterInfo[] parameterArray, Func, double> function) { Type delegateType = GetDelegateType(parameterArray); Type dictionaryType = typeof(Dictionary); ParameterExpression dictionaryExpression = Expression.Variable(typeof(Dictionary), "dictionary"); BinaryExpression dictionaryAssignExpression = Expression.Assign(dictionaryExpression, Expression.New(dictionaryType)); ParameterExpression[] parameterExpressions = new ParameterExpression[parameterArray.Length]; List methodBody = new List(); methodBody.Add(dictionaryAssignExpression); for (int i = 0; i < parameterArray.Length; i++) { // Create parameter expression for each func parameter Type parameterType = parameterArray[i].DataType == DataType.FloatingPoint ? typeof(double) : typeof(int); parameterExpressions[i] = Expression.Parameter(parameterType, parameterArray[i].Name); methodBody.Add(Expression.Call(dictionaryExpression, dictionaryType.GetRuntimeMethod("Add", new Type[] { typeof(string), typeof(double) }), Expression.Constant(parameterArray[i].Name), Expression.Convert(parameterExpressions[i], typeof(double))) ); } InvocationExpression invokeExpression = Expression.Invoke(Expression.Constant(function), dictionaryExpression); methodBody.Add(invokeExpression); LambdaExpression lambdaExpression = Expression.Lambda(delegateType, Expression.Block(new[] { dictionaryExpression }, methodBody), parameterExpressions); return lambdaExpression.Compile(); } private Type GetDelegateType(Jace.Execution.ParameterInfo[] parameters) { string funcTypeName = string.Format("System.Func`{0}", parameters.Length + 1); Type funcType = Type.GetType(funcTypeName); Type[] typeArguments = new Type[parameters.Length + 1]; for (int i = 0; i < parameters.Length; i++) typeArguments[i] = (parameters[i].DataType == DataType.FloatingPoint) ? typeof(double) : typeof(int); typeArguments[typeArguments.Length - 1] = typeof(double); return funcType.MakeGenericType(typeArguments); } private class FuncAdapterArguments { private readonly Func, double> function; public FuncAdapterArguments(Func, double> function) { this.function = function; } } } }