Example #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));
        }
        public override DynamicMetaObject FallbackGetIndex(DynamicMetaObject target, DynamicMetaObject[] indexes, DynamicMetaObject errorSuggestion)
        {
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }
            if (indexes == null)
            {
                throw new ArgumentNullException(nameof(indexes));
            }

            if (!target.HasValue || indexes.Any(x => !x.HasValue))
            {
                return(Defer(target, indexes));
            }

            if (target.Value == null)
            {
                return(BinderHelper.NullTargetResult(target, errorSuggestion));
            }

            var index        = _indexerResolver.ReadableIndexer(target, indexes);
            var restrictions = BinderHelper.CreateCommonRestrictions(target, indexes);

            if (index == null)
            {
                return(BinderHelper.UnresolveableResult(restrictions, errorSuggestion));
            }

            return(new DynamicMetaObject(VelocityExpressions.ConvertIfNeeded(index, ReturnType), restrictions));
        }
Example #3
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));
        }
Example #4
0
        public override DynamicMetaObject FallbackInvokeMember(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion)
        {
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }
            if (args == null)
            {
                throw new ArgumentNullException(nameof(args));
            }

            if (!target.HasValue || args.Any(x => !x.HasValue))
            {
                return(Defer(target, args));
            }

            if (target == null)
            {
                return(BinderHelper.NullTargetResult(target, errorSuggestion));
            }


            // If an argument has a null value, use a null type so that the resolution algorithm can do implicit null conversions
            var argTypes = args.Select(x => x.Value == null ? null : x.RuntimeType).ToImmutableList();

            OverloadResolutionData <MethodInfo> method = _methodResolver.ResolveMethod(target.LimitType.GetTypeInfo(), Name, argTypes);

            var restrictions = BinderHelper.CreateCommonRestrictions(target, args);

            if (method == null)
            {
                var log = BindingEventSource.Log;
                if (log.IsEnabled())
                {
                    var argTypeString = string.Join(",", argTypes.Select(x => x.FullName).ToArray());
                    log.InvokeMemberResolutionFailure(Name, target.LimitType.FullName, argTypeString);
                }

                return(BinderHelper.UnresolveableResult(restrictions, errorSuggestion));
            }

            var methodExpression = _methodResolver.ConvertMethodParameters(method, target.Expression, args);

            return(new DynamicMetaObject(
                       VelocityExpressions.ConvertIfNeeded(methodExpression, ReturnType),
                       restrictions
                       ));
        }
        public override DynamicMetaObject FallbackSetMember(DynamicMetaObject target, DynamicMetaObject value, DynamicMetaObject errorSuggestion)
        {
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }
            if (value == null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            if (!target.HasValue || !value.HasValue)
            {
                return(Defer(target, value));
            }

            if (target.Value == null)
            {
                return(BinderHelper.NullTargetResult(target, errorSuggestion));
            }
            if (value.Value == null)
            {
                return(BinderHelper.SetNullValue(this, value));
            }


            var memberAccess = _memberResolver.MemberExpression(Name, target, MemberAccessMode.Write);

            var restrictions = BinderHelper.CreateCommonRestrictions(target);

            if (memberAccess == null || !memberAccess.Type.IsAssignableFrom(value.RuntimeType))
            {
                BindingEventSource.Log.SetMemberResolutionFailure(Name, target.RuntimeType.FullName, value.RuntimeType.FullName);
                return(BinderHelper.UnresolveableResult(restrictions, errorSuggestion));
            }

            var result = VelocityExpressions.BoxIfNeeded(
                Expression.Assign(
                    memberAccess,
                    VelocityExpressions.ConvertIfNeeded(value, memberAccess.Type)
                    )
                );

            return(new DynamicMetaObject(result, restrictions));
        }
Example #6
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));
        }
