Esempio n. 1
0
        [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]     // bogus since the casts are in different case statements
        internal static MethodInfo MapOperatorToMethod(
            CodeBinaryOperatorType op,
            Type lhs,
            CodeExpression lhsExpression,
            Type rhs,
            CodeExpression rhsExpression,
            RuleValidation validator,
            out ValidationError error)
        {
            // determine what the method name should be
            string methodName;
            string message;
            OperatorGrouping group;

            switch (op)
            {
                case CodeBinaryOperatorType.ValueEquality:
                    methodName = "op_Equality";
                    group = OperatorGrouping.Equality;
                    break;
                case CodeBinaryOperatorType.GreaterThan:
                    methodName = "op_GreaterThan";
                    group = OperatorGrouping.Relational;
                    break;
                case CodeBinaryOperatorType.GreaterThanOrEqual:
                    methodName = "op_GreaterThanOrEqual";
                    group = OperatorGrouping.Relational;
                    break;
                case CodeBinaryOperatorType.LessThan:
                    methodName = "op_LessThan";
                    group = OperatorGrouping.Relational;
                    break;
                case CodeBinaryOperatorType.LessThanOrEqual:
                    methodName = "op_LessThanOrEqual";
                    group = OperatorGrouping.Relational;
                    break;
                case CodeBinaryOperatorType.Add:
                    methodName = "op_Addition";
                    group = OperatorGrouping.Arithmetic;
                    break;
                case CodeBinaryOperatorType.Subtract:
                    methodName = "op_Subtraction";
                    group = OperatorGrouping.Arithmetic;
                    break;
                case CodeBinaryOperatorType.Multiply:
                    methodName = "op_Multiply";
                    group = OperatorGrouping.Arithmetic;
                    break;
                case CodeBinaryOperatorType.Divide:
                    methodName = "op_Division";
                    group = OperatorGrouping.Arithmetic;
                    break;
                case CodeBinaryOperatorType.Modulus:
                    methodName = "op_Modulus";
                    group = OperatorGrouping.Arithmetic;
                    break;
                case CodeBinaryOperatorType.BitwiseAnd:
                    methodName = "op_BitwiseAnd";
                    group = OperatorGrouping.Arithmetic;
                    break;
                case CodeBinaryOperatorType.BitwiseOr:
                    methodName = "op_BitwiseOr";
                    group = OperatorGrouping.Arithmetic;
                    break;
                default:
                    Debug.Assert(false, "Operator " + op.ToString() + " not implemented");
                    message = string.Format(CultureInfo.CurrentCulture, Messages.BinaryOpNotSupported, op.ToString());
                    error = new ValidationError(message, ErrorNumbers.Error_CodeExpressionNotHandled);
                    return null;
            }

            // NOTE: types maybe NullLiteral, which signifies the constant "null"
            List<MethodInfo> candidates = new List<MethodInfo>();
            bool lhsNullable = ConditionHelper.IsNullableValueType(lhs);
            bool rhsNullable = ConditionHelper.IsNullableValueType(rhs);
            Type lhsType0 = (lhsNullable) ? Nullable.GetUnderlyingType(lhs) : lhs;
            Type rhsType0 = (rhsNullable) ? Nullable.GetUnderlyingType(rhs) : rhs;

            // special cases for enums
            if (lhsType0.IsEnum)
            {
                // only 3 cases (U = underlying type of E):
                //    E = E + U
                //    U = E - E
                //    E = E - U
                // plus the standard comparisons (E == E, E > E, etc.)
                // need to also allow E == 0
                Type underlyingType;
                switch (op)
                {
                    case CodeBinaryOperatorType.Add:
                        underlyingType = EnumHelper.GetUnderlyingType(lhsType0);
                        if ((underlyingType != null) &&
                            (RuleValidation.TypesAreAssignable(rhsType0, underlyingType, rhsExpression, out error)))
                        {
                            error = null;
                            return new EnumOperationMethodInfo(lhs, op, rhs, false);
                        }
                        break;
                    case CodeBinaryOperatorType.Subtract:
                        underlyingType = EnumHelper.GetUnderlyingType(lhsType0);
                        if (underlyingType != null)
                        {
                            if (lhsType0 == rhsType0)
                            {
                                // E - E
                                error = null;
                                return new EnumOperationMethodInfo(lhs, op, rhs, false);
                            }
                            else if (DecimalIntegerLiteralZero(rhs, rhsExpression as CodePrimitiveExpression))
                            {
                                // E - 0, can convert 0 to E
                                error = null;
                                return new EnumOperationMethodInfo(lhs, op, rhs, true);
                            }
                            else if (RuleValidation.TypesAreAssignable(rhsType0, underlyingType, rhsExpression, out error))
                            {
                                // expression not passed to TypesAreAssignable, so not looking for constants (since 0 is all we care about)
                                error = null;
                                return new EnumOperationMethodInfo(lhs, op, rhs, false);
                            }
                        }
                        break;
                    case CodeBinaryOperatorType.ValueEquality:
                    case CodeBinaryOperatorType.LessThan:
                    case CodeBinaryOperatorType.LessThanOrEqual:
                    case CodeBinaryOperatorType.GreaterThan:
                    case CodeBinaryOperatorType.GreaterThanOrEqual:
                        if (lhsType0 == rhsType0)
                        {
                            error = null;
                            return new EnumOperationMethodInfo(lhs, op, rhs, false);
                        }
                        else if (lhsNullable && (rhs == typeof(NullLiteral)))
                        {
                            // handle enum? op null
                            // treat the rhs as the same nullable enum
                            error = null;
                            return new EnumOperationMethodInfo(lhs, op, lhs, false);
                        }
                        else if (DecimalIntegerLiteralZero(rhs, rhsExpression as CodePrimitiveExpression))
                        {
                            error = null;
                            return new EnumOperationMethodInfo(lhs, op, rhs, true);
                        }
                        break;
                }
                // can't do it, sorry
                // but check if there is a user-defined operator that works
            }
            else if (rhsType0.IsEnum)
            {
                // lhs != enum, so only 2 cases (U = underlying type of E):
                //    E = U + E
                //    E = U - E
                // comparisons are E == E, etc., so if the lhs is not an enum, too bad
                // although we need to check for 0 == E
                Type underlyingType;
                switch (op)
                {
                    case CodeBinaryOperatorType.Add:
                        underlyingType = EnumHelper.GetUnderlyingType(rhsType0);
                        if ((underlyingType != null) &&
                            (RuleValidation.TypesAreAssignable(lhsType0, underlyingType, lhsExpression, out error)))
                        {
                            error = null;
                            return new EnumOperationMethodInfo(lhs, op, rhs, false);
                        }
                        break;

                    case CodeBinaryOperatorType.Subtract:
                        underlyingType = EnumHelper.GetUnderlyingType(rhsType0);
                        if (underlyingType != null)
                        {
                            CodePrimitiveExpression primitive = lhsExpression as CodePrimitiveExpression;
                            if (DecimalIntegerLiteralZero(lhs, primitive))
                            {
                                // 0 - E, can convert 0 to E
                                error = null;
                                return new EnumOperationMethodInfo(lhs, op, rhs, true);
                            }
                            else if (RuleValidation.TypesAreAssignable(lhsType0, underlyingType, lhsExpression, out error))
                            {
                                // expression not passed to TypesAreAssignable, so not looking for constants (since 0 is all we care about)
                                error = null;
                                return new EnumOperationMethodInfo(lhs, op, rhs, false);
                            }
                        }
                        break;

                    case CodeBinaryOperatorType.ValueEquality:
                    case CodeBinaryOperatorType.LessThan:
                    case CodeBinaryOperatorType.LessThanOrEqual:
                    case CodeBinaryOperatorType.GreaterThan:
                    case CodeBinaryOperatorType.GreaterThanOrEqual:
                        if (rhsNullable && (lhs == typeof(NullLiteral)))
                        {
                            // handle null op enum?
                            // treat the lhs as the same nullable enum type
                            error = null;
                            return new EnumOperationMethodInfo(rhs, op, rhs, false);
                        }
                        else if (DecimalIntegerLiteralZero(lhs, lhsExpression as CodePrimitiveExpression))
                        {
                            error = null;
                            return new EnumOperationMethodInfo(lhs, op, rhs, true);
                        }
                        break;
                }

                // can't do it, sorry
                // but check if there is a user-defined operator that works
            }

            // enum specific operations already handled, see if one side (or both) define operators
            AddOperatorOverloads(lhsType0, methodName, lhs, rhs, candidates);
            AddOperatorOverloads(rhsType0, methodName, lhs, rhs, candidates);
            if (lhsNullable || rhsNullable || (lhs == typeof(NullLiteral)) || (rhs == typeof(NullLiteral)))
            {
                // need to add in lifted methods
                AddLiftedOperators(lhsType0, methodName, group, lhsType0, rhsType0, candidates);
                AddLiftedOperators(rhsType0, methodName, group, lhsType0, rhsType0, candidates);
            }

            if (candidates.Count == 0)
            {
                // no overrides, so get the default list
                methodName = methodName.Substring(3);       // strip off the op_
                foreach (MethodInfo mi in typeof(DefaultOperators).GetMethods())
                {
                    if (mi.Name == methodName)
                    {
                        ParameterInfo[] parameters = mi.GetParameters();
                        Type parm1 = parameters[0].ParameterType;
                        Type parm2 = parameters[1].ParameterType;
                        if (RuleValidation.ImplicitConversion(lhs, parm1) &&
                            RuleValidation.ImplicitConversion(rhs, parm2))
                        {
                            candidates.Add(mi);
                        }
                    }
                }

                // if no candidates and ==, can we use object == object?
                if ((candidates.Count == 0) && ("Equality" == methodName))
                {
                    // C# 7.9.6
                    // references must be compatible
                    // no boxing
                    // value types can't be compared
                    if ((!lhs.IsValueType) && (!rhs.IsValueType))
                    {
                        // they are not classes, so references need to be compatible
                        // also check for null (which is NullLiteral type) -- null is compatible with any object type
                        if ((lhs == typeof(NullLiteral)) || (rhs == typeof(NullLiteral)) ||
                            (lhs.IsAssignableFrom(rhs)) || (rhs.IsAssignableFrom(lhs)))
                        {
                            candidates.Add(ObjectEquality);
                        }
                    }
                }

                // if no candidates and nullable, add lifted operators
                if ((candidates.Count == 0) && ((lhsNullable || rhsNullable || (lhs == typeof(NullLiteral)) || (rhs == typeof(NullLiteral)))))
                {
                    foreach (MethodInfo mi in typeof(DefaultOperators).GetMethods())
                    {
                        if (mi.Name == methodName)
                        {
                            ParameterInfo[] parameters = mi.GetParameters();
                            MethodInfo liftedMethod = EvaluateLiftedMethod(mi, parameters, group, lhsType0, rhsType0);
                            if (liftedMethod != null)
                                candidates.Add(liftedMethod);
                        }
                    }
                }
            }
            if (candidates.Count == 1)
            {
                // only 1, so it is it
                error = null;
                return candidates[0];
            }
            else if (candidates.Count == 0)
            {
                // nothing matched
                message = string.Format(CultureInfo.CurrentCulture,
                    (group == OperatorGrouping.Arithmetic) ? Messages.ArithOpBadTypes : Messages.RelationalOpBadTypes,
                    op.ToString(),
                    (lhs == typeof(NullLiteral)) ? Messages.NullValue : RuleDecompiler.DecompileType(lhs),
                    (rhs == typeof(NullLiteral)) ? Messages.NullValue : RuleDecompiler.DecompileType(rhs));
                error = new ValidationError(message, ErrorNumbers.Error_OperandTypesIncompatible);
                return null;
            }
            else
            {
                // more than 1, so pick the best one
                MethodInfo bestFit = validator.FindBestCandidate(null, candidates, lhs, rhs);
                if (bestFit != null)
                {
                    error = null;
                    return bestFit;
                }
                // must be ambiguous. Since there are at least 2 choices, show only the first 2
                message = string.Format(CultureInfo.CurrentCulture,
                    Messages.AmbiguousOperator,
                    op.ToString(),
                    RuleDecompiler.DecompileMethod(candidates[0]),
                    RuleDecompiler.DecompileMethod(candidates[1]));
                error = new ValidationError(message, ErrorNumbers.Error_OperandTypesIncompatible);
                return null;
            }
        }
        internal static MethodInfo MapOperatorToMethod(CodeBinaryOperatorType op, Type lhs, CodeExpression lhsExpression, Type rhs, CodeExpression rhsExpression, RuleValidation validator, out ValidationError error)
        {
            string str;
            string str2;
            OperatorGrouping arithmetic;
            switch (op)
            {
                case CodeBinaryOperatorType.Add:
                    str = "op_Addition";
                    arithmetic = OperatorGrouping.Arithmetic;
                    break;

                case CodeBinaryOperatorType.Subtract:
                    str = "op_Subtraction";
                    arithmetic = OperatorGrouping.Arithmetic;
                    break;

                case CodeBinaryOperatorType.Multiply:
                    str = "op_Multiply";
                    arithmetic = OperatorGrouping.Arithmetic;
                    break;

                case CodeBinaryOperatorType.Divide:
                    str = "op_Division";
                    arithmetic = OperatorGrouping.Arithmetic;
                    break;

                case CodeBinaryOperatorType.Modulus:
                    str = "op_Modulus";
                    arithmetic = OperatorGrouping.Arithmetic;
                    break;

                case CodeBinaryOperatorType.ValueEquality:
                    str = "op_Equality";
                    arithmetic = OperatorGrouping.Equality;
                    break;

                case CodeBinaryOperatorType.BitwiseOr:
                    str = "op_BitwiseOr";
                    arithmetic = OperatorGrouping.Arithmetic;
                    break;

                case CodeBinaryOperatorType.BitwiseAnd:
                    str = "op_BitwiseAnd";
                    arithmetic = OperatorGrouping.Arithmetic;
                    break;

                case CodeBinaryOperatorType.LessThan:
                    str = "op_LessThan";
                    arithmetic = OperatorGrouping.Relational;
                    break;

                case CodeBinaryOperatorType.LessThanOrEqual:
                    str = "op_LessThanOrEqual";
                    arithmetic = OperatorGrouping.Relational;
                    break;

                case CodeBinaryOperatorType.GreaterThan:
                    str = "op_GreaterThan";
                    arithmetic = OperatorGrouping.Relational;
                    break;

                case CodeBinaryOperatorType.GreaterThanOrEqual:
                    str = "op_GreaterThanOrEqual";
                    arithmetic = OperatorGrouping.Relational;
                    break;

                default:
                    str2 = string.Format(CultureInfo.CurrentCulture, Messages.BinaryOpNotSupported, new object[] { op.ToString() });
                    error = new ValidationError(str2, 0x548);
                    return null;
            }
            List<MethodInfo> candidates = new List<MethodInfo>();
            bool flag = ConditionHelper.IsNullableValueType(lhs);
            bool flag2 = ConditionHelper.IsNullableValueType(rhs);
            Type rhsType = flag ? Nullable.GetUnderlyingType(lhs) : lhs;
            Type type = flag2 ? Nullable.GetUnderlyingType(rhs) : rhs;
            if (!rhsType.IsEnum)
            {
                if (type.IsEnum)
                {
                    Type underlyingType;
                    switch (op)
                    {
                        case CodeBinaryOperatorType.Add:
                            underlyingType = EnumHelper.GetUnderlyingType(type);
                            if ((underlyingType == null) || !RuleValidation.TypesAreAssignable(rhsType, underlyingType, lhsExpression, out error))
                            {
                                break;
                            }
                            error = null;
                            return new EnumOperationMethodInfo(lhs, op, rhs, false);

                        case CodeBinaryOperatorType.Subtract:
                        {
                            underlyingType = EnumHelper.GetUnderlyingType(type);
                            if (underlyingType == null)
                            {
                                break;
                            }
                            CodePrimitiveExpression expression = lhsExpression as CodePrimitiveExpression;
                            if (!DecimalIntegerLiteralZero(lhs, expression))
                            {
                                if (!RuleValidation.TypesAreAssignable(rhsType, underlyingType, lhsExpression, out error))
                                {
                                    break;
                                }
                                error = null;
                                return new EnumOperationMethodInfo(lhs, op, rhs, false);
                            }
                            error = null;
                            return new EnumOperationMethodInfo(lhs, op, rhs, true);
                        }
                        case CodeBinaryOperatorType.ValueEquality:
                        case CodeBinaryOperatorType.LessThan:
                        case CodeBinaryOperatorType.LessThanOrEqual:
                        case CodeBinaryOperatorType.GreaterThan:
                        case CodeBinaryOperatorType.GreaterThanOrEqual:
                            if (!flag2 || !(lhs == typeof(NullLiteral)))
                            {
                                if (DecimalIntegerLiteralZero(lhs, lhsExpression as CodePrimitiveExpression))
                                {
                                    error = null;
                                    return new EnumOperationMethodInfo(lhs, op, rhs, true);
                                }
                                break;
                            }
                            error = null;
                            return new EnumOperationMethodInfo(rhs, op, rhs, false);
                    }
                }
            }
            else
            {
                Type type3;
                switch (op)
                {
                    case CodeBinaryOperatorType.Add:
                        type3 = EnumHelper.GetUnderlyingType(rhsType);
                        if ((type3 == null) || !RuleValidation.TypesAreAssignable(type, type3, rhsExpression, out error))
                        {
                            break;
                        }
                        error = null;
                        return new EnumOperationMethodInfo(lhs, op, rhs, false);

                    case CodeBinaryOperatorType.Subtract:
                        type3 = EnumHelper.GetUnderlyingType(rhsType);
                        if (type3 == null)
                        {
                            break;
                        }
                        if (!(rhsType == type))
                        {
                            if (DecimalIntegerLiteralZero(rhs, rhsExpression as CodePrimitiveExpression))
                            {
                                error = null;
                                return new EnumOperationMethodInfo(lhs, op, rhs, true);
                            }
                            if (!RuleValidation.TypesAreAssignable(type, type3, rhsExpression, out error))
                            {
                                break;
                            }
                            error = null;
                            return new EnumOperationMethodInfo(lhs, op, rhs, false);
                        }
                        error = null;
                        return new EnumOperationMethodInfo(lhs, op, rhs, false);

                    case CodeBinaryOperatorType.ValueEquality:
                    case CodeBinaryOperatorType.LessThan:
                    case CodeBinaryOperatorType.LessThanOrEqual:
                    case CodeBinaryOperatorType.GreaterThan:
                    case CodeBinaryOperatorType.GreaterThanOrEqual:
                        if (!(rhsType == type))
                        {
                            if (flag && (rhs == typeof(NullLiteral)))
                            {
                                error = null;
                                return new EnumOperationMethodInfo(lhs, op, lhs, false);
                            }
                            if (!DecimalIntegerLiteralZero(rhs, rhsExpression as CodePrimitiveExpression))
                            {
                                break;
                            }
                            error = null;
                            return new EnumOperationMethodInfo(lhs, op, rhs, true);
                        }
                        error = null;
                        return new EnumOperationMethodInfo(lhs, op, rhs, false);
                }
            }
            AddOperatorOverloads(rhsType, str, lhs, rhs, candidates);
            AddOperatorOverloads(type, str, lhs, rhs, candidates);
            if ((flag || flag2) || ((lhs == typeof(NullLiteral)) || (rhs == typeof(NullLiteral))))
            {
                AddLiftedOperators(rhsType, str, arithmetic, rhsType, type, candidates);
                AddLiftedOperators(type, str, arithmetic, rhsType, type, candidates);
            }
            if (candidates.Count == 0)
            {
                str = str.Substring(3);
                foreach (MethodInfo info in typeof(DefaultOperators).GetMethods())
                {
                    if (info.Name == str)
                    {
                        ParameterInfo[] parameters = info.GetParameters();
                        Type parameterType = parameters[0].ParameterType;
                        Type toType = parameters[1].ParameterType;
                        if (RuleValidation.ImplicitConversion(lhs, parameterType) && RuleValidation.ImplicitConversion(rhs, toType))
                        {
                            candidates.Add(info);
                        }
                    }
                }
                if ((((candidates.Count == 0) && ("Equality" == str)) && (!lhs.IsValueType && !rhs.IsValueType)) && (((lhs == typeof(NullLiteral)) || (rhs == typeof(NullLiteral))) || (lhs.IsAssignableFrom(rhs) || rhs.IsAssignableFrom(lhs))))
                {
                    candidates.Add(ObjectEquality);
                }
                if ((candidates.Count == 0) && ((flag || flag2) || ((lhs == typeof(NullLiteral)) || (rhs == typeof(NullLiteral)))))
                {
                    foreach (MethodInfo info2 in typeof(DefaultOperators).GetMethods())
                    {
                        if (info2.Name == str)
                        {
                            ParameterInfo[] infoArray2 = info2.GetParameters();
                            MethodInfo item = EvaluateLiftedMethod(info2, infoArray2, arithmetic, rhsType, type);
                            if (item != null)
                            {
                                candidates.Add(item);
                            }
                        }
                    }
                }
            }
            if (candidates.Count == 1)
            {
                error = null;
                return candidates[0];
            }
            if (candidates.Count == 0)
            {
                str2 = string.Format(CultureInfo.CurrentCulture, (arithmetic == OperatorGrouping.Arithmetic) ? Messages.ArithOpBadTypes : Messages.RelationalOpBadTypes, new object[] { op.ToString(), (lhs == typeof(NullLiteral)) ? Messages.NullValue : RuleDecompiler.DecompileType(lhs), (rhs == typeof(NullLiteral)) ? Messages.NullValue : RuleDecompiler.DecompileType(rhs) });
                error = new ValidationError(str2, 0x545);
                return null;
            }
            MethodInfo info4 = validator.FindBestCandidate(null, candidates, new Type[] { lhs, rhs });
            if (info4 != null)
            {
                error = null;
                return info4;
            }
            str2 = string.Format(CultureInfo.CurrentCulture, Messages.AmbiguousOperator, new object[] { op.ToString(), RuleDecompiler.DecompileMethod(candidates[0]), RuleDecompiler.DecompileMethod(candidates[1]) });
            error = new ValidationError(str2, 0x545);
            return null;
        }