public static bool TryEmit(this Expression expression, out Delegate resultDelegate, Type delegateType, Type returnType, params ParameterExpression[] parameters) { resultDelegate = null; var analyzer = new TreeAnalyzer(); if (!analyzer.Analyze(expression, parameters)) { return(false); } var capturedArgumentsType = analyzer.CapturedParameters.Length > 0 ? DynamicModule.GetOrAddCapturedArgumentsType(analyzer.CapturedParameters) : null; if (analyzer.NestedLambdas.Length > 0) { PrepareNestedLambdaTypes(analyzer, capturedArgumentsType); } var targetType = analyzer.StoredExpressions.Length > 0 ? DynamicModule.GetOrAddTargetType(analyzer.StoredObjectTypes) : null; var delegateTarget = targetType != null ? new DelegateTarget(targetType.GetTypeInfo().DeclaredFields.CastToArray(), targetType, Activator.CreateInstance(targetType, analyzer.StoredObjects)) : null; var capturedArgumentsHolder = capturedArgumentsType != null ? new CapturedArgumentsHolder(capturedArgumentsType.GetTypeInfo().DeclaredFields.CastToArray(), capturedArgumentsType) : null; var context = new CompilerContext(delegateTarget, analyzer.DefinedVariables, analyzer.StoredExpressions, analyzer.CapturedParameters, analyzer.NestedLambdas, capturedArgumentsHolder); var method = Emitter.CreateDynamicMethod(context, returnType, parameters); var generator = method.GetILGenerator(); if (context.HasCapturedVariablesArgument) { context.CapturedArgumentsHolderVariable = generator.PrepareCapturedArgumentsHolderVariable(capturedArgumentsType); generator.CopyParametersToCapturedArgumentsIfAny(context, parameters); } if (analyzer.DefinedVariables.Length > 0) { context.LocalBuilders = Emitter.BuildLocals(analyzer.DefinedVariables, generator); } if (!expression.TryEmit(generator, context, parameters)) { return(false); } generator.Emit(OpCodes.Ret); resultDelegate = context.HasClosure ? method.CreateDelegate(delegateType, context.Target.Target) : method.CreateDelegate(delegateType); return(true); }
public bool Analyze(Expression expression, params ParameterExpression[] parameters) { switch (expression.NodeType) { case ExpressionType.Parameter: if (parameters.ContainsElement(expression) || this.DefinedVariables.ContainsElement(expression)) { return(true); } this.AddCapturedParameter((ParameterExpression)expression); return(true); case ExpressionType.Lambda: var lambda = (LambdaExpression)expression; var analyzer = new TreeAnalyzer(); if (!analyzer.Analyze(lambda.Body, lambda.Parameters.CastToArray())) { return(false); } this.AddNestedLambda(new KeyValue <LambdaExpression, Expression[]>(lambda, analyzer.DefinedVariables)); var length = analyzer.StoredExpressions.Length; for (var i = 0; i < length; i++) { this.AddStoredItem(analyzer.StoredExpressions[i], analyzer.StoredExpressions[i].Type, analyzer.StoredObjects[i]); } var lambdaLength = analyzer.NestedLambdas.Length; for (var i = 0; i < lambdaLength; i++) { this.AddNestedLambda(analyzer.NestedLambdas[i]); } var capturedLength = analyzer.CapturedParameters.Length; for (var i = 0; i < capturedLength; i++) { this.AddCapturedParameter(analyzer.CapturedParameters[i]); } return(true); case ExpressionType.MemberAccess: return(this.Analyze(((MemberExpression)expression).Expression, parameters)); case ExpressionType.Constant: var constant = (ConstantExpression)expression; if (constant.Value == null || IsInPlaceEmittableConstant(constant.Type, constant.Value)) { return(true); } this.AddStoredItem(constant, constant.Type, constant.Value); return(true); case ExpressionType.New: return(this.Analyze(((NewExpression)expression).Arguments, parameters)); case ExpressionType.MemberInit: var memberInit = (MemberInitExpression)expression; if (!this.Analyze(memberInit.NewExpression, parameters)) { return(false); } return(this.Analyze(memberInit.Bindings, parameters)); case ExpressionType.Block: var block = (BlockExpression)expression; for (var i = 0; i < block.Variables.Count; i++) { this.AddDefinedVariable(block.Variables[i]); } return(this.Analyze(block.Expressions, parameters)); case ExpressionType.Conditional: var condition = (ConditionalExpression)expression; return(this.Analyze(condition.Test, parameters) && this.Analyze(condition.IfTrue, parameters) && this.Analyze(condition.IfFalse, parameters)); case ExpressionType.Default: return(true); case ExpressionType.Call: var call = (MethodCallExpression)expression; return((call.Object == null || this.Analyze(call.Object, parameters)) && this.Analyze(call.Arguments, parameters)); case ExpressionType.Invoke: var invoke = (InvocationExpression)expression; return(this.Analyze(invoke.Expression, parameters) && this.Analyze(invoke.Arguments, parameters)); case ExpressionType.NewArrayInit: return(this.Analyze(((NewArrayExpression)expression).Expressions, parameters)); default: if (expression is UnaryExpression unaryExpression) { return(this.Analyze(unaryExpression.Operand, parameters)); } if (expression is BinaryExpression binaryExpression) { return(this.Analyze(binaryExpression.Left, parameters) && this.Analyze(binaryExpression.Right, parameters)); } break; } return(false); }