Example #7
0
        public override DynamicMetaObject FallbackSetIndex(DynamicMetaObject target, DynamicMetaObject[] indexes, DynamicMetaObject value, DynamicMetaObject errorSuggestion)
        {
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }
            if (indexes == null)
            {
                throw new ArgumentNullException(nameof(indexes));
            }
            if (indexes == null)
            {
                throw new ArgumentNullException(nameof(indexes));
            }

            if (!target.HasValue || !value.HasValue || indexes.Any(x => !x.HasValue))
            {
                return(Defer(target, indexes.Concat(new[] { value }).ToArray()));
            }

            if (target.Value == null)
            {
                return(BinderHelper.NullTargetResult(target, errorSuggestion));
            }
            if (value.Value == null)
            {
                return(BinderHelper.SetNullValue(this, value));
            }

            var restrictions = BinderHelper.CreateCommonRestrictions(target, indexes)
                               .Merge(BinderHelper.CreateCommonRestrictions(value));

            var index = _indexerResolver.WriteableIndexer(target, indexes);

            if (index == null || !index.Type.IsAssignableFrom(value.RuntimeType))
            {
                return(BinderHelper.UnresolveableResult(restrictions, errorSuggestion));
            }

            var assignment = Expression.Assign(index, VelocityExpressions.ConvertIfNeeded(value.Expression, index.Type));

            return(new DynamicMetaObject(VelocityExpressions.ConvertIfNeeded(assignment, ReturnType), restrictions));
        }
Example #8
0
        public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
        {
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            if (!target.HasValue)
            {
                return(Defer(target));
            }

            if (target.Value == null)
            {
                return(BinderHelper.NullTargetResult(target, errorSuggestion));
            }

            Expression result       = null;
            var        restrictions = BinderHelper.CreateCommonRestrictions(target);

            try
            {
                result = _memberResolver.MemberExpression(Name, target, MemberAccessMode.Read);
            }
            catch (AmbiguousMatchException)
            {
                BindingEventSource.Log.GetMemberResolutionAmbiguous(Name, target.RuntimeType.FullName);
                return(BinderHelper.UnresolveableResult(restrictions, errorSuggestion));
            }


            if (result == null)
            {
                BindingEventSource.Log.GetMemberResolutionFailure(Name, target.RuntimeType.FullName);
                return(BinderHelper.UnresolveableResult(restrictions, errorSuggestion));
            }

            return(new DynamicMetaObject(
                       VelocityExpressions.ConvertIfNeeded(result, ReturnType),
                       restrictions
                       ));
        }
Example #9
0
        public override DynamicMetaObject FallbackBinaryOperation(DynamicMetaObject target, DynamicMetaObject arg, DynamicMetaObject errorSuggestion)
        {
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }
            if (arg == null)
            {
                throw new ArgumentNullException(nameof(arg));
            }

            if (!target.HasValue || !arg.HasValue)
            {
                return(Defer(target, arg));
            }

            DynamicMetaObject result;

            switch (Operation)
            {
            case ExpressionType.Add:
                result = AddSubtractMultiply(VelocityOperator.Add, target, arg);
                break;

            case ExpressionType.Subtract:
                result = AddSubtractMultiply(VelocityOperator.Subtract, target, arg);
                break;

            case ExpressionType.Multiply:
                result = AddSubtractMultiply(VelocityOperator.Multiply, target, arg);
                break;

            case ExpressionType.Divide:
                result = Division(VelocityOperator.Divide, target, arg);
                break;

            case ExpressionType.Modulo:
                result = Division(VelocityOperator.Modulo, target, arg);
                break;

            case ExpressionType.Equal:
                result = Equality(VelocityOperator.Equal, target, arg, errorSuggestion);
                break;

            case ExpressionType.NotEqual:
                result = Equality(VelocityOperator.NotEqual, target, arg, errorSuggestion);
                break;

            case ExpressionType.GreaterThan:
                result = Relational(VelocityOperator.GreaterThan, target, arg, errorSuggestion);
                break;

            case ExpressionType.GreaterThanOrEqual:
                result = Relational(VelocityOperator.GreaterThanOrEqual, target, arg, errorSuggestion);
                break;

            case ExpressionType.LessThan:
                result = Relational(VelocityOperator.LessThan, target, arg, errorSuggestion);
                break;

            case ExpressionType.LessThanOrEqual:
                result = Relational(VelocityOperator.LessThanOrEqual, target, arg, errorSuggestion);
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(Operation));
            }

            if (result == null)
            {
                var restrictions = BinderHelper.CreateCommonRestrictions(target, arg);
                BindingEventSource.Log.BinaryOperationResolutionFailure(Operation, target.LimitType.FullName, arg.LimitType.FullName);
                result = BinderHelper.UnresolveableResult(restrictions, errorSuggestion);
            }
            return(result);
        }
Example #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));
        }