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