217 lines
7.3 KiB
C#
217 lines
7.3 KiB
C#
using System.ComponentModel.Design;
|
|
using System.Reflection.Emit;
|
|
using System.Reflection;
|
|
using Flee.ExpressionElements;
|
|
using Flee.ExpressionElements.Base;
|
|
using Flee.PublicTypes;
|
|
using Flee.Resources;
|
|
using IDynamicExpression = Flee.PublicTypes.IDynamicExpression;
|
|
using System;
|
|
|
|
namespace Flee.InternalTypes
|
|
{
|
|
internal class Expression<T> : IExpression, IDynamicExpression, IGenericExpression<T>
|
|
{
|
|
private readonly string _myExpression;
|
|
private ExpressionContext _myContext;
|
|
private ExpressionOptions _myOptions;
|
|
private readonly ExpressionInfo _myInfo;
|
|
private ExpressionEvaluator<T> _myEvaluator;
|
|
|
|
private object _myOwner;
|
|
private const string EmitAssemblyName = "FleeExpression";
|
|
|
|
private const string DynamicMethodName = "Flee Expression";
|
|
public Expression(string expression, ExpressionContext context, bool isGeneric)
|
|
{
|
|
Utility.AssertNotNull(expression, "expression");
|
|
_myExpression = expression;
|
|
_myOwner = context.ExpressionOwner;
|
|
|
|
_myContext = context;
|
|
|
|
if (context.NoClone == false)
|
|
{
|
|
_myContext = context.CloneInternal(false);
|
|
}
|
|
|
|
_myInfo = new ExpressionInfo();
|
|
|
|
this.SetupOptions(_myContext.Options, isGeneric);
|
|
|
|
_myContext.Imports.ImportOwner(_myOptions.OwnerType);
|
|
|
|
this.ValidateOwner(_myOwner);
|
|
|
|
this.Compile(expression, _myOptions);
|
|
|
|
_myContext.CalculationEngine?.FixTemporaryHead(this, _myContext, _myOptions.ResultType);
|
|
}
|
|
|
|
private void SetupOptions(ExpressionOptions options, bool isGeneric)
|
|
{
|
|
// Make sure we clone the options
|
|
_myOptions = options;
|
|
_myOptions.IsGeneric = isGeneric;
|
|
|
|
if (isGeneric)
|
|
{
|
|
_myOptions.ResultType = typeof(T);
|
|
}
|
|
|
|
_myOptions.SetOwnerType(_myOwner.GetType());
|
|
}
|
|
|
|
private void Compile(string expression, ExpressionOptions options)
|
|
{
|
|
// Add the services that will be used by elements during the compile
|
|
IServiceContainer services = new ServiceContainer();
|
|
this.AddServices(services);
|
|
|
|
// Parse and get the root element of the parse tree
|
|
ExpressionElement topElement = _myContext.Parse(expression, services);
|
|
|
|
if (options.ResultType == null)
|
|
{
|
|
options.ResultType = topElement.ResultType;
|
|
}
|
|
|
|
RootExpressionElement rootElement = new RootExpressionElement(topElement, options.ResultType);
|
|
|
|
DynamicMethod dm = this.CreateDynamicMethod();
|
|
|
|
FleeILGenerator ilg = new FleeILGenerator(dm.GetILGenerator());
|
|
|
|
// Emit the IL
|
|
rootElement.Emit(ilg, services);
|
|
if (ilg.NeedsSecondPass())
|
|
{
|
|
// second pass required due to long branches.
|
|
dm = this.CreateDynamicMethod();
|
|
ilg.PrepareSecondPass(dm.GetILGenerator());
|
|
rootElement.Emit(ilg, services);
|
|
}
|
|
|
|
ilg.ValidateLength();
|
|
|
|
// Emit to an assembly if required
|
|
if (options.EmitToAssembly == true)
|
|
{
|
|
EmitToAssembly(ilg, rootElement, services);
|
|
}
|
|
|
|
Type delegateType = typeof(ExpressionEvaluator<>).MakeGenericType(typeof(T));
|
|
_myEvaluator = (ExpressionEvaluator<T>)dm.CreateDelegate(delegateType);
|
|
}
|
|
|
|
private DynamicMethod CreateDynamicMethod()
|
|
{
|
|
// Create the dynamic method
|
|
Type[] parameterTypes = {
|
|
typeof(object),
|
|
typeof(ExpressionContext),
|
|
typeof(VariableCollection)
|
|
};
|
|
DynamicMethod dm = default(DynamicMethod);
|
|
|
|
dm = new DynamicMethod(DynamicMethodName, typeof(T), parameterTypes, _myOptions.OwnerType);
|
|
|
|
return dm;
|
|
}
|
|
|
|
private void AddServices(IServiceContainer dest)
|
|
{
|
|
dest.AddService(typeof(ExpressionOptions), _myOptions);
|
|
dest.AddService(typeof(ExpressionParserOptions), _myContext.ParserOptions);
|
|
dest.AddService(typeof(ExpressionContext), _myContext);
|
|
dest.AddService(typeof(IExpression), this);
|
|
dest.AddService(typeof(ExpressionInfo), _myInfo);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Emit to an assembly. We've already computed long branches at this point,
|
|
/// so we emit as a second pass
|
|
/// </summary>
|
|
/// <param name="ilg"></param>
|
|
/// <param name="rootElement"></param>
|
|
/// <param name="services"></param>
|
|
private static void EmitToAssembly(FleeILGenerator ilg, ExpressionElement rootElement, IServiceContainer services)
|
|
{
|
|
AssemblyName assemblyName = new AssemblyName(EmitAssemblyName);
|
|
|
|
string assemblyFileName = string.Format("{0}.dll", EmitAssemblyName);
|
|
|
|
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
|
|
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyFileName);
|
|
|
|
MethodBuilder mb = moduleBuilder.DefineGlobalMethod("Evaluate", MethodAttributes.Public | MethodAttributes.Static, typeof(T), new Type[] {
|
|
typeof(object),typeof(ExpressionContext),typeof(VariableCollection)});
|
|
// already emitted once for local use,
|
|
ilg.PrepareSecondPass(mb.GetILGenerator());
|
|
|
|
rootElement.Emit(ilg, services);
|
|
|
|
moduleBuilder.CreateGlobalFunctions();
|
|
//assemblyBuilder.Save(assemblyFileName);
|
|
assemblyBuilder.CreateInstance(assemblyFileName);
|
|
}
|
|
|
|
private void ValidateOwner(object owner)
|
|
{
|
|
Utility.AssertNotNull(owner, "owner");
|
|
if (_myOptions.OwnerType.IsAssignableFrom(owner.GetType()) == false)
|
|
{
|
|
string msg = Utility.GetGeneralErrorMessage(GeneralErrorResourceKeys.NewOwnerTypeNotAssignableToCurrentOwner);
|
|
throw new ArgumentException(msg);
|
|
}
|
|
}
|
|
|
|
public object Evaluate()
|
|
{
|
|
return _myEvaluator(_myOwner, _myContext, _myContext.Variables);
|
|
}
|
|
|
|
public T EvaluateGeneric()
|
|
{
|
|
return _myEvaluator(_myOwner, _myContext, _myContext.Variables);
|
|
}
|
|
T IGenericExpression<T>.Evaluate()
|
|
{
|
|
return EvaluateGeneric();
|
|
}
|
|
|
|
public IExpression Clone()
|
|
{
|
|
Expression<T> copy = (Expression<T>)this.MemberwiseClone();
|
|
copy._myContext = _myContext.CloneInternal(true);
|
|
copy._myOptions = copy._myContext.Options;
|
|
return copy;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return _myExpression;
|
|
}
|
|
|
|
internal Type ResultType => _myOptions.ResultType;
|
|
|
|
public string Text => _myExpression;
|
|
|
|
public ExpressionInfo Info1 => _myInfo;
|
|
|
|
ExpressionInfo IExpression.Info => Info1;
|
|
|
|
public object Owner
|
|
{
|
|
get { return _myOwner; }
|
|
set
|
|
{
|
|
this.ValidateOwner(value);
|
|
_myOwner = value;
|
|
}
|
|
}
|
|
|
|
public ExpressionContext Context => _myContext;
|
|
}
|
|
}
|