/// <summary> /// If return type of the given <paramref name="expression"/> does not match <paramref name="type"/>, /// attempts to adjust it. Returns same or new Expression object. /// If supplied type is <c>null</c>, expression becomes Action. /// </summary> /// <param name="expression">Parse tree root</param> /// <param name="type">Desired return type, or null to produce an action</param> /// <exception cref="ArgumentNullException"><paramref name="expression"/> is null</exception> /// <exception cref="CompilationException">Could produce a safe adjustment</exception> public Expression AdjustReturnType(Expression expression, Type type) { if (expression == null) { throw new ArgumentNullException("expression"); } return(type != null?ExpressionTreeExtensions.AdjustReturnType(null, expression, type) : expression); }
private object CompileImpl(Expression expression, CompilerState state) { if (state == null) { throw new ArgumentNullException("state"); } state.RawReturnType = expression.Type; while (expression.CanReduce) { expression = expression.Reduce(); } LambdaExpression lambda; var paramTypes = state.CompileToAction ? new Type[state.Arguments.Count] : new Type[state.Arguments.Count + 1]; for (var i = 0; i < state.Arguments.Count; i++) { paramTypes[i] = state.Arguments[i].Type; } if (state.CompileToAction) { lambda = Expression.Lambda(Expression.GetActionType(paramTypes), expression, state.Arguments); } else { var returnType = state.ReturnType ?? state.RawReturnType; paramTypes[paramTypes.Length - 1] = returnType; expression = ExpressionTreeExtensions.AdjustReturnType(null, expression, returnType); lambda = Expression.Lambda(expression, state.Arguments); if (!ReferenceEquals(returnType, lambda.ReturnType)) { throw new CompilationException(string.Format("Expected return type: {0}. Real return type: {1}", returnType.FullName, lambda.ReturnType.FullName)); } } if (lambda.Parameters.Count != state.Arguments.Count) { throw new CompilationException(string.Format("Expected number of arguments: {0}. Real number: {1}", state.Arguments.Count, lambda.Parameters.Count)); } for (var i = 0; i < lambda.Parameters.Count; i++) { if (!ReferenceEquals(lambda.Parameters[i].Type, state.Arguments[i].Type)) { throw new CompilationException( string.Format("Expected type of argument {0}: {1}. Real type: {2}", i, state.Arguments[i].Type.FullName, lambda.Parameters[i].Type.FullName)); } } return(lambda.Compile()); }
private static Expression PredefinedAtom_ToDateTime(ParseTreeNode root, CompilerState state) { root.RequireChildren(2); var argNodes = root.RequireChild(null, 1, 0); if (argNodes.ChildNodes.Count == 3 || argNodes.ChildNodes.Count == 6 || argNodes.ChildNodes.Count == 7) { var args = new Expression[argNodes.ChildNodes.Count]; var typeArray = new Type[args.Length]; var typeInt32 = typeof(Int32); for (var i = 0; i < args.Length; i++) { var node = argNodes.RequireChild(null, i); var expr = ExpressionTreeExtensions.AdjustReturnType(node, state.ParentRuntime.Analyze(node, state), typeInt32); args[i] = expr; typeArray[i] = typeInt32; } var ctr = typeof(DateTime).GetConstructor(typeArray); if (ctr == null) { throw new Exception(string.Format("Could not locate datetime constructor with {0} int arguments", typeArray.Length)); } return(ConstantHelper.TryEvalConst(root, ctr, args)); } if (argNodes.ChildNodes.Count == 2) { var arg1Node = root.RequireChild(null, 1, 0, 0); var valueString = state.ParentRuntime.Analyze(arg1Node, state).RemoveNullability(); valueString.RequireString(arg1Node); var arg2Node = root.RequireChild(null, 1, 0, 1); var formatString = state.ParentRuntime.Analyze(arg2Node, state).RemoveNullability(); formatString.RequireString(arg2Node); return(ConstantHelper.TryEvalConst(root, ReflectionHelper.DateTimeParseExact, valueString, formatString, Expression.Constant(null, typeof(IFormatProvider)))); } if (argNodes.ChildNodes.Count == 1) { var arg1Node = root.RequireChild(null, 1, 0, 0); var value = state.ParentRuntime.Analyze(arg1Node, state).RemoveNullability(); value.RequireInteger(arg1Node); var dateTimeFromBinary = ReflectionHelper.GetOrAddMethod1(typeof(DateTime), "FromBinary", typeof(Int64)); return(ConstantHelper.TryEvalConst(root, dateTimeFromBinary, value)); } throw new CompilationException("ToDateTime has four overloads: with two, three, six and seven arguments", root); }
private Expression PredefinedAtom_IfNull(ParseTreeNode root, CompilerState state) { root.RequireChildren(2); var arg1Node = root.RequireChild(null, 1, 0, 0); var argument = state.ParentRuntime.Analyze(arg1Node, state); var isnullableType = argument.IsNullableType(); var arg2Node = root.RequireChild(null, 1, 0, 1); var ifnull = state.ParentRuntime.Analyze(arg2Node, state); if (!ExpressionTreeExtensions.TryAdjustVoid(ref argument, ref ifnull)) { throw new CompilationException("Could not adjust void blocks", root); } if (argument.IsVoid()) { return(ifnull); } ifnull = ExpressionTreeExtensions.AdjustReturnType(arg2Node, ifnull, argument.Type.GetUnderlyingType()); Expression result; if (argument.Type.IsValueType) { result = isnullableType ? Expression.Condition(Expression.Field(argument, "HasValue"), Expression.Field(argument, "Value"), ifnull) : argument; } else { // now only reference types remaining var constantExpression = argument as ConstantExpression; if (constantExpression != null) { result = ReferenceEquals(constantExpression.Value, null) ? ifnull : argument; } else { result = Expression.Condition(Expression.Equal(argument, Expression.Constant(null)), ifnull, argument); } } return(result.IsVoid() ? result : result.RemoveNullability()); }
private Expression BuildBetweenExpression(ParseTreeNode root, CompilerState state) { root.RequireChildren(6); var variableNode = root.ChildNodes[0]; var notOpt = root.RequireChild("notOpt", 1); root.RequireChild("between", 2); root.RequireChild("and", 4); var argument = Analyze(variableNode, state).RemoveNullability(); var leftNode = root.ChildNodes[3]; var leftExpr = Analyze(leftNode, state); leftExpr.RequireNonVoid(leftNode); leftExpr = ExpressionTreeExtensions.AdjustReturnType(leftNode, leftExpr, argument.Type); var rightNode = root.ChildNodes[5]; var rightExpr = Analyze(rightNode, state); rightExpr.RequireNonVoid(rightNode); rightExpr = ExpressionTreeExtensions.AdjustReturnType(rightNode, rightExpr, argument.Type); Expression lower, upper; if (argument.IsString()) { lower = ConstantHelper.TryEvalConst(leftNode, PrepareStringComparison(leftNode, leftExpr, argument), Expression.Constant(0), ExpressionType.LessThanOrEqual); upper = ConstantHelper.TryEvalConst(rightNode, PrepareStringComparison(rightNode, rightExpr, argument), Expression.Constant(0), ExpressionType.GreaterThanOrEqual); } else { lower = ConstantHelper.TryEvalConst(leftNode, argument, leftExpr, ExpressionType.GreaterThanOrEqual); upper = ConstantHelper.TryEvalConst(rightNode, argument, rightExpr, ExpressionType.LessThanOrEqual); } var result = ConstantHelper.TryEvalConst(root, lower, upper, ExpressionType.AndAlso); if (notOpt.ChildNodes.Count > 0) { result = ConstantHelper.TryEvalConst(root, result, ExpressionType.Not); } return(result); }