private DynamicMetaObject Equality(VelocityOperator operatorType, DynamicMetaObject left, DynamicMetaObject right, DynamicMetaObject errorSuggestion) { var op = _operatorResolver.Resolve(operatorType, left.RuntimeType, right.RuntimeType); Expression result; if (op != null) { var leftExpr = VelocityExpressions.ConvertIfNeeded(left.Expression, op.Parameters[0]); var rightExpr = VelocityExpressions.ConvertIfNeeded(right.Expression, op.Parameters[1]); result = operatorType == VelocityOperator.Equal ? Expression.Equal(leftExpr, rightExpr, false, op.FunctionMember) : Expression.NotEqual(leftExpr, rightExpr, false, op.FunctionMember); } else if (left.RuntimeType == typeof(string) && (right.RuntimeType?.IsEnum ?? false)) { var method = MethodHelpers.GenericStringEnumComparer.MakeGenericMethod(right.RuntimeType); result = Expression.Equal(right.Expression, left.Expression, false, method); if (operatorType == VelocityOperator.NotEqual) { result = Expression.Not(result); } } else if (right.RuntimeType == typeof(string) && (left.RuntimeType?.IsEnum ?? false)) { var method = MethodHelpers.GenericStringEnumComparer.MakeGenericMethod(left.RuntimeType); result = Expression.Equal(left.Expression, right.Expression, false, method); if (operatorType == VelocityOperator.NotEqual) { result = Expression.Not(result); } } else if (errorSuggestion != null) { return(null); } else if (left.LimitType.IsAssignableFrom(right.LimitType) || right.LimitType.IsAssignableFrom(left.LimitType)) { var leftExpr = VelocityExpressions.ConvertIfNeeded(left.Expression, typeof(object)); var rightExpr = VelocityExpressions.ConvertIfNeeded(right.Expression, typeof(object)); result = Expression.Equal(leftExpr, rightExpr, false, MethodHelpers.ObjectEquals); if (operatorType == VelocityOperator.NotEqual) { result = Expression.Not(result); } } else { result = operatorType == VelocityOperator.Equal ? Constants.False : Constants.True; } var restrictions = BinderHelper.CreateCommonRestrictions(left, right); result = VelocityExpressions.ConvertIfNeeded(result, ReturnType); return(new DynamicMetaObject(result, restrictions)); }
private void ComparisonTest(object left, object right, VelocityOperator operation, bool?expected, string message = null) { var binder = new VelocityBinaryOperationBinder(operation, new OperatorResolver(new OverloadResolver(new ArgumentConverter()))); var result = InvokeBinder(binder, left, right); Assert.AreEqual(expected, result); }
public OverloadResolutionData <MethodInfo> Resolve(VelocityOperator operatorType, Type left, Type right) { var candidates = GetCandidateUserDefinedOperators(operatorType, left, right); if (!candidates.Any()) { candidates = GetBuiltInOperators(operatorType); } return(_overloadResolver.Resolve(candidates, ImmutableList.Create(left, right))); }
private DynamicMetaObject Relational(VelocityOperator operatorType, DynamicMetaObject left, DynamicMetaObject right, DynamicMetaObject errorSuggestion) { var op = _operatorResolver.Resolve(operatorType, left.RuntimeType, right.RuntimeType); if (op == null) { if (operatorType == VelocityOperator.GreaterThanOrEqual || operatorType == VelocityOperator.LessThanOrEqual) { return(Equality(VelocityOperator.Equal, left, right, errorSuggestion)); } else { return(null); } } var leftExpr = VelocityExpressions.ConvertIfNeeded(left.Expression, op.Parameters[0]); var rightExpr = VelocityExpressions.ConvertIfNeeded(right.Expression, op.Parameters[1]); Expression result; switch (operatorType) { case VelocityOperator.GreaterThan: result = Expression.GreaterThan(leftExpr, rightExpr, false, op.FunctionMember); break; case VelocityOperator.GreaterThanOrEqual: result = Expression.GreaterThanOrEqual(leftExpr, rightExpr, false, op.FunctionMember); break; case VelocityOperator.LessThan: result = Expression.LessThan(leftExpr, rightExpr, false, op.FunctionMember); break; case VelocityOperator.LessThanOrEqual: result = Expression.LessThanOrEqual(leftExpr, rightExpr, false, op.FunctionMember); break; default: throw new ArgumentOutOfRangeException(nameof(Operation)); } var restrictions = BinderHelper.CreateCommonRestrictions(left, right); result = VelocityExpressions.ConvertIfNeeded(result, ReturnType); return(new DynamicMetaObject(result, restrictions)); }
private DynamicMetaObject AddSubtractMultiply(VelocityOperator operatorType, DynamicMetaObject left, DynamicMetaObject right) { var op = _operatorResolver.Resolve(operatorType, left.RuntimeType, right.RuntimeType); if (op == null) { return(null); } var targetExpr = VelocityExpressions.ConvertIfNeeded(left, op.Parameters[0]); var argExpr = VelocityExpressions.ConvertIfNeeded(right, op.Parameters[1]); Func <Expression, Expression, MethodInfo, Expression> operationFunc; switch (operatorType) { case VelocityOperator.Add: operationFunc = Expression.AddChecked; break; case VelocityOperator.Subtract: operationFunc = Expression.SubtractChecked; break; case VelocityOperator.Multiply: operationFunc = Expression.MultiplyChecked; break; default: throw new InvalidOperationException(); } Expression operation = operationFunc(targetExpr, argExpr, op.FunctionMember); if (op.FunctionMember == null) { operation = AddOverflowHandling(operation, left.Expression, right.Expression); } operation = VelocityExpressions.ConvertIfNeeded(operation, ReturnType); var restrictions = BinderHelper.CreateCommonRestrictions(left, right); return(new DynamicMetaObject(operation, restrictions)); }
private string GetOperatorName(VelocityOperator operation) { switch (operation) { case VelocityOperator.Add: return(AdditionMethodName); case VelocityOperator.Subtract: return(SubtractionMethodName); case VelocityOperator.Multiply: return(MultiplicationMethodName); case VelocityOperator.Divide: return(DivisionMethodName); case VelocityOperator.Modulo: return(ModuloMethodName); case VelocityOperator.Equal: return(EqualityMethodName); case VelocityOperator.NotEqual: return(InequalityMethodName); case VelocityOperator.GreaterThan: return(GreaterThanMethodName); case VelocityOperator.GreaterThanOrEqual: return(GreaterThanOrEqualMethodName); case VelocityOperator.LessThan: return(LessThanMethodName); case VelocityOperator.LessThanOrEqual: return(LessThanOrEqualMethodName); default: throw new ArgumentOutOfRangeException(nameof(operation)); } }
private static ExpressionType VelocityOperatorToExpressionType(VelocityOperator op) { switch (op) { case VelocityOperator.Equal: return(ExpressionType.Equal); case VelocityOperator.NotEqual: return(ExpressionType.NotEqual); case VelocityOperator.GreaterThan: return(ExpressionType.GreaterThan); case VelocityOperator.GreaterThanOrEqual: return(ExpressionType.GreaterThanOrEqual); case VelocityOperator.LessThan: return(ExpressionType.LessThan); case VelocityOperator.LessThanOrEqual: return(ExpressionType.LessThanOrEqual); case VelocityOperator.Add: return(ExpressionType.Add); case VelocityOperator.Subtract: return(ExpressionType.Subtract); case VelocityOperator.Multiply: return(ExpressionType.Multiply); case VelocityOperator.Divide: return(ExpressionType.Divide); case VelocityOperator.Modulo: return(ExpressionType.Modulo); default: throw new ArgumentOutOfRangeException(nameof(op)); } }
private IImmutableList <FunctionMemberData <MethodInfo> > GetBuiltInOperators(VelocityOperator operation) { switch (operation) { case VelocityOperator.Add: return(_builtInAdditionOperators); case VelocityOperator.Subtract: return(_builtInSubtractionOperators); case VelocityOperator.Multiply: return(_builtInMultiplicationOperators); case VelocityOperator.Divide: return(_builtInDivisionOperators); case VelocityOperator.Modulo: return(_builtInModulusOperators); case VelocityOperator.Equal: return(_builtInEqualityOperators); case VelocityOperator.NotEqual: return(_builtInInequalityOperators); case VelocityOperator.GreaterThan: return(_builtInGreaterThanOperators); case VelocityOperator.GreaterThanOrEqual: return(_builtInGreaterThanOrEqualOperators); case VelocityOperator.LessThan: return(_builtInLessThanOperators); case VelocityOperator.LessThanOrEqual: return(_builtInLessThanOrEqualOperators); default: throw new ArgumentOutOfRangeException(nameof(operation)); } }
private IImmutableList <FunctionMemberData <MethodInfo> > GetCandidateUserDefinedOperators(VelocityOperator operatorType, Type left, Type right) { var operatorName = GetOperatorName(operatorType); // The set of candidate user-defined operators provided by X and Y for the operation operator op(x, y) is determined. //The set consists of the union of the candidate operators provided by X and the candidate operators provided by Y var candidates = GetCandidateUserDefinedOperatorsForType(operatorName, left); //If X and Y are the same type, or if X and Y are derived from a common base type, then shared candidate operators only occur in the combined set once. if (left != right) { candidates = candidates.Union(GetCandidateUserDefinedOperatorsForType(operatorName, right)); } return(candidates.Select(x => new FunctionMemberData <MethodInfo>(x, x.GetParameters())) .ToImmutableList()); }
private DynamicMetaObject Division(VelocityOperator operatorType, DynamicMetaObject left, DynamicMetaObject right) { var op = _operatorResolver.Resolve(operatorType, left.RuntimeType, right.RuntimeType); if (op == null) { return(null); } var restrictions = BinderHelper.CreateCommonRestrictions(left, right); Expression result = null; // For signed integers, Division can fail in two ways // 1. Divide by Zero: RHS = 0 (Divide by Zero) // 2. Overflow - RHS = -1 && LHS = int.MinValue (or long.MinValue) // Rather than catching an exception as we do for Add & Subtract, we can detect these cases directly, avoiding // exception handling // Floating-point division can't fail // Decimal division can overflow, so needs an exception handler if (op.FunctionMember == null) { //Divide by Zero var argZero = GetZeroExpression(right); if (argZero != null) { BindingRestrictions zeroRestriction; if (argZero.Value.Equals(right.Value)) { zeroRestriction = BindingRestrictions.GetExpressionRestriction(Expression.Equal(argZero, VelocityExpressions.ConvertIfNeeded(right.Expression, argZero.Type))); result = Constants.VelocityUnresolvableResult; } else { zeroRestriction = BindingRestrictions.GetExpressionRestriction(Expression.NotEqual(argZero, VelocityExpressions.ConvertIfNeeded(right.Expression, argZero.Type))); } restrictions = restrictions.Merge(zeroRestriction); } //Overflow ConstantExpression lhsMax = null; if (left.RuntimeType == typeof(int)) { lhsMax = _maxInt; } if (left.RuntimeType == typeof(long)) { lhsMax = _maxLong; } if (lhsMax != null) { var negativeOne = GetNegativeOneExpression(right); if (negativeOne != null) { BindingRestrictions overflowRestrictions; if (left.Value.Equals(lhsMax.Value) && right.Value.Equals(negativeOne.Value)) { result = Constants.VelocityUnresolvableResult; overflowRestrictions = BindingRestrictions.GetExpressionRestriction( Expression.AndAlso( Expression.Equal(left.Expression, lhsMax), Expression.Equal(right.Expression, negativeOne) ) ); } else { overflowRestrictions = BindingRestrictions.GetExpressionRestriction( Expression.OrElse( Expression.NotEqual(lhsMax, VelocityExpressions.ConvertIfNeeded(left.Expression, lhsMax.Type)), Expression.NotEqual(negativeOne, VelocityExpressions.ConvertIfNeeded(right.Expression, lhsMax.Type)) ) ); } restrictions = restrictions.Merge(overflowRestrictions); } } } if (result == null) { var targetExpr = VelocityExpressions.ConvertIfNeeded(left, op.Parameters[0]); var argExpr = VelocityExpressions.ConvertIfNeeded(right, op.Parameters[1]); if (Operation == ExpressionType.Divide) { result = Expression.Divide(targetExpr, argExpr, op.FunctionMember); } else if (Operation == ExpressionType.Modulo) { result = Expression.Modulo(targetExpr, argExpr, op.FunctionMember); } else { throw new InvalidOperationException(); } } if (op.Parameters[0] == typeof(decimal) && op.Parameters[1] == typeof(decimal)) { result = Expression.TryCatch( VelocityExpressions.ConvertIfNeeded(result, ReturnType), Expression.Catch(typeof(ArithmeticException), Constants.VelocityUnresolvableResult ) ); } var operation = VelocityExpressions.ConvertIfNeeded(result, ReturnType); return(new DynamicMetaObject(operation, restrictions)); }
public virtual Expression Binary(Expression left, Expression right, SourceInfo sourceInfo, VelocityOperator type) => new BinaryOperationExpression(left, right, sourceInfo, _binderFactory.GetBinaryOperationBinder(type));
public VelocityBinaryOperationBinder(VelocityOperator op, IOperatorResolver operatorResolver) : base(VelocityOperatorToExpressionType(op)) { _operatorResolver = operatorResolver; }
public BinaryOperationBinder GetBinaryOperationBinder(VelocityOperator type) => new VelocityBinaryOperationBinder(type, _operatorResolver);
public void Test(object left, object right, VelocityOperator operation, bool?expected) { ComparisonTest(left, right, operation, expected); }
public BinaryOperationBinder GetBinaryOperationBinder(VelocityOperator type) => _binaryOperationBinders.GetOrAdd(type, _rawBinderFactory.GetBinaryOperationBinder);
public BinaryOperationBinder GetBinaryOperationBinder(VelocityOperator op) { throw new NotImplementedException(); }
public override Expression Binary(Expression left, Expression right, SourceInfo sourceInfo, VelocityOperator type) { if (IsConstantType(left) && IsConstantType(right)) { //TODO: If types are static, we can optimise here with a static expression, rather than dynamic } return(base.Binary(left, right, sourceInfo, type)); }