protected override Expression VisitConditionalExpression(ConditionalExpressionBindingParserNode node) { var condition = HandleErrors(node.ConditionExpression, n => TypeConversion.ImplicitConversion(Visit(n), typeof(bool), true)); var trueExpr = HandleErrors(node.TrueExpression, Visit); var falseExpr = HandleErrors(node.FalseExpression, Visit); ThrowOnErrors(); return(Expression.Condition(condition, trueExpr, falseExpr)); }
protected override Expression VisitBinary(BinaryExpression node) { Expression createExpr(Expression left) { return(CheckForNull(Visit(node.Right), right => Expression.MakeBinary(node.NodeType, left, right, false, node.Method, node.Conversion), checkReferenceTypes: false)); } if (node.NodeType.ToString().EndsWith("Assign")) { // only check for left target's null, assignment to null is perfectly valid if (node.Left is MemberExpression memberExpression) { return(CheckForNull(Visit(memberExpression.Expression), memberTarget => createExpr(memberExpression.Update(memberTarget)))); } else if (node.Left is IndexExpression indexer) { return(CheckForNull(Visit(indexer.Object), memberTarget => createExpr(indexer.Update(memberTarget, indexer.Arguments)))); } // this should only be ParameterExpression else { return(createExpr(node.Left)); } } else { if (true) { var left = Visit(node.Left); var right = Visit(node.Right); var nullable = left.Type.IsNullable() ? left.Type : right.Type; left = TypeConversion.ImplicitConversion(left, nullable); right = TypeConversion.ImplicitConversion(right, nullable); if (right != null && left != null) { return(Expression.MakeBinary(node.NodeType, left, right, left.Type.IsNullable() && node.NodeType != ExpressionType.Equal && node.NodeType != ExpressionType.NotEqual, node.Method)); } else { return(CheckForNull(base.Visit(node.Left), left2 => createExpr(left2), checkReferenceTypes: false)); } } else { } } }
public CastedExpressionBindingProperty ConvertExpressionToType(ParsedExpressionBindingProperty expr, ExpectedTypeBindingProperty expectedType = null) { var destType = expectedType?.Type ?? typeof(object); var convertedExpr = TypeConversion.ImplicitConversion(expr.Expression, destType, throwException: false, allowToString: true); return(new CastedExpressionBindingProperty( // if the expression is of type object (i.e. null literal) try the lambda conversion. convertedExpr != null && expr.Expression.Type != typeof(object) ? convertedExpr : TypeConversion.MagicLambdaConversion(expr.Expression, destType) ?? TypeConversion.ImplicitConversion(expr.Expression, destType, throwException: true, allowToString: true) )); }
protected override Expression VisitConditional(ConditionalExpression node) { return(CheckForNull(Visit(node.Test), test => { var ifTrue = Visit(node.IfTrue); var ifFalse = Visit(node.IfFalse); if (ifTrue.Type != ifFalse.Type) { var nullable = ifTrue.Type.IsNullable() ? ifTrue.Type : ifFalse.Type; ifTrue = TypeConversion.ImplicitConversion(ifTrue, nullable); ifFalse = TypeConversion.ImplicitConversion(ifFalse, nullable); } return Expression.Condition(test, ifTrue, ifFalse); })); }
protected override Expression VisitConditionalExpression(ConditionalExpressionBindingParserNode node) { var condition = HandleErrors(node.ConditionExpression, n => TypeConversion.ImplicitConversion(Visit(n), typeof(bool), true)); var trueExpr = HandleErrors(node.TrueExpression, Visit); var falseExpr = HandleErrors(node.FalseExpression, Visit); ThrowOnErrors(); if (trueExpr.Type != falseExpr.Type) { trueExpr = TypeConversion.ImplicitConversion(trueExpr, falseExpr.Type, allowToString: true) ?? trueExpr; falseExpr = TypeConversion.ImplicitConversion(falseExpr, trueExpr.Type, allowToString: true) ?? falseExpr; } return(Expression.Condition(condition, trueExpr, falseExpr)); }
public static Expression GetBinaryOperator(Expression left, Expression right, ExpressionType operation) { if (operation == ExpressionType.Coalesce) { return(Expression.Coalesce(left, right)); } if (operation == ExpressionType.Assign) { return(Expression.Assign(left, TypeConversion.ImplicitConversion(right, left.Type, true, true))); } // TODO: type conversions if (operation == ExpressionType.AndAlso) { return(Expression.AndAlso(left, right)); } else if (operation == ExpressionType.OrElse) { return(Expression.OrElse(left, right)); } var binder = (DynamicMetaObjectBinder)Microsoft.CSharp.RuntimeBinder.Binder.BinaryOperation( CSharpBinderFlags.None, operation, typeof(object), GetBinderArguments(2)); var result = ApplyBinder(binder, false, left, right); if (result != null) { return(result); } if (operation == ExpressionType.Equal) { return(EqualsMethod(left, right)); } if (operation == ExpressionType.NotEqual) { return(Expression.Not(EqualsMethod(left, right))); } // lift the operator if (left.Type.IsNullable() || right.Type.IsNullable()) { return(GetBinaryOperator(left.UnwrapNullable(), right.UnwrapNullable(), operation)); } throw new Exception($"could not apply { operation } binary operator to { left } and { right }"); // TODO: comparison operators }
public static Expression EqualsMethod(Expression left, Expression right) { Expression equatable = null; Expression theOther = null; if (typeof(IEquatable <>).IsAssignableFrom(left.Type)) { equatable = left; theOther = right; } else if (typeof(IEquatable <>).IsAssignableFrom(right.Type)) { equatable = right; theOther = left; } if (equatable != null) { var m = CallMethod(equatable, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy, "Equals", null, new[] { theOther }); if (m != null) { return(m); } } if (left.Type.GetTypeInfo().IsValueType) { equatable = left; theOther = right; } else if (left.Type.GetTypeInfo().IsValueType) { equatable = right; theOther = left; } if (equatable != null) { theOther = TypeConversion.ImplicitConversion(theOther, equatable.Type); if (theOther != null) { return(Expression.Equal(equatable, theOther)); } } return(CallMethod(left, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy, "Equals", null, new[] { right })); }
protected Expression CheckForNull(Expression parameter, Func <Expression, Expression> callback, bool checkReferenceTypes = true, bool suppress = false) { if (suppress || parameter == null || (parameter.Type.GetTypeInfo().IsValueType&& !parameter.Type.IsNullable()) || !checkReferenceTypes && !parameter.Type.GetTypeInfo().IsValueType) { return(callback(parameter)); } var p2 = Expression.Parameter(parameter.Type, "tmp" + tmpCounter++); var eresult = callback(p2.Type.IsNullable() ? (Expression)Expression.Property(p2, "Value") : p2); eresult = TypeConversion.ImplicitConversion(eresult, eresult.Type.MakeNullableType()); return(Expression.Block( new[] { p2 }, Expression.Assign(p2, parameter), Expression.Condition(parameter.Type.IsNullable() ? (Expression)Expression.Property(p2, "HasValue") : Expression.NotEqual(p2, Expression.Constant(null, p2.Type)), eresult, Expression.Default(eresult.Type)))); }
/// Performs conversions by wrapping or unwrapping results to/from <see cref="Task" /> public static Expression TaskConversion(Expression expr, Type expectedType) { if (typeof(Task).IsAssignableFrom(expectedType)) { if (!typeof(Task).IsAssignableFrom(expr.Type)) { // return dummy completed task if (expectedType == typeof(Task)) { return(Expression.Block(expr, Expression.Call(typeof(TaskUtils), "GetCompletedTask", Type.EmptyTypes))); } else if (expectedType.GetGenericTypeDefinition() == typeof(Task <>)) { var taskType = GetTaskType(expectedType); var converted = TypeConversion.ImplicitConversion(expr, taskType); if (converted != null) { return(Expression.Call(typeof(Task), "FromResult", new Type[] { taskType }, converted)); } else { return(null); } } else { return(null); } } else { return(null); } // TODO: convert Task<> to another Task<> } else { return(null); } }
/// This is a strange conversion that wraps the entire expression into a Lambda /// and makes an invokable delegate from a normal expression. /// It also replaces special ExtensionParameters attached to the expression for lambda parameters public static Expression MagicLambdaConversion(Expression expr, Type expectedType) { if (expectedType.IsDelegate()) { var resultType = expectedType.GetMethod("Invoke").ReturnType; var delegateArgs = expectedType .GetMethod("Invoke") .GetParameters() .Select(p => Expression.Parameter(p.ParameterType, p.Name)) .ToArray(); var convertedToResult = TypeConversion.ImplicitConversion(expr, resultType) ?? TaskConversion(expr, resultType); // TODO: convert delegates to another delegates if (convertedToResult == null) { return(null); } else { var replacedArgs = convertedToResult.ReplaceAll(arg => arg?.GetParameterAnnotation()?.ExtensionParameter is MagicLambdaConversionExtensionParameter extensionParam ? delegateArgs.Single(a => a.Name == extensionParam.Identifier) .Assert(p => p.Type == ResolvedTypeDescriptor.ToSystemType(extensionParam.ParameterType)) : arg ); return(Expression.Lambda( expectedType, replacedArgs, delegateArgs )); } } else { return(null); } }
public static Expression GetIndexer(Expression expr, Expression index) { if (expr.Type.IsArray) { return(Expression.ArrayIndex(expr, index)); } var indexProp = (from p in expr.Type.GetProperties(BindingFlags.Instance | BindingFlags.Public) let param = p.GetIndexParameters() where param.Length == 1 let indexConvert = TypeConversion.ImplicitConversion(index, param[0].ParameterType) where indexConvert != null select Expression.MakeIndex(expr, p, new[] { indexConvert })).ToArray(); if (indexProp.Length == 0) { throw new Exception($"could not find and indexer property on type { expr.Type } that accepts { index.Type } as argument"); } if (indexProp.Length > 1) { throw new Exception($"more than one indexer found on type { expr.Type } that accepts { index.Type } as argument"); } return(indexProp[0]); }
private static MethodRecognitionResult TryCallMethod(MethodInfo method, Type[] typeArguments, Expression[] positionalArguments, IDictionary <string, Expression> namedArguments) { var parameters = method.GetParameters(); int castCount = 0; if (parameters.Length < positionalArguments.Length) { return(null); } var args = new Expression[parameters.Length]; Array.Copy(positionalArguments, args, positionalArguments.Length); int namedArgCount = 0; for (int i = positionalArguments.Length; i < args.Length; i++) { if (namedArguments?.ContainsKey(parameters[i].Name) == true) { args[i] = namedArguments[parameters[i].Name]; namedArgCount++; } else if (parameters[i].HasDefaultValue) { castCount++; args[i] = Expression.Constant(parameters[i].DefaultValue, parameters[i].ParameterType); } else { return(null); } } // some named arguments were not used if (namedArguments != null && namedArgCount != namedArguments.Count) { return(null); } int automaticTypeArgs = 0; // resolve generic parameters if (method.ContainsGenericParameters) { var typeArgs = new Type[method.GetGenericArguments().Length]; if (typeArguments != null) { if (typeArguments.Length > typeArgs.Length) { return(null); } Array.Copy(typeArguments, typeArgs, typeArgs.Length); } for (int i = 0; i < typeArgs.Length; i++) { if (typeArgs[i] == null) { // try to resolve from arguments var arg = Array.FindIndex(parameters, p => p.ParameterType.IsGenericParameter && p.ParameterType.GenericParameterPosition == i); automaticTypeArgs++; if (arg >= 0) { typeArgs[i] = args[arg].Type; } else { return(null); } } } method = method.MakeGenericMethod(typeArgs); parameters = method.GetParameters(); } else if (typeArguments != null) { return(null); } // cast arguments for (int i = 0; i < args.Length; i++) { var casted = TypeConversion.ImplicitConversion(args[i], parameters[i].ParameterType); if (casted == null) { return(null); } if (casted != args[i]) { castCount++; args[i] = casted; } } return(new MethodRecognitionResult { CastCount = castCount, AutomaticTypeArgCount = automaticTypeArgs, Method = method, Arguments = args }); }
public CastedExpressionBindingProperty ConvertExpressionToType(ParsedExpressionBindingProperty expr, ExpectedTypeBindingProperty expectedType = null) => new CastedExpressionBindingProperty(TypeConversion.ImplicitConversion(expr.Expression, expectedType?.Type ?? typeof(object), throwException: true, allowToString: true));