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;
}
}
}
}