Beispiel #1
0
        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)));
        }
Beispiel #4
0
        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));
        }
Beispiel #5
0
        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));
            }
        }
Beispiel #7
0
        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());
        }
Beispiel #10
0
        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));
Beispiel #12
0
 public VelocityBinaryOperationBinder(VelocityOperator op, IOperatorResolver operatorResolver)
     : base(VelocityOperatorToExpressionType(op))
 {
     _operatorResolver = operatorResolver;
 }
Beispiel #13
0
 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);
 }
Beispiel #15
0
 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));
 }