[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; }