private static object TryCompile(ref ClosureInfo closureInfo,
                                         Type delegateType, Type[] paramTypes, Type returnType,
                                         Expression bodyExpr, IList <ParameterExpression> paramExprs)
        {
            if (!TryCollectBoundConstants(ref closureInfo, bodyExpr, paramExprs))
            {
                return(null);
            }

            if (closureInfo != null)
            {
                closureInfo.ConstructClosure();
            }

            var method = GetDynamicMethod(paramTypes, returnType, closureInfo);

            var il = method.GetILGenerator();

            if (!EmittingVisitor.TryEmit(bodyExpr, paramExprs, il, closureInfo))
            {
                return(null);
            }

            il.Emit(OpCodes.Ret); // emits return from generated method

            // create open delegate with closure object
            if (closureInfo == null)
            {
                return(method.CreateDelegate(delegateType));
            }

            return(method.CreateDelegate(delegateType, closureInfo.ClosureObject));
        }
Example #2
0
        /// <summary>Compiles expression to delegate by emitting the IL.
        /// If sub-expressions are not supported by emitter, then the method returns null.
        /// The usage should be calling the method, if result is null then calling the Expression.Compile.</summary>
        /// <param name="bodyExpr">Lambda body.</param>
        /// <param name="paramExprs">Lambda parameter expressions.</param>
        /// <param name="paramTypes">The types of parameters.</param>
        /// <param name="returnType">The return type.</param>
        /// <returns>Result delegate or null, if unable to compile.</returns>
        public static TDelegate TryCompile <TDelegate>(
            Expression bodyExpr,
            ParameterExpression[] paramExprs,
            Type[] paramTypes,
            Type returnType) where TDelegate : class
        {
            var constantExprs = new List <ConstantExpression>();

            if (!TryCollectBoundConstants(bodyExpr, constantExprs))
            {
                return(null);
            }

            object      closure     = null;
            ClosureInfo closureInfo = null;

            DynamicMethod method;

            if (constantExprs.Count == 0)
            {
                method = new DynamicMethod(string.Empty, returnType, paramTypes,
                                           typeof(FastExpressionCompiler).Module, skipVisibility: true);
            }
            else
            {
                var constants     = new object[constantExprs.Count];
                var constantCount = constants.Length;

                for (var i = constantCount - 1; i >= 0; i--)
                {
                    constants[i] = constantExprs[i].Value;
                }

                if (constantCount <= Closure.CreateMethods.Length)
                {
                    var createClosureMethod = Closure.CreateMethods[constantCount - 1];

                    var constantTypes = new Type[constantCount];
                    for (var i = 0; i < constantCount; i++)
                    {
                        constantTypes[i] = constantExprs[i].Type;
                    }

                    var createClosure = createClosureMethod.MakeGenericMethod(constantTypes);

                    closure = createClosure.Invoke(null, constants);

                    var fields      = closure.GetType().GetTypeInfo().DeclaredFields;
                    var fieldsArray = fields as FieldInfo[] ?? fields.ToArray();

                    closureInfo = new ClosureInfo(constantExprs, fieldsArray);
                }
                else
                {
                    var arrayClosure = new ArrayClosure(constants);
                    closure     = arrayClosure;
                    closureInfo = new ClosureInfo(constantExprs);
                }

                var closureType          = closure.GetType();
                var closureAndParamTypes = GetClosureAndParamTypes(paramTypes, closureType);

                method = new DynamicMethod(string.Empty, returnType, closureAndParamTypes, closureType, skipVisibility: true);
            }

            var il      = method.GetILGenerator();
            var emitted = EmittingVisitor.TryEmit(bodyExpr, paramExprs, il, closureInfo);

            if (emitted)
            {
                il.Emit(OpCodes.Ret);
                return((TDelegate)(object)method.CreateDelegate(typeof(TDelegate), closure));
            }

            return(null);
        }