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
|
{
|
/// <summary>
|
/// 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.
|
/// </summary>
|
public class FuncAdapter
|
{
|
/// <summary>
|
/// 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.
|
/// <br/>
|
/// 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.
|
/// </summary>
|
/// <param name="parameters">The required parameters of the wrapping function delegate.</param>
|
/// <param name="function">The function that must be wrapped.</param>
|
/// <returns>A delegate instance of the required type.</returns>
|
public Delegate Wrap(IEnumerable<Jace.Execution.ParameterInfo> parameters,
|
Func<IDictionary<string, double>, 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<Calculator.Execution.ParameterInfo>() {
|
// 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<Dictionary<string, double>, double> function)
|
{
|
Type delegateType = GetDelegateType(parameterArray);
|
Type dictionaryType = typeof(Dictionary<string, double>);
|
|
ParameterExpression dictionaryExpression =
|
Expression.Variable(typeof(Dictionary<string, double>), "dictionary");
|
BinaryExpression dictionaryAssignExpression =
|
Expression.Assign(dictionaryExpression, Expression.New(dictionaryType));
|
|
ParameterExpression[] parameterExpressions = new ParameterExpression[parameterArray.Length];
|
|
List<Expression> methodBody = new List<Expression>();
|
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<Dictionary<string, double>, double> function;
|
|
public FuncAdapterArguments(Func<Dictionary<string, double>, double> function)
|
{
|
this.function = function;
|
}
|
}
|
}
|
}
|