コード例 #1
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);
        }
コード例 #2
0
            public void ConstructClosure()
            {
                var constantCount          = ConstantCount;
                var constantPlusParamCount = constantCount + UsedParamCount;
                var totalItemCount         = constantPlusParamCount + NestedLambdaCount;

                var items = new object[totalItemCount];

                var constantTypes = totalItemCount <= Closure.CreateMethods.Length ? new Type[totalItemCount] : null;

                if (ConstantExpressions != null)
                {
                    for (var i = 0; i < ConstantExpressions.Count; i++)
                    {
                        var constantExpr = ConstantExpressions[i];
                        items[i] = constantExpr.Value;
                        if (constantTypes != null)
                        {
                            constantTypes[i] = constantExpr.Type;
                        }
                    }
                }

                if (UsedParamExpressions != null)
                {
                    for (var i = 0; i < UsedParamExpressions.Count; i++)
                    {
                        items[constantCount + i] = null;
                        if (constantTypes != null)
                        {
                            constantTypes[constantCount + i] = UsedParamExpressions[i].Type;
                        }
                    }
                }

                if (NestedLambdas != null)
                {
                    for (var i = 0; i < NestedLambdas.Count; i++)
                    {
                        var lambda = NestedLambdas[i].Lambda;
                        items[constantPlusParamCount + i] = lambda;
                        if (constantTypes != null)
                        {
                            constantTypes[constantPlusParamCount + i] = lambda.GetType();
                        }
                    }
                }

                if (constantTypes != null)
                {
                    var createClosureMethod = Closure.CreateMethods[totalItemCount - 1];

                    var createClosure = createClosureMethod.MakeGenericMethod(constantTypes);

                    var closure = createClosure.Invoke(null, items);

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

                    ClosureObject = closure;
                    Fields        = fieldsArray;
                }
                else
                {
                    ClosureObject = new ArrayClosure(items);
                    IsArray       = true;
                }
            }